一、什么ContentProvider
二、如何使用ContentProvider
三、沙场练兵-实例操练
四、深入理解ContentProvider原理
为什么使用ContentProvider可以实现跨进程的通讯,第一反应肯定是这货和binder有关,因为android中只有稍微跟跨进程搭上边的,必定想到binder。
下面就来分析ContentProvider是怎么一步一步利用binder实现跨进程通信的:
1、首先你得创建一个ContentProvider运行在进程A,如上篇博客
AndroidManifest.xml中定义provider:
<provider
android:name=".MyContentProvider"
android:authorities="telefk"//这个很重要,Uri中的主机名,查询数据就靠它
android:enabled="true"
android:exported="true">
</provider>
2、进程B中通过如下接口来访问进程A的数据:
getContentResolver().query(uri,columns,null,null,null);
getContentResolver()实际调用的ContextImpl.java的方法返回mContentResolver对象
此对象在ContextImpl的构造方法中实例化
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
现在来看query方法,其在ContentResolver.java中定义:
public final @Nullable Cursor query(final @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);//这个函数很关键,通过指定的Uri找到我们刚刚定义的provider if (unstableProvider == null) { return null; } IContentProvider stableProvider = null; ......
接下来看acquireUnstableProvider()函数:
百转千回,最终走到了刚刚返回的ApplicationContentResolver.java中
@Override protected IContentProvider acquireUnstableProvider(Context c, String auth) { return mMainThread.acquireProvider(c, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), false); }
这里又调用了ActivityThread.java 的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);//很关键,请注意这里跨进程调到ActivityManagerService.java的方法 } 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; }
我们继续来看ActivityManagerService.java 的getContentProvider()方法:
@Override public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) { enforceNotIsolatedCaller("getContentProvider"); if (caller == null) { String msg = "null IApplicationThread when getting content provider " + name; Slog.w(TAG, msg); throw new SecurityException(msg); } // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal // with cross-user grant. return getContentProviderImpl(caller, name, null, stable, userId); }
肾呐~还没到头!又继续往下走,大家继续坚持:
//这个方法很长,我们挑选重点的分析,方法第二个参数name就是我们刚刚query传进来的uir的authorityprivate final ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr;//AMS中用来记录provider的,还有其他三大组件AcitityRecord、ServiceRecord、BroadcastRecord ContentProviderConnection conn = null; ProviderInfo cpi = null;//这个应该是PMS解析应用的AndroidManifest得来的信息 ......
// First check if this content provider has been published...
cpr = mProviderMap.getProviderByName(name, userId);
......
如果发现对应的provider 已经运行,这个我们之后再分析,先分析没有运行的情况:
boolean providerRunning = cpr != null; if (providerRunning) { cpi = cpr.info; String msg; checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission"); if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) != null) { throw new SecurityException(msg); } checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission"); if (r != null && cpr.canRunHere(r)) { // 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(); checkTime(startTime, "getContentProviderImpl: incProviderCountLocked"); // In this case the provider instance already exists, so we can // return it right away. conn = incProviderCountLocked(r, cpr, token, stable); 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. checkTime(startTime, "getContentProviderImpl: before updateLruProcess"); updateLruProcessLocked(cpr.proc, false, null); checkTime(startTime, "getContentProviderImpl: after updateLruProcess"); } } 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); } } checkTime(startTime, "getContentProviderImpl: before updateOomAdj"); boolean success = updateOomAdjLocked(cpr.proc); maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name); checkTime(startTime, "getContentProviderImpl: after updateOomAdj"); if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "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); checkTime(startTime, "getContentProviderImpl: before appDied"); appDiedLocked(cpr.proc); checkTime(startTime, "getContentProviderImpl: after appDied"); 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); }
if (!providerRunning) { try { checkTime(startTime, "getContentProviderImpl: before resolveContentProvider"); cpi = AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); checkTime(startTime, "getContentProviderImpl: after resolveContentProvider"); } catch (RemoteException ex) { } if (cpi == null) { return null; } // If the provider is a singleton AND // (it‘s a call within the same user || the provider is a // privileged app) // Then allow connecting to the singleton provider singleton = isSingleton(cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags) && isValidSingletonCall(r.uid, cpi.applicationInfo.uid); if (singleton) { userId = UserHandle.USER_OWNER; } cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId); checkTime(startTime, "getContentProviderImpl: got app info for user"); String msg; checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission"); if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton)) != null) { throw new SecurityException(msg); } checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission"); 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 running. If not, // we don‘t want to allow it to run. if (!isUserRunningLocked(userId, false)) { 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); checkTime(startTime, "getContentProviderImpl: before getProviderByClass"); cpr = mProviderMap.getProviderByClass(comp, userId); checkTime(startTime, "getContentProviderImpl: after getProviderByClass"); final boolean firstClass = cpr == null; if (firstClass) { final long ident = Binder.clearCallingIdentity(); try { checkTime(startTime, "getContentProviderImpl: before getApplicationInfo"); ApplicationInfo ai = AppGlobals.getPackageManager(). getApplicationInfo( cpi.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);//很关键的,首先创建cpr,然后再启动目标进程