android 进阶(有待整理)

96
小猪x
2017.10.26 10:04* 字数 3428

参考 http://blog.sina.com.cn/s/blog_4c451e0e0102wofv.html

二、使用 NDK 的注意事项:

  1. 用 minSdkVersion 对应的 NDK 来编译;
  2. 让所有的 .so 文件都动态链接相同的 C++ 运行时;
  3. 为每个 CPU 架构提供对应的 .so 文件
    或在 build.gradle 中用 ndk。adiFilters 显式指定支持的ABI;
  4. 所有的设备都支持 armeabi 架构的 .so 文件,但影响性能和兼容性;

三、Builder 模式:

  1. AS 中安装 InnerBuilder 插件来简化 Builder 模式的创建过程;

  2. 例子:
    public class User {
    private final String mName; // 必选
    private final String mGender; // 必选
    private final int mAge; // 必选

    private final String mPhoto; // 可选

    private User(Builder builder) {
    mName = build.mName;
    mGender = build.mGender;
    mAge = build.mAge;

     mPhoto  = build.mPhoto;
    

    }

    public static final class Builder {
    private String mName;
    private String mGender;
    private int mAge;

     private String mPhoto; 
     
     public Builder() {}
    
     public Builder setName(String val) {
         mName = val;
         
         return this;
     }
    
     public Builder setGender(String val) {
         mGender = val;
         
         return this;
     }
     
     public Builder setAge(int val) {
         mAge = val;
         
         return this;
     }
     
      public Builder setPhoto(String val) {
         mPhoto; = val;
         
         return this;
     }
    
     public User build() {
         return new User(this);
     }
    

    }
    }

四、版本新特性:

  1. Android 3.0 引入属性动画;
  2. Android 3.0 开始引入 Loader 异步数据加载框架;
  3. Android 4.4 引入过渡动画;
  4. Android 4.0 在 UI 线程中进行网络操作抛出 NetworkOnMainThreadException 异常

*. Activity/View 超 5 秒,BroadcastReceiver 超 10 秒,Service 超 20 秒 ANR 产生

五、ANR的避免和检测:

  1. 在 Application 或 MainActivity 类的 onCreate 方法中执行 StrictMode 严格模式:
    // 线程策略
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    .detectCustomSlowCalls() // 检测自定义耗时操作
    .detectDiskReads() // 检测是否存在磁盘读取操作
    .detectDiskWrites() // 检测是否存在磁盘写入操作
    .detectNetwork() // 检测是否存在网络操作
    .penaltyLog() // 检测将警告输出到 LogCat
    .build());

// 虚拟机策略
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectActivityLeaks() // 检测是否存在Activity泄漏
.detectLeakedClosableObjects() // 检测是否存在未关闭的 Closable 对象泄漏
.detectLeakedSqlLiteObjects() // 检测是否存在 Sqlite 对象泄漏
.setClassInstanceLimit() // 检测实例个数是否超过限制
.penaltyLog() // 检测将警告输出到 LogCat
.build());

  1. BlockCanary 非侵入式的性能监控函数库,监控主线程的卡顿,原理:
    利用主线程的消息队列处理机制,
    通过对比消息分发开始和结束的时间点来判断是否超过设定的时间。

    1. 添加依赖:
      dependencies {
      compile 'com.github.moduth:blockcanary-android:1.2.1'

      // 仅在 debug 包启用 BlockCanary 进行卡顿监控和提示
      debugCompile 'com.github.moduth:blockcanary-android:1.2.1'
      releaseCompile 'com.github.moduth:blockcanary-no-op:1.2.1'
      }

    2. 在 Application 类的派生类的 onCreate 中进行配置和初始化:
      // 在主进程初始化调用
      BlockCanary.install(this, new AppBlockCanaryContext()).start();

    3. 创建 BlockCanaryContext 派生类,实现各种上下文:
      public class AppBlockCanaryContext extends BlockCanaryContext {
      // 包括:应用标识符、用户uid、网络类型、判断阈值、Log保存位置等
      }

六、异步处理技术:

  • Android 系统如果检测到非主线程更新 UI 组件,
    那么就会抛出 CalledFromWrongThreadException 异常!
  1. Thread 是 Android 中异步处理技术的基础,创建线程有两种方法:
  1. 继承 Thread 类并重写 run 方法:
    public class MyThread extends Thread {
    @Override
    public void run() {
    // 具体实现逻辑
    }
    }

