简介
ContentProvider是一种内容共享型组件,底层通过Binder和其他进程进行通信。ContentProvider一般是运行在独立的进程中的,每一个Content Provider在系统中只有一个实例存在,其它应用程序首先要找到这个实例,然后才能访问它的数据。ContentProvider采取懒加载的形式,即安装的时候并不会把ContentProvider加载到内存中来,而是会等到第一次使用的时候才加载,第二次使用的时候就直接返回了。
过程分析
要访问ContentProvider的数据,首先需要调用Context的getContentResolver()方法获取ContentResolver对象,从这里入手,我们找到ContextImpl的getContentResolver()方法实现如下:
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
追溯到ContentResolver 的初始化过程如下:
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
通过跟踪代码知道,ContentResolver是一个抽象类,我们使用的ApplicationContentResolver是这个抽象类的一个实现类。其中一个参数mainThread是是ActivityThread的一个实例。
一个应用启动时,入口方法为ActivityThread的main方法,该方法是一个static方法,其中创建了ActivityThread的实例并创建了主线程的消息队列,而且调用了ActivityThread的attach方法。
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
ActivityThread的attach()方法远程调用了AMS的 attachApplication()方法。参数mAppThread是一个ApplicationThread对象(Binder对象),该对象主要用于ActivityThread和AMS之间的通信。
//AMS继承的ActivityManagerNative实现了IActivityManager 接口
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
AMS的attachApplication()方法跨进程调用了ApplicationThread的bindApplication方法。
thread.bindApplication(processName, appInfo, providers,
app.instrumentationClass, profileFile, profileFd, profileAutoStop,
app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
mCoreSettingsObserver.getCoreSettingsLocked());
ApplicationThread是ActivityThread的一个内部类。bindApplication最终会通过H类的实例mH切换到ActivityThread中去执行。
sendMessage(H.BIND_APPLICATION, data);
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
在handleBindApplication中,ActivityThread会创建Application对象并加载ContentProvider。由以下代码可知,ActivityThread是先加载ContentProvider,然后再调用Application的onCreate方法。
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
以上为ContentProvider的启动过程,启动后,外界就可以通过增删改查接口来操纵ContentProvider的数据。
当ContentProvider所在的进程未启动时,第一次访问它时会触发ContentProvider的创建,同时会拉起ContentProvider所在的进程。下面从调用ContentResolver的query入手分析:
//ContentResolver.java
IContentProvider unstableProvider = acquireUnstableProvider(uri);
......
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
//ApplicationContentResolver.java
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false);
}
ApplicationContentResolver的acquireUnstableProvider自己不做任何事情,最终交由ActivityThread的acquireProvider处理。
//ActivityThread
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 会先通过acquireExistingProvider查询IContentProvider 是否存在,存在则直接返回。acquireExistingProvider源码如下:
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
}
IContentProvider provider = pr.mProvider;
如果目标ContentProvider未启动,ActivityThread会通过进程间通信让AMS启动ContentProvider。启动ContentProvider,需要先通过AMS的startProcessLocked启动其所在的进程。startProcessLocked最终会调用到Process.start,最后任务交由Zygote处理。
// Use existing process if already started
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null) {
if (DEBUG_PROVIDER) {
Slog.d(TAG, "Installing in existing process " + proc);
}
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
} else {
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": process is bad");
return null;
}
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, null);
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
进程启动后,入口为ActivityThread的main方法。
网友评论