启动性能 ContentProvider 的生命周期默认在 Application onCreate() 之前,而且都是在主线程创建的。我们自定义的 ContentProvider 类的构造函数、静态代码块、onCreate 函数都尽量不要做耗时的操作,会拖慢启动速度。
稳定性 ContentProvider 在进行跨进程数据传递时,利用了 Android 的 Binder 和匿名共享内存机制。
Binder 传递 CursorWindow 对象内部的匿名共享内存的文件描述符。 这样在跨进程传输中,**结果数据并不需要跨进程传输,而是在不同进程中通过传输的匿名共享内存文件描述符来操作同一块匿名内存,**这样来实现不同进程访问相同数据的目的
基于 mmap 的匿名共享内存机制也是有代价的。当传输的数据量非常小的时候,可能不一定划算。所以 ContentProvider 提供了一种 call 函数,它会直接通过 Binder 来传输数据。
ContentProvider 的接口调用参数和 call 函数调用并没有使用匿名共享机制,Binder有1024的限制,传输数据如果过大,就会抛出异常
安全性 虽然 ContentProvider 为应用程序之间的数据共享提供了很好的安全机制,但是如果 ContentProvider 是 exported,当支持执行 SQL 语句时就需要注意 SQL 注入的问题。另外如果我们传入的参数是一个文件路径,然后返回文件内容,这个时候也要校验合法性,不然整个应用的私有数据都有可能被别人拿到,在 Intent 传递参数的时候可能会经常会犯这个错误。
源码分析:
attachApplication 方法工作的第一部分就算是分析完了,该部分内容的主要工作是:通过 PMS 收集在 AndroidManifest 中注册的 ContentProvider 信息,将其封装成 ProviderInfo 集合。
将的 ProviderInfo 信息集合,作为参数远程调用 ApplicationThread 的 bindApplication 方法。此时将重新回到应用进程启动类 ActivityThread 中:
ApplicationThread 是 ActivityThread 与系统 PMS 进程通信的桥梁,它本质也是一个 Binder 对象。
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ...省略) {
... 省略
//将返回数据都封装在AppBindData中
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
//这是我们要跟踪的ContentProvider集合
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
//发送BIND_APPLICATION消息到主线程Handler
sendMessage(H.BIND_APPLICATION, data);
}
在 bindApplication 方法发送 BIND_APPLICATION 消息到当前进程的主线程 Handler 中。
在分析 ContentProvider 的收集过程中,验证了自定义 ContentProvider 必须在 AndroidManifest.xml 注册,这里是在AMS里验证的
回到 ActivityThread 在 ApplicationThread 的 bindApplication 方法发送消息到主线程,此时来到 ActivityThread Handler 的 handleMessage 方法,先看下在 ActivityThread 中的声明:
class H extends Handler {
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
// 省略
public void handleMessage(Message msg) {
switch (msg.what) {
//通过 ApplicationThread 发送的 BIND_APPLICATION
case BIND_APPLICATION:
AppBindData data = (AppBindData) msg.obj;
//调用 handleBindApplication 开始真正创建 Application
handleBindApplication(data);
break;
...省略
}
}
}
handleBindApplication主要做了以下2个逻辑: 1.遍历 PMS 收集到的所有 ContentProvider 集合信息(ProviderInfo),并创建所有 ContentProvider 实例。回调其 onCreate 方法。 2.创建当前进程的 Application 对象,首先回调其 attach 方法,这步发生在遍历 ContentProvider 集合之前,创建每个 ContentProvider 并回调其 onCreate 方法之后,回调 Application 的 onCreate。
**ContentProvider 加载和创建都是在主线程完成,并且还都是在应用启动过程完成,**ContentProvider 的生命周期默认在 Application onCreate 之前。这也验证了文章开头为大家介绍的启动性能,在使用 ContentProvider 需要注意的“暗坑”,自定义 ContentProvider 类的构造函数、静态代码块、onCreate 函数都尽量不要做耗时的操作,会拖慢启动速度。