MyThread mt = new MyThread();
mt.start();

  1. 实现 Runnable 接口并实现 run 方法:
    public class MyRunnable implements Runnable {
    @Override
    public void run() {
    // 具体实现逻辑
    }
    }

MyRunnable mr = new MyRunnable();
Thread thread = new Thread(mr);
thread.start();

  1. HandlerThread 是一个集成了 Looper 和 MessageQueue 的线程,
    当启动 HandlerThread 时,
    会同时生成 Looper 和 MessageQueue,然后等待消息进行处理。
    开发者不需要自己去创建和维护 Looper,
    只有一个消息队列,顺序执行,线程安全,它的用法和普通线程一样:

    HandlerThread handlerThread = new HandlerThread("HandlerThread");
    handlerThread.start();

    // 只有当 HandlerThread 准备好接收消息之后才会返回
    mHandler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
    super.handleMessage(msg);
    // 处理接收到的消息
    }
    }

  2. AsyncQueryHandler 是一个抽象类,继承自 Handler,
    通过封装 ContentResolver、HandlerThread、Handler等
    实现 ContentProvider 上面执行异步操作,
    这些操作会被放到一个单独的子线程中执行,当操作结束获取到结果后,
    将通过消息的方式传递给调用 AsyncQueryHandler 的线程,通常就是主线程。
    方法 回调函数
    startQuery onQueryComplete
    startInsert onInsertComplete
    startUpdate onUpdateComplete
    startDelete onDeleteComplete

  3. IntentService 是一个抽象类,使用前需要继承并实现 onHandleIntent 方法,
    具有 Service 一样的生命周期,也提供了后台线程处理异步任务。
    顺序执行所有的任务,
    Context.startService 传递一个 Intent 类型的参数可以启动 IntentService,
    当所有任务处理完成后,IntentService 将会自动结束生命周期。

    public class SimpleIntentService extends IntentService {
    public SimpleIntentService() {
    // 子类构造方法中需要调用 super 传入子类名字
    super(SimpleIntentService.class.getName());

        setIntentRedelivery(true);
    }
    
    @Override
    protected void onHandleIntent(Intent intent) {
        // 在后台线程中调用
    }
    

    }

    在 AndroidManifest.xml 文件中注册:

  1. Executor 的主要目的是分离任务的创建和执行,基于 Executor 的接口定义:
    public interface Executor {
    void execute(Runnable command);
    }

    public class SimpleExecutor implements executor {
    @Override
    public void execute(Runnable runnable) {
    // 最简单的方法就是直接在这个方法中创建一个线程来执行 Runnable
    // 实际应用中 execute 方法很少是这么简单的,
    // 通常需要增加类似队列、任务优先级等功能,最终实现一个线程池。
    new Thread(runnable).start();
    }
    }

    固定大小的线程池:Executors.newFixedThreadPool(线程池中线程的个数);
    可变大小的线程池:Executors.newCachedThreadPool();
             空闲线程等待 60 秒来执行新任务,超时自动销毁。
    单个线程的线程池:Executors.newSingleThreadExecutor();

  2. 预定义的线程池都是基于 ThreadPoolExecutor 类之上构建的,
    而通过 ThreadPoolExecutor 开发者可以自定义线程池的一些行为。

    ThreadPoolExecutor tpe = new ThreadPoolExecutor(
    int corePoolSize, // 核心线程数,始终保有这么多线程
    int maximumPoolSize, // 最大线程数,不能超过这么多线程
    long keepAliveTime, // 空闲时长,超出核心线程数即注销
    TimeUnit unit, // 空时单位(可选)
    // TimeUnit 类中的
    // NANOSECONDS、MICROSECONDS、
    // MILLISECONDS、SECONDS
    BlockingQueue workQueue); // 任务缓冲队列

  3. AsyncTask 是在 Executor 框架基础上进行的封装,
    它实现将耗时任务移动到工作线程中执行,
    同时提供方便的接口实现工作线程和主线程的通信,一般会用到如下方法:

    public class MyAsyncTask extends AsyncTask {
    @Override
    protected void onPreExecute() {
    // 在执行实际的后台操作前被 UI thread 调用。
    // 可以在该方法中做一些准备工作,如在界面上显示一个进度条
    }

    // 除了 doInBackground 方法是在工作线程中执行,
    // 其他的都是在主线程中执行
    @Override
    protected Result doInBackground(Params... params) { 
        // 该方法运行在后台线程中,主要负责执行那些很耗时的后台计算工作 
    }
    
    @Override
    protected void onProgressUpdate(Progress... progress) { 
        // 更新实时的任务进度 
    }
    
    @Override
    protected void onPostExecute(Result result) { 
        // 任务执行的结果作为此方法的参数传入 
    }
    
    @Override
    protected void onCancelled() { 
        // 用户调用取消时,要做的操作
    }
    

    }

    new MyAsyncTask().execute(param);

    new MyAsyncTask().executeOnExecutor(Executors.newCachedThreadPool());

    注:
    executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
    THREAD_POOL_EXECUTOR 是一个 corePoolSize 为 CPU 核数 + 1 的线程池,
    超过指定个数的就要等待。

    executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)
    与 API Level > 13 的系统上执行 execute() 是一样的!

  4. Loader 是 Android 3.0 开始引入一个异步数据加载框架,
    使得在 Activity 或 Fragment 中异步加载数据变得很简单,
    同时它在数据源发生变化时,能够及时发出消息通知。

    1. AsyncTaskLoader 是 Loader 子类,基于 AsyncTask 实现异步数据加载的抽象类,
      其派生实例类必须实现 loadInBackground 方法,在其中进行具体的数据加载操作。

    2. CursorLoader 是 AsyncTaskLoader 的子类,
      封装了对 ContentResolver 的 query 操作,
      实现从 ContentProvider 中查询数据的功能。

    3. LoaderManager 是一个抽象类,用来管理一个或多个加载器对象的。
      Activity 和 Fragment 与之默认关联,
      只需要通过 getLoaderManager 方法即可获取其实例对象。

      主要有三个回调接口方法:
      onCreateLoader 初始化并返回一个新的 Loader 实例;
      onLoadFininshed 当一个加载器完成加载过程之后会回调这个方法;
      onLoaderReset 当一个加载器被重置并且数据无效时会回调这个方法;

      public class MyActivity extends ListActivity
      implements LoaderManager.LoaderCallbacks {

      // 只获取 ID 和用户名两个字段
      static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
           ContactsContract.Contacts._ID,
           ContactsContract.Contacts.DISPLAY_NAME
      };
      
      SimpleCursorAdapter mAdapter;
      
      private void initAdapter() {
          mAdapter = new SimpleCursorAdapter(this, 
                                             android.R.layout.simple_list_item_1,
                                             null, 
                                             new String[] {ContactsContract.Contacts.DISPLAY_NAME}, 
                                             new int[]{android.R.id.text1}, 
                                             0);
          setListAdapter(mAdapter);
      }
      
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          
          // 通过 LoderManager 初始化 Loader,这会回调到 onCreateLoader
          getLoaderManager().initLoader(0x0,  // 加载器的标识
                                        null, // 初始化加载器的参数,可以空
                                        // LoaderCallbacks 实现类实例
                                        this); 
      }
      
      @Override
      public Loader onCreateLoader(int id, Bundle args) {
          // 初始化并返回一个新的装载器
          // return new CursorLoader(this, 
                                     ContactsContract.Contacts.CONTENT_URI,
                                     CONTACTS_SUMMARY_PROJECTION, 
                                     null, 
                                     null, 
                                     ContactsContract.Contacts.DISPLAY_NAME + "ASC");
      }
      
      @Override
      public void onLoadFininshed(Loader loader, Cursor c) {
          // 后台线程中加载完数据后,回调这个方法将数据传递给主线程
          mAdapter.swapCursor(c);
      }
      
      @Override
      public void onLoaderReset(Loader loader) {
          // Loader 被重置后的回调,在这里可以重新刷新页面数据
          mAdapter.swapCursor(null);
      }
      

      }

