As we all know,ContentProvider这个组件只能进行静态注册,要跟这个组件讲道理,就必须从它的静态注册讲起。而ContentProvider的静态注册是从AndroidManifest.xml解析出来的,当然其他组件也是解析后完成注册的。解析是一个很大的工作量,所以Android系统特地提供了一个PackageManagerService(缩写,PMS)类负责解析工作,它继承自IPackageManager.Stub。

1. PMS的启动

PMS的内部提供了一个静态方法main,顾名思义,它是PMS的入口方法。Command+Shift+F选中Scope#All Places选项,搜索“PackageManagerService.main”字符串,会发现SystemServer类调用了它。继续追踪会发现SystemServer#startBootstrapServices方法对它进行直接调用,startBootstrapServices又在SystemServer#run方法中被调用,而run方法又被SystemServer#main方法调用,这个main方法有一行注释:

    /*** The main entry point from zygote.* 译文:zygote的主切入点*/public static void main(String[] args) {new SystemServer().run();}

根据注释可知,SystemServer#main是zygote的主切入点,由于系统所有进程都从zygote进程fork而来,那么该方法也就是整个应用的起点。由于PMS继承自IPackageManager.Stub,这是一次Binder机制,SystemServer是客户端,PMS是服务端。

2. PMS的解析

根据第1节我们知道,SystemServer最终会调用PMS#main方法,在该方法内部完成PMS的实例化,代码如下:

public static PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {......PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);m.enableSystemUserPackages();ServiceManager.addService("package", m);return m;}

进入PackageManagerService构造方法,它内部调用了scanDirTracedLI方法,其中一个方法传入了mAppInstallDir这个全局变量,它是对第三方安装包的解析,核心代码如下:

if (!mOnlyCore) {......scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);

其中,mAppInstallDir的注释如下:

/** Directory where installed third-party apps stored */final File mAppInstallDir;

从注释可知,这就是我们从应用商店下载第三方APP的存储目录,PMS追踪这个目录,对所有的apk进行逐个解析、注册。点击进入scanDirTracedLI方法,它的内部调用了scanDirLI方法,该方法内部又会调用scanPackageLI方法,它将任务又交给了scanPackageInternalLI方法执行,它也未做具体的工作,而是将任务交给了scanPackageLI方法,核心代码如下:

// Note that we invoke the following method only if we are about to unpack an applicationPackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags| SCAN_UPDATE_SIGNATURE, currentTime, user);

从注释可以知道,scanPackageLI方法只有在解包的时候才会调用,点击进入该方法发现它调用了scanPackageDirtyLI方法,该方法很长,核心代码只有一行:

// Modify state for the given package setting
// 译文:修正被给的包的设置状态。注意:解析的过程实际上就是,修改包的内容适配当前设备的过程。commitPackageSettings(pkg, pkgSetting, user, scanFlags, (policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);

它最终调用了commitPackageSettings方法,静态注册就是由它完成的。

3. ContentProvider的注册

