Content Provider 做为四大组件之一,通常情况下并没有其他的组件使用频繁,但这不能作为我们不去深入学习它的理由。关于 Content Provider 一篇文章是写不完的,这一篇文章先来介绍它的启动过程。
在 Android IPC 机制(四)用 ContentProvider 进行进程间通信这篇文章我举了一个 Content Provider 使用的例子,在 Activity 中我是使用如下代码调用 Content Provider 的:
- public class ContentProviderActivity extends AppCompatActivity {
- private final static String TAG = "ContentProviderActivity";@Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_content_provider);
- Uri uri = Uri.parse("content://com.example.liuwangshu.mooncontentprovide.GameProvider");
- ContentValues mContentValues = new ContentValues();
- mContentValues.put("_id", 2);
- mContentValues.put("name", "大航海时代ol");
- mContentValues.put("describe", "最好玩的航海网游");
- getContentResolver().insert(uri, mContentValues); //1
- Cursor gameCursor = getContentResolver().query(uri, new String[] {
- "name",
- "describe"
- },
- null, null, null);...
- }
- }
要想调用 Content Provider,首先需要使用注释 1 处的 getContentResolver 方法,如下所示。 frameworks/base/core/Java/android/content/ContextWrapper.java
- OverridepublicContentResolvergetContentResolver() {returnmBase.getContentResolver();
- }
这里 mBase 指的是 ContextImpl,ContextImpl 的 getContentResolver 方法如下所示。
frameworks/base/core/java/android/app/ContextImpl.java
- @Override
- publicContentResolvergetContentResolver() {returnmContentResolver;
- }
上面的代码 return 了 ApplicationContentResolver 类型的 mContentResolver 对象,ApplicationContentResolver 是 ContextImpl 中的静态内部类,继承自 ContentResolver,它在 ContextImpl 的构造方法中被创建。 当我们调用 ContentResolver 的 insert、query、update 等方法时就会启动 Content Provider,这里拿 query 方法来进行举例。 query 方法的实现在 ApplicationContentResolver 的父类 ContentResolver 中,代码如下所示。 frameworks/base/core/java/android/content/ContentResolver.java
- public final@Nullable Cursorquery(final@RequiresPermission.Read @NonNull Uri uri,
- @Nullable String[] projection, @Nullable String selection,
- @Nullable String[] selectionArgs, @Nullable String sortOrder,
- @Nullable CancellationSignal cancellationSignal) {
- Preconditions.checkNotNull(uri,"uri");
- IContentProvider unstableProvider = acquireUnstableProvider(uri);//1...try{
- ...try{
- qCursor = unstableProvider.query(mPackageName, uri, projection,
- selection, selectionArgs, sortOrder, remoteCancellationSignal);//2}catch(DeadObjectException e) {
- ...
- }
- ...
- }
在注释 1 处通过 acquireUnstableProvider 方法返回 IContentProvider 类型的 unstableProvider 对象,在注释 2 处调用 unstableProvider 的 query 方法。我们查看 acquireUnstableProvider 方法做了什么,如下所示。 frameworks/base/core/java/android/content/ContentResolver.java
- public finalIContentProvideracquireUnstableProvider(Uri uri) {if(!SCHEME_CONTENT.equals(uri.getScheme())) {//1
- return null;
- }
- String auth = uri.getAuthority();if(auth !=null) {returnacquireUnstableProvider(mContext, uri.getAuthority());//2}return null;
- }
注释 1 处用来检查 Uri 的 scheme 是否等于 "content",如果不是则返回 null。注释 2 处调用了 acquireUnstableProvider 方法,这是个抽象方法,它的实现在 ContentResolver 的子类 ApplicationContentResolver 中: frameworks/base/core/java/android/app/ContextImpl.java
- @Override
- protectedIContentProvideracquireUnstableProvider(Context c, String auth) {returnmMainThread.acquireProvider(c,
- ContentProvider.getAuthorityWithoutUserId(auth),
- resolveUserIdFromAuthority(auth),false);
- }
return 了 ActivityThread 类型的 mMainThread 对象的 acquireProvider 方法: frameworks/base/core/java/android/app/ActivityThread.java
- public finalIContentProvideracquireProvider(
- Context c, String auth,intuserId,booleanstable) {finalIContentProvider provider = acquireExistingProvider(c, auth, userId, stable);//1
- if(provider !=null) {returnprovider;
- }
- IActivityManager.ContentProviderHolder holder =null;try{
- holder = ActivityManagerNative.getDefault().getContentProvider(
- getApplicationThread(), auth, userId, stable);//2}catch(RemoteException ex) {throwex.rethrowFromSystemServer();
- }if(holder ==null) {
- Slog.e(TAG,"Failed to find provider info for "+ auth);return null;
- }
- holder = installProvider(c, holder, holder.info,true /*noisy*/, holder.noReleaseNeeded, stable);//3
- returnholder.provider;
- }
注释 1 处检查 ActivityThread 中的 ArrayMap 类型的 mProviderMap 中是否有目标 ContentProvider 存在,有则返回,没有就会在注释 2 处调用 AMP 的 getContentProvider 方法,最终会调用 AMS 的 getContentProvider 方法。注释 3 处的 installProvider 方法用来将注释 2 处返回的 ContentProvider 相关的数据存储在 mProviderMap 中,起到缓存的作用,这样使用相同的 Content Provider 时,就不需要每次都要调用 AMS 的 getContentProvider 方法。使用我们接着查看 AMS 的 getContentProvider 方法,代码如下所示。 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- @Override
- public finalContentProviderHoldergetContentProvider(
- IApplicationThread caller, String name,intuserId,booleanstable) {
- ...returngetContentProviderImpl(caller, name,null, stable, userId);
- }
getContentProvider 方法 return 了 getContentProviderImpl 方法: frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- privateContentProviderHoldergetContentProviderImpl(IApplicationThread caller,
- String name, IBinder token,booleanstable,intuserId) {
- ...
- ProcessRecord proc = getProcessRecordLocked(
- cpi.processName, cpr.appInfo.uid,false);//1
- if(proc !=null&& proc.thread !=null&& !proc.killed) {
- ...if(!proc.pubProviders.containsKey(cpi.name)) {
- checkTime(startTime,"getContentProviderImpl: scheduling install");
- proc.pubProviders.put(cpi.name, cpr);try{
- proc.thread.scheduleInstallProvider(cpi);//2}catch(RemoteException e) {
- }
- }
- }else{
- checkTime(startTime,"getContentProviderImpl: before start process");
- proc = startProcessLocked(cpi.processName,
- cpr.appInfo,false,0,"content provider",newComponentName(cpi.applicationInfo.packageName,
- cpi.name),false,false,false);//3checkTime(startTime,"getContentProviderImpl: after start process");
- ...
- }
- ...
- }
getContentProviderImpl 方法的代码很多,这里截取了关键的部分。注释 1 处通过 getProcessRecordLocked 方法来获取目标 ContentProvider 的应用程序进程信息,这些信息用 ProcessRecord 类型的 proc 来表示,如果该应用进程已经启动就会调用注释 2 处的代码,否则就会调用注释 3 的 startProcessLocked 方法来启动进程。这里我们假设 ContentProvider 的应用进程还没有启动,关于应用进程启动过程,我在 Android 应用程序进程启动过程(前篇)已经讲过,最终会调用 ActivityThread 的 main 方法,代码如下所示。 frameworks/base/core/java/android/app/ActivityThread.java
- public static void main(String[] args) {
- ...
- Looper.prepareMainLooper();//1ActivityThread thread =newActivityThread();//2thread.attach(false);if(sMainThreadHandler ==null) {
- sMainThreadHandler = thread.getHandler();
- }if(false) {
- Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG,"ActivityThread"));
- }// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- Looper.loop();//3
- throw newRuntimeException("Main thread loop unexpectedly exited");
- }
注释 1 处通过 prepareMainLooper 方法在 ThreadLocal 中获取 Looper,并在注释 3 处开启消息循环。在注释 2 处创建了 ActivityThread 并调用了它的 attach 方法: frameworks/base/core/java/android/app/ActivityThread.java
- private void attach(booleansystem) {
- ...finalIActivityManager mgr = ActivityManagerNative.getDefault();//1
- try{
- mgr.attachApplication(mAppThread);//2}catch(RemoteException ex) {throwex.rethrowFromSystemServer();
- }
- ...
- }
注释 1 处最终会得到 AMS,在注释 2 处调用 AMS 的 attachApplication 方法,并将 ApplicationThread 类型的 mAppThread 对象传进去。 query 方法到 AMS 的调用过程,如下面时序图所示(省略应用程序进程启动过程)。
我们接着来查看 AMS 的 attachApplication 方法,如下所示。 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- @Override
- public final void attachApplication(IApplicationThread thread) {synchronized(this) {intcallingPid = Binder.getCallingPid();final longorigId = Binder.clearCallingIdentity();
- attachApplicationLocked(thread, callingPid);
- Binder.restoreCallingIdentity(origId);
- }
- }
attachApplication 方法中又调用了 attachApplicationLocked 方法: frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- private final boolean attachApplicationLocked(IApplicationThread thread,intpid) {
- ...
- thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
- profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
- app.instrumentationUiAutomationConnection, testMode,
- mBinderTransactionTrackingEnabled, enableTrackAllocation,
- isRestrictedBackupMode || !normalMode, app.persistent,newConfiguration(mConfiguration), app.compat,
- getCommonServicesLocked(app.isolated),
- mCoreSettingsObserver.getCoreSettingsLocked());
- ...
- }
attachApplicationLocked 方法中调用了 thread 的 bindApplication 方法,thread 是 IApplicationThread 类型的,从类型名字就可以看出来是用于进程间通信,这里实现 bindApplication 方法的是 ApplicationThreadProxy 类,它实现了 IApplicationThread 接口。 frameworks/base/core/java/android/app/ApplicationThreadNative.java
- class ApplicationThreadProxy implements IApplicationThread {
- ...@Override
- public final void bindApplication(String packageName, ApplicationInfo info,
- List providers, ComponentName testName, ProfilerInfo profilerInfo,
- Bundle testArgs, IInstrumentationWatcher testWatcher,
- IUiAutomationConnection uiAutomationConnection, intdebugMode,booleanenableBinderTracking,booleantrackAllocation,booleanrestrictedBackupMode,booleanpersistent, Configuration config, CompatibilityInfo compatInfo,
- Map services, Bundle coreSettings) throwsRemoteException {
- ...
- mRemote.transact(BIND_APPLICATION_TRANSACTION, data,null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
- ...
- }
到目前为止,上面的调用过程还是在 AMS 进程中执行的,因此,需要通过 IBinder 类型的 mRemote 对象向新创建的应用程序进程(目标 Content Provider 所在的进程)发送 BIND_APPLICATION_TRANSACTION 类型的通信请求。处理这个通信请求的是在新创建的应用程序进程中执行的 ApplicationThread 的 bindApplication 方法,如下所示。 frameworks/base/core/java/android/app/ActivityThread.java
- public final void bindApplication(String processName, ApplicationInfo appInfo,
- List providers, ComponentName instrumentationName,
- ProfilerInfo profilerInfo, Bundle instrumentationArgs,
- IInstrumentationWatcher instrumentationWatcher,
- IUiAutomationConnection instrumentationUiConnection, intdebugMode,booleanenableBinderTracking,booleantrackAllocation,booleanisRestrictedBackupMode,booleanpersistent, Configuration config,
- CompatibilityInfo compatInfo, Map services, Bundle coreSettings) {
- ...
- sendMessage(H.BIND_APPLICATION, data);
- }
调用 sendMessage 方法像 H 发送 BIND_APPLICATION 类型消息,H 的 handleMessage 方法如下所示。 frameworks/base/core/java/android/app/ActivityThread.java
- public void handleMessage(Message msg) {if(DEBUG_MESSAGES) Slog.v(TAG,">>> handling: "+ codeToString(msg.what));switch(msg.what) {...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 方法: frameworks/base/core/java/android/app/ActivityThread.java
- private void handleBindApplication(AppBindData data) {...final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);//1
- try{
- final ClassLoader cl = instrContext.getClassLoader();
- mInstrumentation = (Instrumentation)
- cl.loadClass(data.instrumentationName.getClassName()).newInstance();//2} catch (Exception e) {...}
- final ComponentName component = new ComponentName(ii.packageName, ii.name);
- mInstrumentation.init(this, instrContext, appContext, component,
- data.instrumentationWatcher, data.instrumentationUiAutomationConnection);//3
- ...Application app = data.info.makeApplication(data.restrictedBackupMode, null);//4mInitialApplication = app;if(!data.restrictedBackupMode) {if(!ArrayUtils.isEmpty(data.providers)) {
- installContentProviders(app, data.providers);//5mH.sendEmptyMessageDelayed(H.ENABLE_JIT,10*1000);
- }
- }...mInstrumentation.callApplicationOnCreate(app);//6
- ...}
handleBindApplication 方法的代码很长,这里截取了主要的部分。注释 1 处创建了 ContextImpl 。注释 2 处通过反射创建 Instrumentation 并在注释 3 处初始化 Instrumentation。注释 4 处创建 Application 并且在注释 6 处调用 Application 的 onCreate 方法,这意味着 Content Provider 所在的应用程序进程已经启动完毕,在这之前,注释 5 处调用 installContentProviders 方法来启动 Content Provider,代码如下所示。 frameworks/base/core/java/android/app/ActivityThread.java
- private void installContentProviders(Context context, List providers) {
- final ArrayList results = new ArrayList();
- for (ProviderInfo cpi: providers) { //1
- ...IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false
- /*noisy*/
- , true
- /*noReleaseNeeded*/
- , true
- /*stable*/
- ); //2
- ...
- }
- try {
- ActivityManagerNative.getDefault().publishContentProviders(getApplicationThread(), results); //3
- } catch(RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- }
注释 1 处遍历当前应用程序进程的 ProviderInfo 列表,得到每个 Content Provider 的 ProviderInfo(存储 Content Provider 的信息),并在注释 2 处调用 installProvider 方法来启动这些 Content Provider。在注释 3 处通过 AMS 的 publishContentProviders 方法将这些 Content Provider 存储在 AMS 的 mProviderMap 中,这个 mProviderMap 在前面提到过,起到缓存的作用,防止每次使用相同的 Content Provider 时都会调用 AMS 的 getContentProvider 方法。来查看 installProvider 方法时如何启动 Content Provider 的,installProvider 方法如下所示。 frameworks/base/core/java/android/app/ActivityThread.java
- private IActivityManager.ContentProviderHolder installProvider(Context context,
- IActivityManager.ContentProviderHolder holder, ProviderInfo info,
- boolean noisy, boolean noReleaseNeeded, boolean stable) {
- ContentProvider localProvider = null;...final java.lang.ClassLoader cl = c.getClassLoader();
- localProvider = (ContentProvider)cl.
- loadClass(info.name).newInstance();//1provider = localProvider.getIContentProvider();if(provider == null) {...
- returnnull;
- }if(DEBUG_PROVIDER) Slog.v(
- TAG,"Instantiating local provider "+ info.name);
- localProvider.attachInfo(c, info);//2} catch (java.lang.Exception e) {...}returnnull;
- }
- }...
- returnretHolder;
- }
在注释 1 处通过反射来创建 ContentProvider 类型的 localProvider 对象,并在注释 2 处调用了它的 attachInfo 方法: frameworks/base/core/java/android/content/ContentProvider.java
- private void attachInfo(Context context, ProviderInfo info,booleantesting) {
- ...
- ContentProvider.this.onCreate();
- }
- }
在 attachInfo 方法中调用了 onCreate 方法,它是一个抽象方法。这样 Content Provider 就启动完毕。 最后给出 AMS 启动 Content Provider 的时序图。
来源: http://blog.csdn.net/itachi85/article/details/72618688