七、数据序列化:

序列化是将数据结构或者对象转换成可用于存储或者传输的数据格式的过程。
  反序列化是将序列化过程中生成的数据还原数据结构或者对象的过程。

  1. Serializable 是 Java 语言自带的序列化方案,只需实现 Serializable 接口即可!
    序列化将 Java 对象转换成字节序列的过程,反序列化是将字节序列恢复成 Java 对象。
    Serializable 接口是一种标识接口,无需实现任何方法!
    Java 使用反射机制,在序列化的过程中会创建很多临时对象,容易触发垃圾回收,
    序列化的过程比较慢,对于性能要求很严格的场景不建议使用这种方案。

    public class MySerializable implements Serializable {
    // serialVersionUID 在反序列化过程中用于验证序列化对象的发送者和接收者
    // 是否为该对象加载了与序列化兼容的类对象,
    // 如果接收者加载的对象的 serialVersionUID
    // 和发送者加的对象的 serialVersionUID 取值不同,
    // 则反序列化过程会出现 InvalidClassException 异常!

    // Add default serial version ID
    private static final long serialVersionUID = 1L;
    或
    // Add generated serial version ID
    private static final long serialVersionUID = 4603642343377807741L;
    

    }

  2. 将 OutputStream 封装到 ObjectOutputStream 对象中,
    调用 writeObjet 方法将对象进行序列化。
    调用 readObject 方法将数据转换成某种类型的 InputStream 。

    对象序列化是基于字节的!

    • Reader 和 Writer 方法是基于字符方式。
  3. Parcelable 是 Android SDK 提供的,基于内存的,Android 中跨进程对象的传递一般使用 Parcelable 。
    Serializable 是 JDK 提供的接口,基于磁盘或网络的。

    • Android Studio 中可以安装一个名为 Android Parcelable code generator 的插件。
      定义好数据类,右键选择 Parcelable 菜单项,
      该插件自动帮我们转换成实现了 Parcelable 接口的形式:

      public class DataClassExam {
      public String mName;
      public Long mTime;
      }

      public class DataClassExam implements Parcelable {
      public String mName;
      public Long mTime;

      public DataClassExam() {}
      
      @Override
      public int describeContents() {
          // 接口内容的描述,一般默认返回零即可!
          return 0;
      }
      
      @Override
      public void writeToParcel(Parcel dest, int flags) {
          // 将类的数据写到 Parcel 容器中
          dest.writeString(this.mName);
          dest.writLong(this.mTime);
      }
      
      @Override
      protected DataClassExam(Parcel in) {
          // 顺序要与 writeToParcel 方法中的一致
          this.mName = in.readString();
          this.mTime = in.readLong();
      }
      
      public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
          public DataClassExam createFromParcel(Parcel source) {
              // 反序列化,将 Parcel 还原成对象
              return new DataClassExam(source);
          }
          
          public DataClassExam[] newArray(int size) {
              // 提供外部类反序列化这个数组使用
              return new DataClassExam[size];
          }
      }
      

      }

  4. SQLiteDatabase 默认目录是 /data/data/包名/database
    sqlcipher 函数库实现操作数据库敏感信息加解密(256位AES加密)。

  5. SharedPreferences 保存应用的一些常用配置信息,其本质是一个键值对存储。
    是以 XML 文件的形式保存在 /data/data/包名/shared_prefs 目录中的。
    读取和存储数据,主要可以分三步:

    SharedPreferences sp = context.getSharedPreferences("偏好名",
    // 操作模式
    Context.MODE_PRIVATE);
    // SharedPreferences的四种操作模式:
    // Context.MODE_PRIVATE
    // 为默认操作模式,代表该文件是私有数据,只能被应用本身访问,
    // 在该模式下,写入的内容会覆盖原文件的内容

    // Context.MODE_APPEND
    // 模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件

    // 以下两个用来控制其他应用是否有权限读写该文件
    // Context.MODE_WORLD_READABLE 表示当前文件可以被其他应用读取
    // Context.MODE_WORLD_WRITEABLE 表示当前文件可以被其他应用写入

    sp.get数据类型(键名,默认值);

    SharedPreferences.Editor editor = sp.edit();

    editor.put数据类型(键名,值);

    editor.commit();

    • secure-preferences 是 SharedPreferences 的安全封装类。
      conceal 是 Facebook 的加解密开源库。
  6. JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。
    几乎 80% 的 APP 与服务端的通信都是使用 JSON 格式。

  7. ProtocolBuffer 是 Google 设计的与语言、平台无关的一种轻便高效的序列化结构。
    移动端应该使用 Nano-ProtocolBuffer 版本。

  8. FlatBuffers 是 Google 为游戏开发或其他对性能敏感的应用程序创建的,
    开源的跨平台的高效的序列化函数库(对 C++/Java 等语言接口的支持)。

    • 这个更适合移动
日记本