点击进入PMS#commitPackageSettings方法,可以看到它的注释如下:

    /*** Adds a scanned package to the system. When this method is finished, the package will* be available for query, resolution, etc...* 译文:添加一个扫码到的包到系统中,当这个方法调用完之后,包就可以用于查询、解析等操作了。*/private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting,UserHandle user, int scanFlags, boolean chatty) throws PackageManagerException {......int N = pkg.providers.size();  //代码3-1StringBuilder r = null;int i;for (i=0; i<N; i++) {   //代码3-2PackageParser.Provider p = pkg.providers.get(i);p.info.processName = fixProcessName(pkg.applicationInfo.processName,p.info.processName);mProviders.addProvider(p);    //代码3-3p.syncable = p.info.isSyncable;if (p.info.authority != null) {String names[] = p.info.authority.split(";");p.info.authority = null;for (int j = 0; j < names.length; j++) {if (j == 1 && p.syncable) {// We only want the first authority for a provider to possibly be// syncable, so if we already added this provider using a different// authority clear the syncable flag. We copy the provider before// changing it because the mProviders object contains a reference// to a provider that we don't want to change.// Only do this for the second authority since the resulting provider// object can be the same for all future authorities for this provider.p = new PackageParser.Provider(p);p.syncable = false;}if (!mProvidersByAuthority.containsKey(names[j])) {mProvidersByAuthority.put(names[j], p); //代码3-4if (p.info.authority == null) {p.info.authority = names[j];} else {p.info.authority = p.info.authority + ";" + names[j];}......} else {PackageParser.Provider other = mProvidersByAuthority.get(names[j]);......}}}if (chatty) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(p.info.name);  //代码3-6}}......}

根据注释可知,系统调用过该方法后,就可以使用包里面的注册组件了。方法中从代码3-1开始ContentProvider的注册过程,在代码3-2处根据安装包中ContentProvider的数量开启循环注册,在代码3-3处将解析出的ContentProvider实例添加到mProviders这个全局变量中,mProviders是ProviderIntentResolver的实例,ProviderIntentResolver内部有一个同名的私有的final全局变量,核心代码如下:

private final ArrayMap<ComponentName, PackageParser.Provider> mProviders= new ArrayMap<ComponentName, PackageParser.Provider>();

ProviderIntentResolver#mProviders才是ContentProvider的实际持有者,通俗的讲就是,ContentProvider的注册过程就是将AndroidManifest中解析出的实例保存在ArrayMap中。
在代码3-4处,将content://com.edwin.idea.FirstContentProvider/*后的第一个目录名保存在mProvidersByAuthority中,mProvidersByAuthority也是一个ArrayMap,它的定义如下:

    // Mapping from provider base names (first directory in content URI codePath)// to the provider information.// 译文:provider名称和provider信息的映射,其中provider名指的是内容URI代码路径下的第一个目录final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority =new ArrayMap<String, PackageParser.Provider>();

值得注意的是,Authority的准确翻译是权柄,mProvidersByAuthority保存的是注册时authorities属性对应的值,它是ContentProvider是唯一标识,其他进程访问FirstContentProvider,必须传入这个Uri前缀+指定信息(“/*”或“/TABLE/ROW”等),代码如下:

Uri uri = Uri.parse("content://com.edwin.idea.FirstContentProvider/*");

代码3-6处,仅通过一行代码r.append(p.info.name),将注册时ContentProvider组件中name属性对应的值保存在字符串r中。这样整个注册过程就完成了。
所以,ContentProvider的静态注册过程,其本质就是将ContentProvider实例及其属性值保存在ArrayMap或String字符串中而已。当然,其他组件也是类似的。

4. ContentProvider的启动第一次Binder机制

ContentProvider作为一个进程间通信组件,一旦完成注册就可以供其它进程进行增删查改操作,但无论哪一个方法都可以触发ContentProvider的启动。这里以最受欢迎的query方法作为分析起点。根据《Activity启动流程源码探究》我们可以知道Context的具体实现类是ContextImpl,同样的,ContentProvider的启动也是在ContextImpl中通过调用getContentResolver方法,该方法返回一个ContentResolver实例,ContentResolver类的内部封装了query方法,追踪ContentResolver#query方法,在其重载方法里调用了acquireUnstableProvider方法,该方法返回了IContentProvider的实例。核心代码如下:

    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,@Nullable String[] projection, @Nullable Bundle queryArgs,@Nullable CancellationSignal cancellationSignal) {Preconditions.checkNotNull(uri, "uri");IContentProvider unstableProvider = acquireUnstableProvider(uri);

追踪ContentResolver#acquireUnstableProvider会发现它调用了一个受保护类型的抽象重载方法,其具体实现在派生类中。我们的ContextImpl#getContentResolver返回了一个ContentResolver实例,它的具体实现在ContextImpl#ApplicationContentResolver中,我们可以看到acquireUnstableProvider方法的具体实现,代码如下:

        @Overrideprotected IContentProvider acquireUnstableProvider(Context c, String auth) {return mMainThread.acquireProvider(c,ContentProvider.getAuthorityWithoutUserId(auth),resolveUserIdFromAuthority(auth), false);}

它直接调用了主线程的acquireProvider方法,这里的mMainThread当然就是ActivityThread。点击进入ActivityThread#acquireProvider方法,其代码如下:

   public final IContentProvider acquireProvider(Context c, String auth, int userId, boolean stable) {final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable); // 代码4-1if (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.ContentProviderHolder holder = null; try {holder = ActivityManager.getService().getContentProvider( // 代码4-2getApplicationThread(), auth, userId, stable);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}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,    // 代码4-3true /*noisy*/, holder.noReleaseNeeded, stable);return holder.provider;}

