ContentProvider的工作过程
ContentProvider是一种内容共享型组件,它通过Binder向其他组件乃至其他应用提供数据,当ContentProvider所在的进程启动时,ContentProvider会同时启动并发布到AMS中。需要注意,ContentProvider的onCreate要先于Application的onCreate而执行。
当一个应用启动时,入口方法为ActivityThread的main方法,在main中创建ActivityThread的实例并创建主线程的消息队列,然后在ActivityThread的attach方法中远程调用AMS的attachApplication方法并将ApplicationThread对象提供给AMS。ApplicationThread是一个Binder对象,它的Binder接口是IApplicationThread,它主要用于ActivityThread和AMS间的通讯。在AMS的attachApplication方法中,会调用ApplicationThread的bindApplication方法(跨进程完成),bindApplication的逻辑会经过ActivityThread中的Handler切换到ActivityThread中执行,具体方法是handleBindApplication。
在handleBindApplication方法中,ActivityThread会创建Application对象并加载ContentProvider。需要注意,ContentProvider的onCreate要先于Application的onCreate而执行。
ContentProvider提供增删改查四个接口来操作数据源。这四个方法都是通过Binder来调用的,外界无法直接访问ContentProvider,只能通过AMS根据Uri来获取对应的ContentProvider的Binder接口IContentProvider,然后再通过IContentProvider来访问ContentProvider的数据源。
下面来分析单实例的ContentProvider的启动过程的源码。
访问ContentProvider需要通过ContentResolver(抽象类,具体实现是ApplicationContentResolver)。通过ContentProvider四个中任一方法都可以触发ContentProvider启动,这里选择query方法。
ContentProvider的query方法中,首先会获取IContentProvider对象(通过ApplicationContentResolve的acquireProvider方法实现)。
ApplicationContentResolve的acquireProvider直接调用了ActivityThread的acquireProvider。
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
// There is a possible race here. Another thread may try to acquire
// the same provider at the same time. When this happens, we want to ensure
// that the first one wins.
// Note that we cannot hold the lock while acquiring and installing the
// provider since it might take a long time to run and it could also potentially
// be re-entrant in the case where the provider is in the same process.
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
从ActivityThread中查找是否已经存在ContentProvider,存在直接返回。如果没有启动,那就发送一个进程间请求给AMS让其启动目标ContentProvider,再通过installProvider方法来修改引用计数。在AMS中首先会启动ContentProvider所在的进程,然后再启动ContentProvider。启动进程是由AMS的startProcessLocked方法完成,其内部主要通过Process的start方法来完成新进程启动,新进程启动后的入口方法为ActivityThread的main。