关于ContentProvider网上有很多文章进行了详细的分析,这里我初略地画了一个getContentProvider时序图,帮助整体上进行理解把握。其中ActivityThread_A代表进程A的ActivityThread,ActivityThread_B代表进程B的ActivityThread,这里是在进程A中去get进程B中的ContentProvider。有四个关键点,下面将一一详细说明下。
了解了上面这些信息我们再来看installProvider()函数。在上面的时序图中有两次调用,一次在进程B中,一次在进程A中。这里我们只关心installProvider(..,holder,..)函数的第二个参数holder。holder是一个ContentProviderHolder类型参数,如果holder.provider为null,那边需要在本次installProvider()函数调用中实例化ContentProvider对象,如果holder.provider不为null,则无需实例化ContentProvider对象。在进程B中installProvider()函数调用参数holder.provider=null,在进程A中installProvider()函数调用参数holder.provider!=null,因此进程B中会实例化ContentProvider对象,而进程A中holder.provider保存着进程B中ContentProvider.mTransport的代理,这一点读者稍微跟下代码便可发现。
private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null; IContentProvider provider; if (holder == null || holder.provider == null) { //holder.provider=null,意味着需要在此实例化ContentProvider对象。进程B中调用该函数满足这个条件。 if (DEBUG_PROVIDER || noisy) { Slog.d(TAG, "Loading provider " + info.authority + ": " + info.name); } Context c = null; ApplicationInfo ai = info.applicationInfo; Slog.d(TAG, "installProvider: context.getPackageName()=" + context.getPackageName()); if (context.getPackageName().equals(ai.packageName)) { c = context; } else if (mInitialApplication != null && mInitialApplication.getPackageName().equals(ai.packageName)) { c = mInitialApplication; } else { try { c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); //创建ContentProvider所在的package的Context; } catch (PackageManager.NameNotFoundException e) { // Ignore } } if (c == null) { Slog.w(TAG, "Unable to get context for package " + ai.packageName + " while loading content provider " + info.name); return null; } try { final java.lang.ClassLoader cl = c.getClassLoader(); localProvider = (ContentProvider)cl. loadClass(info.name).newInstance(); //实例化ContentProvider对象; provider = localProvider.getIContentProvider(); if (provider == null) { Slog.e(TAG, "Failed to instantiate class " + info.name + " from sourceDir " + info.applicationInfo.sourceDir); return null; } if (DEBUG_PROVIDER) Slog.v( TAG, "Instantiating local provider " + info.name); // XXX Need to create the correct context for this provider. localProvider.attachInfo(c, info); } catch (java.lang.Exception e) { if (!mInstrumentation.onException(null, e)) { throw new RuntimeException( "Unable to get provider " + info.name + ": " + e.toString(), e); } return null; } } else { //holder.provider!=null,进程A中调用该函数时满足这个逻辑,holder.provider指向mTransport的代理; provider = holder.provider; if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": " + info.name); } IActivityManager.ContentProviderHolder retHolder; //下面的逻辑主要是进行本地保存provider,方便第二次调用同一个ContentProvider时,无需重新到AMS中去查询。 synchronized (mProviderMap) { if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider + " / " + info.name); IBinder jBinder = provider.asBinder(); if (localProvider != null) { ComponentName cname = new ComponentName(info.packageName, info.name); ProviderClientRecord pr = mLocalProvidersByName.get(cname); if (pr != null) { if (DEBUG_PROVIDER) { Slog.v(TAG, "installProvider: lost the race, " + "using existing local provider"); } provider = pr.mProvider; } else { holder = new IActivityManager.ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = true; pr = installProviderAuthoritiesLocked(provider, localProvider, holder); mLocalProviders.put(jBinder, pr); mLocalProvidersByName.put(cname, pr); } retHolder = pr.mHolder; } else { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null) { if (DEBUG_PROVIDER) { Slog.v(TAG, "installProvider: lost the race, updating ref count"); } // We need to transfer our new reference to the existing // ref count, releasing the old one... but only if // release is needed (that is, it is not running in the // system process). if (!noReleaseNeeded) { incProviderRefLocked(prc, stable); try { ActivityManagerNative.getDefault().removeContentProvider( holder.connection, stable); } catch (RemoteException e) { //do nothing content provider object is dead any way } } } else { ProviderClientRecord client = installProviderAuthoritiesLocked( provider, localProvider, holder); if (noReleaseNeeded) { prc = new ProviderRefCount(holder, client, 1000, 1000); } else { prc = stable ? new ProviderRefCount(holder, client, 1, 0) : new ProviderRefCount(holder, client, 0, 1); } mProviderRefCountMap.put(jBinder, prc); } retHolder = prc.holder; } } return retHolder; }上面所说的只是普通情况,当然还有特殊情况。比如这个ContentProvider定义时设置了multiprocess=true,那么ContentProvider便会在进程A中进行实例化,而是不是在进程B中实例化,同时也没必要启动进程B了,意味着哪个进程去get ContentProvider,哪个进程自己实例化ContentProvider,这个读者可以仔细分析AMS.getContentProviderImpl()函数什么时候return的holder.provider = null来验证这一点。
附getContentProviderImpl()函数分析
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null; ProviderInfo cpi = null; synchronized(this) { ProcessRecord r = null; if (caller != null) { r = getRecordForAppLocked(caller); //①获取调用进程记录信息块 if (r == null) { throw new SecurityException( "Unable to find app for caller " + caller + " (pid=" + Binder.getCallingPid() + ") when getting content provider " + name); } } // First check if this content provider has been published... cpr = mProviderMap.getProviderByName(name, userId); //②根据authority获取ContentProviderRecord,如果之前publish过ContentProvider,那么返回值必然不为null。 boolean providerRunning = cpr != null; if (providerRunning) { //②providerRunning=true表示ContentProvider已经启动过了; cpi = cpr.info; String msg; if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) { throw new SecurityException(msg); } if (r != null && cpr.canRunHere(r)) { //③如果ContentProvider允许多进程实例化或目标进程就是调用进程,那么直接返回。返回的时候将ContentProviderHolder. Provider置空,这样调用进程便会在自己的进程中实例化ContentProvider。 // This provider has been published or is in the process // of being published... but it is also allowed to run // in the caller's process, so don't make a connection // and just let the caller instantiate its own instance. ContentProviderHolder holder = cpr.newHolder(null); // don't give caller the provider object, it needs // to make its own. holder.provider = null; return holder; } final long origId = Binder.clearCallingIdentity(); // In this case the provider instance already exists, so we can // return it right away. conn = incProviderCountLocked(r, cpr, token, stable);<span style="white-space:pre"> </span>//④创建一个ContentProviderConnection; if (conn != null && (conn.stableCount+conn.unstableCount) == 1) { if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { // If this is a perceptible app accessing the provider, // make sure to count it as being accessed and thus // back up on the LRU list. This is good because // content providers are often expensive to start. updateLruProcessLocked(cpr.proc, false, null); } } if (cpr.proc != null) { if (false) { if (cpr.name.flattenToShortString().equals( "com.android.providers.calendar/.CalendarProvider2")) { Slog.v(TAG, "****************** KILLING " + cpr.name.flattenToShortString()); Process.killProcess(cpr.proc.pid); } } boolean success = updateOomAdjLocked(cpr.proc); //返回false表示cpr.proc被杀了 if (DEBUG_PROVIDER) Slog.i(TAG, "Adjust success: " + success); // NOTE: there is still a race here where a signal could be // pending on the process even though we managed to update its // adj level. Not sure what to do about this, but at least // the race is now smaller. if (!success) { // Uh oh... it looks like the provider's process // has been killed on us. We need to wait for a new // process to be started, and make sure its death // doesn't kill our process. Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString() + " is crashing; detaching " + r); boolean lastRef = decProviderCountLocked(conn, cpr, token, stable); appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread); if (!lastRef) { // This wasn't the last ref our process had on // the provider... we have now been killed, bail. return null; } providerRunning = false; conn = null; } } Binder.restoreCallingIdentity(origId); } boolean singleton; if (!providerRunning) { // ⑤providerRunning=false,有两种情况:ContentProvider所在进程没有启动过、ContentProvider所在进程启动了,并publish了ContentProvider,但是挂掉了。 try { cpi = AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); } catch (RemoteException ex) { } if (cpi == null) { return null; } singleton = isSingleton(cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags); if (singleton) { userId = 0; } cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId); String msg; if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) { throw new SecurityException(msg); } if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate && !cpi.processName.equals("system")) { // If this content provider does not run in the system // process, and the system is not yet ready to run other // processes, then fail fast instead of hanging. throw new IllegalArgumentException( "Attempt to launch content provider before system ready"); } // Make sure that the user who owns this provider is started. If not, // we don't want to allow it to run. if (mStartedUsers.get(userId) == null) { Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " + name + ": user " + userId + " is stopped"); return null; } ComponentName comp = new ComponentName(cpi.packageName, cpi.name); cpr = mProviderMap.getProviderByClass(comp, userId); final boolean firstClass = cpr == null; if (firstClass) { //这是第一种情况,ContentProvider所在进程未启动过,那么new ContentProviderRecord try { ApplicationInfo ai = AppGlobals.getPackageManager(). getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId); 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); } catch (RemoteException ex) { // pm is in same process, this will never happen. } } if (r != null && cpr.canRunHere(r)) { //⑤如果ContentProvider允许多进程实例化,那么直接返回,让调用进程自己实例化ContentProvider; // If this is a multiprocess provider, then just return its // info and allow the caller to instantiate it. Only do // this if the provider is the same user as the caller's // process, or can run as root (so can be in any process). return cpr.newHolder(null); } // This is single process, and our app is now connecting to it. // See if we are already in the process of launching this // provider. final int N = mLaunchingProviders.size(); int i; for (i=0; i<N; i++) { //查询是否有其他进程正在加载该Provider if (mLaunchingProviders.get(i) == cpr) { break; } } // If the provider is not already being launched, then get it // started. if (i >= N) { final long origId = Binder.clearCallingIdentity(); try { // Content provider is now in use, its package can't be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( cpr.appInfo.packageName, false, userId); } catch (RemoteException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " + cpr.appInfo.packageName + ": " + e); } // 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 { //⑥启动Provider所在的目标进程; 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); } finally { Binder.restoreCallingIdentity(origId); } } // Make sure the provider is published (the same provider class // may be published under multiple names). if (firstClass) { mProviderMap.putProviderByClass(comp, cpr); } mProviderMap.putProviderByName(name, cpr); conn = incProviderCountLocked(r, cpr, token, stable); if (conn != null) { conn.waiting = true; } } } // Wait for the provider to be published... synchronized (cpr) { //⑦等待Provider被Publish,目标进程publish后会调用notifiyAll()接口,此时当前线程便可返回; while (cpr.provider == null) { if (cpr.launchingApp == null) { Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " + name + ": launching app became null"); EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS, UserHandle.getUserId(cpi.applicationInfo.uid), cpi.applicationInfo.packageName, cpi.applicationInfo.uid, name); return null; } try { if (DEBUG_MU) { Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp=" + cpr.launchingApp); } if (conn != null) { conn.waiting = true; } cpr.wait(); } catch (InterruptedException ex) { } finally { if (conn != null) { conn.waiting = false; } } } } return cpr != null ? cpr.newHolder(conn) : null; }