它主要完成两个任务:1. 检查正在请求的provider是否已经存在,如果存在直接返回当前provider,供请求方或者叫客户端使用;2. provider不存在时,启动目标进程,构造目标provider;
代码4-1处,通过调用了acquireExistingProvider方法完成了第一次Binder通信。但是我们分析的当然是首次调用的情况,也就是任务2的情形。
代码4-2处,通过调用AMS#getContentProvider方法,直接开始一次Binder机制,其中ActivityThread是客户端,AMS是服务端。

5.ContentProvider的启动第二次Binder机制

在AMS中找到getContentProvider方法,它将任务交给了AMS#getContentProviderImpl方法,参数name就是在AndroidManifest中注册ContentProvider组件时的authorities属性值,前面提到通过它可以找到目标进程中的ContentProvider。该方法还通过cpr.newHolder创建了ContentProviderHolder实例,这里的cpr是ContentProviderRecord的实例。在创建ContentProviderHolder的同时调用了startProcessLocked方法,核心代码如下:

    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,String name, IBinder token, boolean stable, int userId) {ContentProviderRecord cpr;ContentProviderConnection conn = null;ProviderInfo cpi = null;......if (!providerRunning) {try {cpi = AppGlobals.getPackageManager().resolveContentProvider(name, // 代码5-0STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);} catch (RemoteException ex) {}if (cpi == null) {return null;}......try {checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( // 代码5-1cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId);checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");if (ai == null) {Slog.w(TAG, "No package info for content provider "+ cpi.name);return null;}ai = getAppInfoForUser(ai, userId);cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);// 代码5-2} catch (RemoteException ex) {// pm is in same process, this will never happen.} finally {Binder.restoreCallingIdentity(ident);}......// Use existing process if already startedcheckTime(startTime, "getContentProviderImpl: looking for process record");ProcessRecord proc = getProcessRecordLocked(cpi.processName, cpr.appInfo.uid, false);if (proc != null && proc.thread != null && !proc.killed) {if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER, "Installing in existing process " + proc);if (!proc.pubProviders.containsKey(cpi.name)) {checkTime(startTime, "getContentProviderImpl: scheduling install");proc.pubProviders.put(cpi.name, cpr);try {proc.thread.scheduleInstallProvider(cpi);} catch (RemoteException e) {}}} else {......proc = startProcessLocked(cpi.processName, // 代码5-3cpr.appInfo, false, 0, "content provider",new ComponentName(cpi.applicationInfo.packageName,cpi.name), false, false, false);......if (proc == null) {......return null;}}cpr.launchingApp = proc;mLaunchingProviders.add(cpr);......}return cpr != null ? cpr.newHolder(conn) : null;}

代码5-0处,通过PackageManagerService#resolveContentProvider方法根据name这个authorities属性拿到ProviderInfo实例;代码5-1处,通过PackageManagerService#getApplicationInfo拿到ApplicationInfo实例,在代码5-2处传入ContentProviderRecord的构造方法,构建ContentProviderRecord实例cpr。由于进程未启动,getProcessRecordLocked会返回null,系统会自动进入代码5-3所在代码块,执行开启新进程逻辑。进程的启动还是有章可循的,为Google工程师点赞!

startProcessLocked的功能就是其方法名:开启进程Process。追踪该方法,在其重载方法中,最终调用了Process.start方法,核心代码如下:

 private final void startProcessLocked(ProcessRecord app, String hostingType,String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {......            // Start the process.  It will either succeed and return a result containing// the PID of the new process, or else throw a RuntimeException.boolean isActivityProcess = (entryPoint == null);if (entryPoint == null) entryPoint = "android.app.ActivityThread";......{startResult = Process.start(entryPoint,app.processName, uid, uid, gids, debugFlags, mountExternal,app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,app.info.dataDir, invokeWith, entryPointArgs);}

追本溯源,我们再来看看Process#start这个静态方法的功能,声明如下:

    /*** Start a new process.* 开启一个进程* * <p>If processes are enabled, a new process is created and the* static main() function of a <var>processClass</var> is executed there.* The process will continue running after this function returns.* 如果进程使能,创建一个新进程并且,进程类的静态main方法在那个进程中执行。* * <p>If processes are not enabled, a new thread in the caller's* process is created and main() of <var>processClass</var> called there.* 如果进程未使能,在调用进程中创建一个新的线程,并且执行进程类的静态main方法在调用处执行。* * @param processClass The class to use as the process's main entry*                     point.* processClass类是进程的主切入点* * @return An object that describes the result of the attempt to start the process.* @throws RuntimeException on fatal start failure* * {@hide}*/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 abi,String instructionSet,String appDataDir,String invokeWith,String[] zygoteArgs) {return zygoteProcess.start(processClass, niceName, uid, gid, gids,debugFlags, mountExternal, targetSdkVersion, seInfo,abi, instructionSet, appDataDir, invokeWith, zygoteArgs);}

根据注释我们可以知道,start方法的第一个参数processClass是新创建进程的主切入点,新进程创建后会调用processClass的静态main方法。
返回到startProcessLocked方法,我们发现processClass的赋值是entryPoint,而entryPoint的赋值为"android.app.ActivityThread",换句话说,系统开启一个Process之后,首先会调用ActivityThread.main方法作为切入点。在ActivityThread#main方法内部,主要完成以下任务:1. 创建mainLooper;2.创建ActivityThread实例;3.调用ActivityThread#attach方法完成具体的工作;4.创建sMainThreadHandler作为主线程的handler,就是H。这四个任务充分说明了在进行主线程消息通信时不需要再次声明Looper和Handler。
进入attach方法,发现它将具体任务交给了AMS,核心代码如下:

    private void attach(boolean system) {......if (!system) {......final IActivityManager mgr = ActivityManager.getService();try {mgr.attachApplication(mAppThread);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}......

这里,ActivityThread作为客户端,通过AMS#attachApplication方法,向服务端AMS发起请求,注意,这里已经是被启动的新进程了。

6.ContentProvider的启动第三次Binder机制

进入AMS#attachApplication方法,发现它将锅甩给了attachApplicationLocked,在attachApplicationLocked内部调用了ApplicationThread的bindApplication方法,核心代码如下:

           if (app.instr != null) {thread.bindApplication(processName, appInfo, providers,app.instr.mClass,profilerInfo, app.instr.mArguments,app.instr.mWatcher,app.instr.mUiAutomationConnection, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(getGlobalConfiguration()), app.compat,getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial);} else {thread.bindApplication(processName, appInfo, providers, null, profilerInfo,null, null, null, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(getGlobalConfiguration()), app.compat,getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial);}

这里的thread就是ActivityThread#ApplicationThread的具体实现。是的,第四次Binder机制开始了。

7.ContentProvider的启动第四次Binder机制

进入ActivityThread#ApplicationThread#bindApplication方法,发现它将所有从客户端AMS传入的参数封装在AppBindData类的一个实例中,然后通过H.BIND_APPLICATION切换到了主线程。这个消息的对应处理方法是handleBindApplication,进入ActivityThread#handleBindApplication方法,它主要主要完成ContextImpl的创建、Instrumentation的创建、Application的创建、ContentProvider的创建等工作。

  1. ContextImpl和Instrumentation的创建
           if (ii != null) {final ApplicationInfo instrApp = new ApplicationInfo();......final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,appContext.getClassLoader(), false, true, false);final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);try {final ClassLoader cl = instrContext.getClassLoader(); mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();} catch (Exception e) {throw new RuntimeException("Unable to instantiate instrumentation "+ data.instrumentationName + ": " + e.toString(), e);}......} else {mInstrumentation = new Instrumentation();}

可见,Instrumentation是由ClassLoader加载完成的。
2. Application的创建

            // If the app is being launched for full backup or restore, bring it up in// a restricted environment with the base application class.Application app = data.info.makeApplication(data.restrictedBackupMode, null);mInitialApplication = app;
  1. ContentProvider的创建
            // don't bring up providers in restricted mode; they may depend on the// app's custom Application classif (!data.restrictedBackupMode) {if (!ArrayUtils.isEmpty(data.providers)) {installContentProviders(app, data.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);}}

installContentProviders方法完成了ContentProvider的创建和启动任务。代码如下:

private void installContentProviders(Context context, List<ProviderInfo> providers) {final ArrayList<ContentProviderHolder> results = new ArrayList<>();for (ProviderInfo cpi : providers) {//代码7.3-1......ContentProviderHolder cph = installProvider(context, null, cpi,false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);if (cph != null) {cph.noReleaseNeeded = true;results.add(cph);}}try {//代码7.3-2ActivityManager.getService().publishContentProviders(getApplicationThread(), results);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}

代码7.3-1通过for循环调用installProvider方法来完成ContentProvider的安装和创建工作。在其内部有一段代码如下:

try {final java.lang.ClassLoader cl = c.getClassLoader();localProvider = (ContentProvider)cl.loadClass(info.name).newInstance();provider = localProvider.getIContentProvider(); //代码7.3-3if (provider == null) {...return null;}...// XXX Need to create the correct context for this provider.localProvider.attachInfo(c, info); //代码7.3-4} catch (java.lang.Exception e) {if (!mInstrumentation.onException(null, e)) {throw new RuntimeException("Unable to get provider " + info.name+ ": " + e.toString(), e);}return null;}

可见,类加载器完成了ContentProvider的创建工作。而且代码7.3-4处还调用了ContentProvider#attachInfo方法,该方法最终调用了ContentProvider的onCreate方法。这一任务是在attachInfo的重载方法中完成的,代码如下:

private void attachInfo(Context context, ProviderInfo info, boolean testing) {mNoPerms = testing;/** Only allow it to be set once, so after the content service gives* this to us clients can't change it.*/if (mContext == null) {mContext = context;if (context != null) {mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);}mMyUid = Process.myUid();if (info != null) {setReadPermission(info.readPermission);setWritePermission(info.writePermission);setPathPermissions(info.pathPermissions);mExported = info.exported;mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;setAuthorities(info.authority);}ContentProvider.this.onCreate();}}

其核心代码只有最后一行:ContentProvider.this.onCreate(),而且它只调用一次,注释可知。注意:此时Application的onCreate方法还未调用,这就是ContentProvider这个组件的特殊之处。ContentProvider创建完成之后,其他的进程就可以调用它进行数据传输了。
4. 调用Application的onCreate方法
在ActivityThread的handleBindApplication方法中,执行完installContentProviders方法后,会接着执行如下代码:

            try {mInstrumentation.callApplicationOnCreate(app);} catch (Exception e) {if (!mInstrumentation.onException(app, e)) {throw new RuntimeException("Unable to create application " + app.getClass().getName()+ ": " + e.toString(), e);}}

这里就是通过Instrumentation的callApplicationOnCreate方法执行Application的onCreate方法了。经过以上四个步骤,ContentProvider的启动就完成了。

8. ContentProvider的启动第一次Binder机制IContentProvider的具体实现

让我们回到代码4-3处,ActivityThread#installProvider最终完成了holder实例的创建,但鉴于Google 工程师构造代码的特殊技巧,我们并未直接看到IContentProvider的具体实现。直接Command+SHIFT+F,选中Scope的All Places选项,发现ContentProviderNative继承了Binder类并且实现了IContentProvider,而且其内部子类ContentProviderProxy也实现了该接口。搜索ContentProviderNative的调用,发现ContentProvider的内部类Transport继承了该类,看来Transport是它的最终实现类。
在ActivityThread#installProvider方法中代码7.3-3处,调用了localProvider.getIContentProvider给IContentProvider赋值,这里的localProvider就是ContentProvider的实例,进入ContentProvider#getIContentProvider方法,我们可发现如下代码实现:

    /*** Returns the Binder object for this provider.** @return the Binder object for this provider* @hide*/public IContentProvider getIContentProvider() {return mTransport;}

该方法返回的是mTransport这个实例,也就是说ContentProvider#Transport是IContentProvider的具体实现。也就是说第一Binder机制是ContextImpl作为客户端向ContentProvider#Transport这个服务端发起查询请求,query最终调用的是ontentProvider#Transport#query方法。进入ContentResolver#query方法可看到如下代码:

    /*** Query the given URI, returning a {@link Cursor} over the result set* with support for cancellation.** <p>For best performance, the caller should follow these guidelines:** <li>Provide an explicit projection, to prevent reading data from storage* that aren't going to be used.** Provider must identify which QUERY_ARG_SORT* arguments were honored during* the preparation of the result set by including the respective argument keys* in the {@link Cursor} extras {@link Bundle}. See {@link #EXTRA_HONORED_ARGS}* for details.** @see #QUERY_ARG_SORT_COLUMNS, #QUERY_ARG_SORT_DIRECTION, #QUERY_ARG_SORT_COLLATION.** @param uri The URI, using the content:// scheme, for the content to*         retrieve.* @param projection A list of which columns to return. Passing null will*         return all columns, which is inefficient.* @param queryArgs A Bundle containing any arguments to the query.* @param cancellationSignal A signal to cancel the operation in progress, or null if none.* If the operation is canceled, then {@link OperationCanceledException} will be thrown* when the query is executed.* @return A Cursor object, which is positioned before the first entry, or null* @see Cursor*/public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,@Nullable String[] projection, @Nullable Bundle queryArgs,@Nullable CancellationSignal cancellationSignal) {Preconditions.checkNotNull(uri, "uri");IContentProvider unstableProvider = acquireUnstableProvider(uri);if (unstableProvider == null) {return null;}IContentProvider stableProvider = null;Cursor qCursor = null;try {......try {qCursor = unstableProvider.query(mPackageName, uri, projection,queryArgs, remoteCancellationSignal);} catch (DeadObjectException e) {// The remote process has died...  but we only hold an unstable// reference though, so we might recover!!!  Let's try!!!!// This is exciting!!1!!1!!!!1unstableProviderDied(unstableProvider);stableProvider = acquireProvider(uri);if (stableProvider == null) {return null;}qCursor = stableProvider.query(mPackageName, uri, projection, queryArgs, remoteCancellationSignal);}......}}

无论是unstableProvider还是stableProvider都是IContentProvider的引用,也就是ContentProvider#Transport类的实例,他们最终都调用了query方法,也就是ContentProvider#Transport#query方法。当然,insert、delete、update的调用也是类似的。

9. ContentProvider的启动总结

Process1通过ContextImpl通过调用IContentProvider的query方法查询Process2的数据,触发Process2的启动。在启动时,自动调用Process#start方法,Process2启动时会在其start方法中调用其ActivityThread的main方法。Process2#main方法执行过程中,会先通过Binder通信切换到AMS服务端获取创建ContentProvider所需信息,然后通过IApplicationThread再次进行Binder通信切回到ActivityThread#ApplicationThread线程对信息进行封装。封装后的信息存储在AppBindData中,最后通过H这个主线程handler切换到ActivityThread主线程。在主线程中,依次创建ContextImpl实例、Instrumentation实例、Application实例和ContentProvider实例,并先后调用ContentProvider#onCreate、Application#onCreate方法。之后,Context.getResolverContent.query方法生效,完成数据查询工作。注意:具体查询任务是IContentProvider的实现类ContentProvider#Transport完成的。

诗云:

山外青山楼外楼,西湖歌舞几时休? 暖风熏得游人醉,直把杭州作汴州。

ContentProvider跨进程注册、启动流程源码解析相关推荐

  1. NioEventLoop启动流程源码解析

    NioEventLoop的启动时机是在服务端的NioServerSocketChannel中的ServerSocketChannel初始化完成,且注册在NioEventLoop后执行的, 下一步就是去 ...

  2. Kernel启动流程源码解析 1 head.S

    bootloader在跳转到kernel前,需要确保如下设置: MMU = off, D-cache = off, I-cache = on or off x0 = physical address ...

  3. Kernel启动流程源码解析 2 head.S

    __cpu_setup.定义kernel\arch\arm64\mm\proc.S中. #define MAIR(attr, mt)    ((attr) << ((mt) * 8)) / ...

  4. SpringSecurity启动流程源码解析

    前面两期我讲了SpringSecurity认证流程和SpringSecurity鉴权流程,今天是第三期,是SpringSecurity的收尾工作,讲SpringSecurity的启动流程. 就像很多电 ...

  5. Android Launcher启动应用程序流程源码解析

    带着问题看源码 点击桌面Launcher图标后做了哪些工作? 应用程序什么时候被创建的? Application和MainActivity的onCreate()方法什么时候被调用的? 概述 在Andr ...

  6. BroadcastReceiver的跨进程注册、接收流程源码解析

    根据<Activity跨进程启动流程源码探究>我们可以清楚以下几点: 1)Context的通用实现是在ContextIml这个类中 2)Activity的启动过程需要借助ActivityM ...

  7. Service通过onBind跨进程启动流程源码探究

    根据<Activity跨进程启动流程源码探究>我们可以清楚以下几点: 1)Context的通用实现是在ContextIml这个类中 2)Activity的启动过程需要借助ActivityM ...

  8. swoole 启动流程_EasySwoole 服务启动过程以及主体设计流程源码解析

    EasySwoole 服务启动过程以及主体设计流程源码解析 本文主要讲解EasySwoole 服务的启动过程,会通过源码片段讲解主体的设计流程 命令启动 当我们通过php easyswoole sta ...

  9. Activity启动流程源码分析(基于Android N)

    Activity启动流程源码分析 一个Activity启动分为两种启动方式,一种是从Launcher界面上的图标点击启动,另一种是从一个Activity中设置按钮点击启动另外一个Activity.这里 ...

  10. Android音频框架之二 用户录音启动流程源码走读

    前言 此篇是对<Android音频框架之一 详解audioPolicy流程及HAL驱动加载>的延续,此系列博文是记录在Android7.1系统即以后版本实现 内录音功能. 当用户使用 Au ...

最新文章

  1. Python 把字符串变成浮点数
  2. php 简单路由实现
  3. AJAX-prototype.js实现Ajax
  4. Serverless 解惑——函数计算如何访问 PostgreSQL 数据库
  5. 关于Android studio3.0的坑之butterknife 7.0.1(低版本)
  6. 2021牛客暑期多校训练营5 E-Eert Esiwtib(树形dp+位运算)
  7. Lecture 21 Parallel Algorithms II
  8. datagridview绑定与详细说明 (搜集)
  9. 支付宝的架构到底有多牛逼?还没看完我就跪了!
  10. 【VB.NET】VB.NET数据库技术问题的解答
  11. iPhone+wp7并行初体验
  12. java手机编程软件_手机java编程软件下载
  13. 电子元器件封装知识大全(内含AD封装库下载资料)
  14. 详解EtherCAT主站SOEM源码_eepromtool.c
  15. [Python]正相匹配中文分词【哈工大 车万翔老师视频公开课】
  16. 龙格库塔方法在实际生活中的应用(数值计算Java)
  17. 【vue+cesium】加载三维天地图
  18. 在Java中为JFrame添加背景音乐
  19. Libero安装最新版本2022.2
  20. 手机扫描计数器有哪些?实用扫描计数软件分享给你

热门文章

  1. 北京工业大学2020计算机考研复试科目,2020北京工业大学计算机考研专业课调整...
  2. Linux完美学习笔记
  3. 5G 产业链:基站天线和小基站爆发潜力大
  4. 深入探究:TIFF格式的影像如何转jpg (保持色彩不变)
  5. 什么是字节?字节怎么进行换算?
  6. 发现一款好用的 java web报表工具
  7. oracle数据库自动修复,【案例】Oracle数据库由于存在坏块导致无法启动的恢复过程...
  8. Java获取实体类字段名
  9. UVA 177 Paper Folding
  10. css 背景颜色默认,css改变文字选择时的默认背景颜色