原文地址:https://skytoby.github.io/2019/ContentProvider%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/

基于Android10.0,分析ContentProvider原理

一、概述

ContentProvider用于提供数据的统一访问格式,封装具体的实现。对于数据的使用无需知道是数据库、文件、网络,只需要使用ContentProvider的数据操作接口,即增(insert)删(delete)改(update)查(query)。

1.1 ContentProvider

ContentProvider作为四大组件之一,没有Activity复杂的生命周期,只有简单的onCreate过程。ContentProvider是一个抽象类,当实现自己的ContentProvider类,需要继承ContentProvider,并且实现下面6个抽象即可:

insert(Uri, ContentValues):插入新数据;
delete(Uri, String, String[]):删除已有数据;
update(Uri, ContentValues, String, String[]):更新数据;
query(Uri, String[], String, String[], String):查询数据;
onCreate():执行初始化工作;
getType(Uri):获取数据MIME类型。

Uri数据格式如下:content://com.skytoby.articles/android/1

字段 含义 对应项
前缀 默认的固定开头格式 content://
授权 唯一表示provider com.skytoby.articles
路径 数据类别以及数据项 /android/1

1.2 ContentResolver

其他进程想要操作ContentProvider,需要获取其对应的ContentResolver,利用ContentResolver类完成对数据的增删改查操作,下面列举一个查询的操作。

//获取ContentResolver
ContentResolver cr = getContentResolver();
Uri uri = Uri.parse("content://com.skytoby.articles/android/1");
Cursor cursor = cr.query(uri, null, null, null, null);  //执行查询操作
...
cursor.close(); //关闭

1.3 类图

CPP和CPN是Binder通信的C/S两端

ACR(ApplicationContentResolver)继承于ContentResolver,是ContextImpl的内部类,ACR的实现通过调用其成员变量mMainThread来实现。

1.4 重要成员变量

类名 成员变量 含义
AMS CONTENT_PROVIDER_PUBLISH_TIMEOUT 默认值为10s
AMS mProviderMap 记录所有contentProvider
AMS mLaunchingProviders 记录存在客户端等待publish的ContentProviderRecord
PR pubProviders 该进程创建的ContentProviderRecord
PR conProviders 该进程使用的ContentProviderConnection
AT mLocalProviders 记录所有本地的ContentProvider,以IBinder以key
AT mLocalProvidersByName 记录所有本地的ContentProvider,以组件名为key
AT mProviderMap 记录该进程的contentProvider
AT mProviderRefCountMap 记录所有对其他进程中的ContentProvider的引用计数
  • CONTENT_PROVIDER_PUBLISH_TIMEOUT(10s): provider所在进程发布其ContentProvider的超时时长为10s,超过10s则会系统所杀;
  • mProviderMap: AMS和AT都有一个同名的成员变量, AMS的数据类型为ProviderMap,而AT则是以ProviderKey为key的ArrayMap类型;
  • mLaunchingProviders:记录的每一项是一个ContentProviderRecord对象, 所有的存在client等待其发布完成的contentProvider列表,一旦发布完成则相应的contentProvider便会从该列表移除;
  • PR:ProcessRecord, AT: ActivityThread;
  • mLocalProvidersmLocalProvidersByName:都是用于记录所有本地的ContentProvider,不同的只是key。

1.5 query流程图

二、发布ContentProvider

通过ContentProvider共享数据时,需要编写一个类继承ContentProvier,创建数据库,并在AndroidManifest声明该Provider,这样其他的进行就可以通过ContentResolver去查询共享的信息。先来看一下应用时如何发布ContentProvider,提供给其他应用使用。ContentProvider一般是在应用进程启动的时候启动,是四大组件中最早启动的。进程的启动在四大组件与进程启动那里有详细的分析。

发布ContentProvider分两种情况:Provider进程未启动,Provider进程已经启动但未发布。

  • Provider进程未启动

    systemserver进程调用startProcessLocked创建provider进程,并attach到systemserver后,通过binder机制到Provider进程执行bindApplication方法,见2.1节。

  • Provider进程已经启动但未发布

    如果发现provider进程已经存在且attach到systemserver,但对应的provider还没有发布,

    则通过binder机制到provider进程执行scheduleInstallProvider方法,见2.7节。

    这两种情况最后都会走到installProvider这个方法。

2.1 Provider进程未启动

2.1.1 AT.bindApplication

[->ActivityThread.java]

  public final void bindApplication(String processName, ApplicationInfo appInfo,List<ProviderInfo> providers, ComponentName instrumentationName,ProfilerInfo profilerInfo, Bundle instrumentationArgs,IInstrumentationWatcher instrumentationWatcher,IUiAutomationConnection instrumentationUiConnection, int debugMode,boolean enableBinderTracking, boolean trackAllocation,boolean isRestrictedBackupMode, boolean persistent, Configuration config,CompatibilityInfo compatInfo, Map services, Bundle coreSettings,String buildSerial, boolean autofillCompatibilityEnabled) {AppBindData data = new AppBindData();data.processName = processName;data.appInfo = appInfo;//providerdata.providers = providers;...sendMessage(H.BIND_APPLICATION, data);}

发送BIND_APPLICATION消息到主线程。

2.1.2 AT.handleMessage

[->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;}...}

主线程收到BIND_APPLICATION后,执行handleBindApplication方法。

2.1.3 AT.handleMessage

[->ActivityThread.java]

 private void handleBindApplication(AppBindData data) {// send up app name; do this *before* waiting for debugger//设置进程名Process.setArgV0(data.processName);android.ddm.DdmHandleAppName.setAppName(data.processName,UserHandle.myUserId());VMRuntime.setProcessPackageName(data.appInfo.packageName);...// Allow disk access during application and provider setup. This could// block processing ordered broadcasts, but later processing would// probably end up doing the same disk access.Application app;final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();try {// If the app is being launched for full backup or restore, bring it up in// a restricted environment with the base application class.//实例化Application,会调用attachBaseContext方法app = data.info.makeApplication(data.restrictedBackupMode, null);// Propagate autofill compat stateapp.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);mInitialApplication = app;//不是限制备份模式// 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)) {//安装ContentProvider,见2.4节installContentProviders(app, data.providers);}}try {//调用Application的onCreate方法mInstrumentation.callApplicationOnCreate(app);} catch (Exception e) {if (!mInstrumentation.onException(app, e)) {throw new RuntimeException("Unable to create application " + app.getClass().getName()+ ": " + e.toString(), e);}}} // 加载字体资源// Preload fonts resourcesFontsContract.setApplicationContextForResources(appContext);if (!Process.isIsolated()) {try {final ApplicationInfo info =getPackageManager().getApplicationInfo(data.appInfo.packageName,PackageManager.GET_META_DATA /*flags*/,UserHandle.myUserId());if (info.metaData != null) {final int preloadedFontsResource = info.metaData.getInt(ApplicationInfo.METADATA_PRELOADED_FONTS, 0);if (preloadedFontsResource != 0) {data.info.getResources().preloadFonts(preloadedFontsResource);}}} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}}

2.1.4 AT.installContentProviders

[->ActivityThread.java]

@UnsupportedAppUsageprivate void installContentProviders(Context context, List<ProviderInfo> providers) {final ArrayList<ContentProviderHolder> results = new ArrayList<>();for (ProviderInfo cpi : providers) {if (DEBUG_PROVIDER) {StringBuilder buf = new StringBuilder(128);buf.append("Pub ");buf.append(cpi.authority);buf.append(": ");buf.append(cpi.name);Log.i(TAG, buf.toString());}//安装provider,见2.1.5节ContentProviderHolder cph = installProvider(context, null, cpi,false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);if (cph != null) {cph.noReleaseNeeded = true;results.add(cph);}}try {//发布provider见2.2.1节ActivityManager.getService().publishContentProviders(getApplicationThread(), results);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}

2.1.5 AT.installProvider

[->ActivityThread.java]

  private ContentProviderHolder installProvider(Context context,ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {ContentProvider localProvider = null;IContentProvider provider;if (holder == null || holder.provider == null) {if (DEBUG_PROVIDER || noisy) {Slog.d(TAG, "Loading provider " + info.authority + ": "+ info.name);}Context c = null;ApplicationInfo ai = info.applicationInfo;//赋值contextif (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);} catch (PackageManager.NameNotFoundException e) {// Ignore}}//无法获取context则直接返回if (c == null) {Slog.w(TAG, "Unable to get context for package " +ai.packageName +" while loading content provider " +info.name);return null;}if (info.splitName != null) {try {c = c.createContextForSplit(info.splitName);} catch (NameNotFoundException e) {throw new RuntimeException(e);}}try {final java.lang.ClassLoader cl = c.getClassLoader();LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);if (packageInfo == null) {// System startup case.packageInfo = getSystemContext().mPackageInfo;}//通过反射,创建目标ContentProvider对象localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);//获取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.//回调目标 ContentProvider.this.onCreate()方法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 {provider = holder.provider;if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "+ info.name);}//retHolder赋值,用于将contentProvider放入缓存ContentProviderHolder retHolder;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 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).//不再需要则removeif (!noReleaseNeeded) {incProviderRefLocked(prc, stable);try {ActivityManager.getService().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,并回调其onCreate方法,最后对ContentProviderHolder进行赋值并返回。

2.1.6 AMS.publishContentProviders

[->ActivityManagerService.java]

public final void publishContentProviders(IApplicationThread caller,List<ContentProviderHolder> providers) {if (providers == null) {return;}enforceNotIsolatedCaller("publishContentProviders");synchronized (this) {final ProcessRecord r = getRecordForAppLocked(caller);if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);if (r == null) {throw new SecurityException("Unable to find app for caller " + caller+ " (pid=" + Binder.getCallingPid()+ ") when publishing content providers");}final long origId = Binder.clearCallingIdentity();final int N = providers.size();for (int i = 0; i < N; i++) {ContentProviderHolder src = providers.get(i);if (src == null || src.info == null || src.provider == null) {continue;}ContentProviderRecord dst = r.pubProviders.get(src.info.name);if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);if (dst != null) {ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);//将该provider添加到mProviderMapmProviderMap.putProviderByClass(comp, dst);String names[] = dst.info.authority.split(";");for (int j = 0; j < names.length; j++) {mProviderMap.putProviderByName(names[j], dst);}int launchingCount = mLaunchingProviders.size();int j;boolean wasInLaunchingProviders = false;for (j = 0; j < launchingCount; j++) {if (mLaunchingProviders.get(j) == dst) {mLaunchingProviders.remove(j);wasInLaunchingProviders = true;j--;launchingCount--;}}if (wasInLaunchingProviders) {//移除超时消息mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);}synchronized (dst) {dst.provider = src.provider;dst.proc = r;//唤醒客户端的wait等待方法dst.notifyAll();}//更新adjupdateOomAdjLocked(r, true);maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,src.info.authority);}}Binder.restoreCallingIdentity(origId);}}

ContentProviders一旦publish成功,则会移除超时发布的消息,并调用notifyAll来唤醒所有等待client端进程。

2.2 Provider进程启动但未发布

下面在看下Provider进程未发布的情况,。。。。。。。。。。。。。。。。。。。。。。

2.2.1 AT.scheduleInstallProvider

[->ActivityThread.java]

        @Overridepublic void scheduleInstallProvider(ProviderInfo provider) {sendMessage(H.INSTALL_PROVIDER, provider);}public void handleMessage(Message msg) {...case INSTALL_PROVIDER:handleInstallProvider((ProviderInfo) msg.obj);break;...}

2.2.2 AT.handleInstallProvider

[->ActivityThread.java]

 public void handleInstallProvider(ProviderInfo info) {final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();try {installContentProviders(mInitialApplication, Arrays.asList(info));} finally {StrictMode.setThreadPolicy(oldPolicy);}}

这个方法之后就是2.1.4节的流程installContentProviders

三、查询ContentResolver

一般用ContentProvider的时候,会先得到ContentResolver,之后通过uri可以获取相应的信息,下面就从查询方法开始,分析这个过程的源码流程。

ContentResolver contentResolver = getContentResolver();
Uri uri = Uri.parse("content://com.skytoby.articles/android/1");
Cursor cursor = contentResolver.query(uri, null, null, null, null);

3.1 CI.getContentResolver

[->ContextImpl.java]

    @Overridepublic ContentResolver getContentResolver() {return mContentResolver;}private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,@NonNull LoadedApk packageInfo, @Nullable String splitName,@Nullable IBinder activityToken, @Nullable UserHandle user, int flags,@Nullable ClassLoader classLoader) {...mContentResolver = new ApplicationContentResolver(this, mainThread);}

Context中调用getContentResolver,经过层层调用到ContextImpl,返回是在ContextImpl对象创建过程中完成赋值的。下面看下查询的操作。

3.2 CR.query

[->ContentResolver.java]

    public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,@Nullable String[] projection, @Nullable String selection,@Nullable String[] selectionArgs, @Nullable String sortOrder) {return query(uri, projection, selection, selectionArgs, sortOrder, null);}public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,@Nullable String[] projection, @Nullable Bundle queryArgs,@Nullable CancellationSignal cancellationSignal) {Preconditions.checkNotNull(uri, "uri");//获取unstableProvider见3.3节IContentProvider unstableProvider = acquireUnstableProvider(uri);if (unstableProvider == null) {return null;}IContentProvider stableProvider = null;Cursor qCursor = null;try {long startTime = SystemClock.uptimeMillis();ICancellationSignal remoteCancellationSignal = null;if (cancellationSignal != null) {cancellationSignal.throwIfCanceled();remoteCancellationSignal = unstableProvider.createCancellationSignal();cancellationSignal.setRemote(remoteCancellationSignal);}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!!!!1//远程进程死亡,处理unstableProvider死亡过程unstableProviderDied(unstableProvider);//unstable类型死亡后,在创建stable类型的provider,见3.5节stableProvider = acquireProvider(uri);if (stableProvider == null) {return null;}//再次执行查询操作qCursor = stableProvider.query(mPackageName, uri, projection, queryArgs, remoteCancellationSignal);}if (qCursor == null) {return null;}//强制执行操作,可能会失败并抛出RuntimeException// Force query execution.  Might fail and throw a runtime exception here.qCursor.getCount();long durationMillis = SystemClock.uptimeMillis() - startTime;maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);//创建CursorWrapperInner对象// Wrap the cursor object into CursorWrapperInner object.final IContentProvider provider = (stableProvider != null) ? stableProvider: acquireProvider(uri);final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);stableProvider = null;qCursor = null;return wrapper;} catch (RemoteException e) {// Arbitrary and not worth documenting, as Activity// Manager will kill this process shortly anyway.return null;} finally {if (qCursor != null) {qCursor.close();}if (cancellationSignal != null) {cancellationSignal.setRemote(null);}if (unstableProvider != null) {releaseUnstableProvider(unstableProvider);}if (stableProvider != null) {releaseProvider(stableProvider);}}}

一般获取unstable的Provider:调用acquireUnstableProvider,尝试获取unstable的ContentProvider;然后执行query操作

当执行query操作过程中抛出DeadObjectException,即ContentProvider所在的进程死亡,则尝试获取stable的ContentProvider:

1.先调用unstableProviderDied,清理刚创建的unstable的ContentProvider

2.调用acquireProvider,尝试获取stable的ContentProvider,此时当ContentProvider进程死亡,则会杀掉该ContentProvider的客户端进程;

3.执行query操作。

stable和unstable的区别:

采用unstable类型的ContentProvider的应用不会因为远程ContentProvider进程的死亡而被杀。

采用stable类型的ContentProvider的应用会因为远程ContentProvider进程的死亡而被杀。

对于应用无法决定创建的ContentProvider是stable还是unstable的,也无法知道自己的进程是否依赖于远程进程的生死。

3.3 CR.acquireUnstableProvider

[->ContentResolver.java]

 /*** Returns the content provider for the given content URI.** @param uri The URI to a content provider* @return The ContentProvider for the given URI, or null if no content provider is found.* @hide*/public final IContentProvider acquireUnstableProvider(Uri uri) {if (!SCHEME_CONTENT.equals(uri.getScheme())) {return null;}String auth = uri.getAuthority();if (auth != null) {//获取UnstableProvider,见3.4节return acquireUnstableProvider(mContext, uri.getAuthority());}return null;}

3.4 ACR.acquireUnstableProvider

[->ContextImpl.java::ApplicationContentResolver]

private static final class ApplicationContentResolver extends ContentResolver {...private static final class ApplicationContentResolver extends ContentResolver {@UnsupportedAppUsageprivate final ActivityThread mMainThread;...//stable provider@Override@UnsupportedAppUsageprotected IContentProvider acquireProvider(Context context, String auth) {return mMainThread.acquireProvider(context,ContentProvider.getAuthorityWithoutUserId(auth),resolveUserIdFromAuthority(auth), true);}//unstable provider@Overrideprotected IContentProvider acquireUnstableProvider(Context c, String auth) {return mMainThread.acquireProvider(c,ContentProvider.getAuthorityWithoutUserId(auth),resolveUserIdFromAuthority(auth), false);}}
}

从上面可以看出,无论是acquireProvider还是acquireUnstableProvider方法,最后调用的都市ActivityThread的同一个方法的acquireProvider。getAuthorityWithoutUserId是字符截断过程,即去掉auth中的userid信息,比如com.skytoby.article@666,经过该方法则变成了com.skytoby.article。

3.5 AT.acquireProvider

[->ActivityThread.java]

  public final IContentProvider acquireProvider(Context c, String auth, int userId, boolean stable) {//获取已经存在的provider    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.ContentProviderHolder holder = null;try {synchronized (getGetProviderLock(auth, userId)) {//见3.6节holder = ActivityManager.getService().getContentProvider(getApplicationThread(), auth, userId, stable);}} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}if (holder == null) {Slog.e(TAG, "Failed to find provider info for " + auth);return null;}// 安装provider将会增加引用计数,见3.8节// 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;}

主要过程:1.获取已经存在的provider,如果存在则返回,否则继续执行;

​ 2.通过AMS获取provider,无法获取则返回,否则继续执行;

​ 3.通过installProvider方法安装provider,并增加该provider的引用计数。

3.5.1 AT.acquireExistingProvider

[->ActivityThread.java]

    @UnsupportedAppUsagepublic final IContentProvider acquireExistingProvider(Context c, String auth, int userId, boolean stable) {synchronized (mProviderMap) {final ProviderKey key = new ProviderKey(auth, userId);//从mProviderMap中查询是否存在,在2.1.6节发布时将provider添加到了mProviderMapfinal ProviderClientRecord pr = mProviderMap.get(key);if (pr == null) {return null;}IContentProvider provider = pr.mProvider;IBinder jBinder = provider.asBinder();if (!jBinder.isBinderAlive()) {// The hosting process of the provider has died; we can't// use this one.Log.i(TAG, "Acquiring provider " + auth + " for user " + userId+ ": existing object's process dead");//当provider所在的进程已经死亡则返回        handleUnstableProviderDiedLocked(jBinder, true);return null;}// Only increment the ref count if we have one.  If we don't then the// provider is not reference counted and never needs to be released.ProviderRefCount prc = mProviderRefCountMap.get(jBinder);if (prc != null) {//增加引用计数incProviderRefLocked(prc, stable);}return provider;}}

查询mProviderMap是否存在provider,如果不存在则直接返回。如果存在判断provider的进程是否死亡,死亡则返回null。如果provider所在的进程还在,则增加引用计数。

3.6 AMS.getContentProvider

[->ActivityManagerService.java]

 @Overridepublic 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);}

3.7 AMS.getContentProviderImpl

[->ActivityManagerService.java]

  private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,String name, IBinder token, boolean stable, int userId) {ContentProviderRecord cpr;ContentProviderConnection conn = null;ProviderInfo cpi = null;boolean providerRunning = false;synchronized(this) {long startTime = SystemClock.uptimeMillis();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);}}boolean checkCrossUser = true;checkTime(startTime, "getContentProviderImpl: getProviderByName");//从mProviderMap中获取ContentProviderRecord// First check if this content provider has been published...cpr = mProviderMap.getProviderByName(name, userId);// If that didn't work, check if it exists for user 0 and then// verify that it's a singleton provider before using it.if (cpr == null && userId != UserHandle.USER_SYSTEM) {//从USER_SYSTEM获取,这里的name是componentNamecpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);if (cpr != null) {cpi = cpr.info;if (isSingleton(cpi.processName, cpi.applicationInfo,cpi.name, cpi.flags)&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {userId = UserHandle.USER_SYSTEM;checkCrossUser = false;} else {cpr = null;cpi = null;}}}if (cpr != null && cpr.proc != null) {providerRunning = !cpr.proc.killed;// Note if killedByAm is also set, this means the provider process has just been// killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called// yet. So we need to call appDiedLocked() here and let it clean up.// (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see// how to test this case.)//目标provider进程死亡if (cpr.proc.killed && cpr.proc.killedByAm) {checkTime(startTime, "getContentProviderImpl: before appDied (killedByAm)");final long iden = Binder.clearCallingIdentity();try {appDiedLocked(cpr.proc);} finally {Binder.restoreCallingIdentity(iden);}checkTime(startTime, "getContentProviderImpl: after appDied (killedByAm)");}}//目标provider存在的情况,见3.7.1if (providerRunning) {....}//目标provider不存在的情况,见3.7.2if (!providerRunning) {....}//循环等待provider发布完成,见3.7.3synchronized (cpr) {while (cpr.provider == null) {....}}}return super.onTransact(code, data, reply, flags);}

这个方法比较长,主要分为三个部分:

目标provider存在的情况;目标provider不存在的情况;循环等待目标provider发布完成。

3.7.1 目标provider存在

     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;}// Don't expose providers between normal apps and instant appstry {if (AppGlobals.getPackageManager().resolveContentProvider(name, 0 /*flags*/, userId) == null) {return null;}} catch (RemoteException e) {}final long origId = Binder.clearCallingIdentity();checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");// 增加引用计数,见3.8.1节// 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.//更新进程LRU队列checkTime(startTime, "getContentProviderImpl: before updateLruProcess");updateLruProcessLocked(cpr.proc, false, null);checkTime(startTime, "getContentProviderImpl: after updateLruProcess");}}checkTime(startTime, "getContentProviderImpl: before updateOomAdj");final int verifiedAdj = cpr.proc.verifiedAdj;//更新进程adjboolean success = updateOomAdjLocked(cpr.proc, true);// XXX things have changed so updateOomAdjLocked doesn't actually tell us// if the process has been successfully adjusted.  So to reduce races with// it, we will check whether the process still exists.  Note that this doesn't// completely get rid of races with LMK killing the process, but should make// them much smaller.if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {success = false;}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) {//provider进程被杀死,则减少应用计数,见3.8.3节// 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;} else {cpr.proc.verifiedAdj = cpr.proc.setAdj;}Binder.restoreCallingIdentity(origId);}

当ContentProvider所在的进程已经存在:

检查权限;当允许运行在调用者进程且已经发布,则直接返回

增加引用计数;更新进程LRU队列;更新adj

当provider进程被杀时,则奸商引用计数并调用appDiedLocked,设置provider为未发布状态。

3.7.2 目标provider不存在

          if (!providerRunning) {try {checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");//获取ProviderInfo对象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;}//provider是否是单例// 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 providerboolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,cpi.name, cpi.flags)&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid);if (singleton) {userId = UserHandle.USER_SYSTEM;}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&& !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");}// If system providers are not installed yet we aggressively crash to avoid// creating multiple instance of these providers and then bad things happen!if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp()&& "system".equals(cpi.processName)) {throw new IllegalStateException("Cannot access system provider: '"+ cpi.authority + "' before system providers are installed!");}//拥有该provider的用户没有运行,则直接返回// Make sure that the user who owns this provider is running.  If not,// we don't want to allow it to run.if (!mUserController.isUserRunning(userId, 0)) {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();// If permissions need a review before any of the app components can run,// we return no provider and launch a review activity if the calling app// is in the foreground.if (mPermissionReviewRequired) {if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {return null;}}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);} catch (RemoteException ex) {// pm is in same process, this will never happen.} finally {Binder.restoreCallingIdentity(ident);}}checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");//见3.7.4节if (r != null && cpr.canRunHere(r)) {// 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);}if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid "+ (r != null ? r.uid : null) + " pruid " + cpr.appInfo.uid + "): "+ cpr.info.name + " callers=" + Debug.getCallers(6));//从mLaunchingProviders中查询// 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++) {if (mLaunchingProviders.get(i) == cpr) {break;}}//如果mLaunchingProviders中没有该provider,则启动它// 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 {checkTime(startTime, "getContentProviderImpl: before set stopped state");AppGlobals.getPackageManager().setPackageStoppedState(cpr.appInfo.packageName, false, userId);checkTime(startTime, "getContentProviderImpl: after set stopped state");} catch (RemoteException e) {} catch (IllegalArgumentException e) {Slog.w(TAG, "Failed trying to unstop package "+ cpr.appInfo.packageName + ": " + e);}// 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 {//发布provider,见2.2.1节proc.thread.scheduleInstallProvider(cpi);} catch (RemoteException e) {}}} else {checkTime(startTime, "getContentProviderImpl: before start process");//启动进程proc = startProcessLocked(cpi.processName,cpr.appInfo, false, 0, "content provider",new ComponentName(cpi.applicationInfo.packageName,cpi.name), false, false, false);checkTime(startTime, "getContentProviderImpl: after start process");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;//将cpr添加到mLaunchingProvidersmLaunchingProviders.add(cpr);} finally {Binder.restoreCallingIdentity(origId);}}checkTime(startTime, "getContentProviderImpl: updating data structures");// 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;}}checkTime(startTime, "getContentProviderImpl: done!");grantEphemeralAccessLocked(userId, null /*intent*/,cpi.applicationInfo.uid, UserHandle.getAppId(Binder.getCallingUid()));}

当ContentProvider所在的进程不存在:

根据authority获取ProviderInfo对象;权限检查;

当系统不是运行在system进程,且系统未准备好,则抛出异常;

当拥有该provider的用户没有运行,则直接返回;

当允许运行在调用者进程且ProcessRecord不为空,则直接返回;

当provider并没有在mLaunchingProviders队列,则启动它:

  • 当ProcessRecord不为空,则直接加入到pubProviders队列,并安装provider

  • 当ProcessRecord为空,则启动进程

增加引用计数

3.7.3 等待目标provider发布

【ContentProviderRecord

        static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;// Wait for the provider to be published...final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;synchronized (cpr) {//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 {final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());if (DEBUG_MU) Slog.v(TAG_MU,"Waiting to start provider " + cpr+ " launchingApp=" + cpr.launchingApp + " for " + wait + " ms");if (conn != null) {conn.waiting = true;}cpr.wait(wait);if (cpr.provider == null) {Slog.wtf(TAG, "Timeout waiting for provider "+ cpi.applicationInfo.packageName + "/"+ cpi.applicationInfo.uid + " for provider "+ name+ " providerRunning=" + providerRunning);return null;}} catch (InterruptedException ex) {} finally {if (conn != null) {conn.waiting = false;}}}}return cpr != null ? cpr.newHolder(conn) : null;

循环等待20s,直到发布完成才退出。

3.7.4 CPR.canRunHere

[->ContentProviderRecord.java]

  public boolean canRunHere(ProcessRecord app) {return (info.multiprocess || info.processName.equals(app.processName))&& uid == app.info.uid;}

ContentProvider是否能运行在调用者所在的进程需要满足以下条件

1.ContentProvider在AndroidManifest.xml文件配置中multiprocess=true;或者调用者进程和ContentProvider在同一进程

2.ContentProvider进程跟调用者所在的进程是同一个uid。

3.8 AT.installProvider

[->ActivityThread.java]

private ContentProviderHolder installProvider(Context context,ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {ContentProvider localProvider = null;IContentProvider provider;if (holder == null || holder.provider == null) {//从packageInfo中获取providerif (DEBUG_PROVIDER || noisy) {Slog.d(TAG, "Loading provider " + info.authority + ": "+ info.name);}Context c = null;ApplicationInfo ai = info.applicationInfo;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);} 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;}if (info.splitName != null) {try {c = c.createContextForSplit(info.splitName);} catch (NameNotFoundException e) {throw new RuntimeException(e);}}try {final java.lang.ClassLoader cl = c.getClassLoader();LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);if (packageInfo == null) {// System startup case.packageInfo = getSystemContext().mPackageInfo;}localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);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 {provider = holder.provider;if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "+ info.name);}ContentProviderHolder retHolder;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 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 {//查询ProviderRefCountProviderRefCount 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) {//增加引用计数,通过AMS,见3.8.4incProviderRefLocked(prc, stable);try {ActivityManager.getService().removeContentProvider(holder.connection, stable);} catch (RemoteException e) {//do nothing content provider object is dead any way}}} else {//见3.8.5节ProviderClientRecord client = installProviderAuthoritiesLocked(provider, localProvider, holder);if (noReleaseNeeded) {//见3.8.6节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;}

3.8.1 AMS.incProviderCountLocked

[->ActivityManagerService.java]

 ContentProviderConnection incProviderCountLocked(ProcessRecord r,final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {if (r != null) {for (int i=0; i<r.conProviders.size(); i++) {//从当前进程所使用的provider中查询与目标provider一致的信息ContentProviderConnection conn = r.conProviders.get(i);if (conn.provider == cpr) {if (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER,"Adding provider requested by "+ r.processName + " from process "+ cpr.info.processName + ": " + cpr.name.flattenToShortString()+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);if (stable) {conn.stableCount++;conn.numStableIncs++;} else {conn.unstableCount++;conn.numUnstableIncs++;}return conn;}}//查不到则新建provider连接对象ContentProviderConnection conn = new ContentProviderConnection(cpr, r);if (stable) {conn.stableCount = 1;conn.numStableIncs = 1;} else {conn.unstableCount = 1;conn.numUnstableIncs = 1;}cpr.connections.add(conn);r.conProviders.add(conn);startAssociationLocked(r.uid, r.processName, r.curProcState,cpr.uid, cpr.name, cpr.info.processName);return conn;}cpr.addExternalProcessHandleLocked(externalProcessToken);return null;}

3.8.2 AMS.removeContentProvider

[->ActivityManagerService.java]

 /*** Drop a content provider from a ProcessRecord's bookkeeping*/public void removeContentProvider(IBinder connection, boolean stable) {enforceNotIsolatedCaller("removeContentProvider");long ident = Binder.clearCallingIdentity();try {synchronized (this) {ContentProviderConnection conn;try {conn = (ContentProviderConnection)connection;} catch (ClassCastException e) {String msg ="removeContentProvider: " + connection+ " not a ContentProviderConnection";Slog.w(TAG, msg);throw new IllegalArgumentException(msg);}if (conn == null) {throw new NullPointerException("connection is null");}//见3.8.3节if (decProviderCountLocked(conn, null, null, stable)) {updateOomAdjLocked();}}} finally {Binder.restoreCallingIdentity(ident);}}

3.8.3 AMS.decProviderCountLocked

[->ActivityManagerService.java]

boolean decProviderCountLocked(ContentProviderConnection conn,ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {if (conn != null) {cpr = conn.provider;if (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER,"Removing provider requested by "+ conn.client.processName + " from process "+ cpr.info.processName + ": " + cpr.name.flattenToShortString()+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);if (stable) {conn.stableCount--;} else {conn.unstableCount--;}//当provider连接的stable和unstable的引用次数为01时,则移除连接对象信息if (conn.stableCount == 0 && conn.unstableCount == 0) {cpr.connections.remove(conn);conn.client.conProviders.remove(conn);if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {// The client is more important than last activity -- note the time this// is happening, so we keep the old provider process around a bit as last// activity to avoid thrashing it.if (cpr.proc != null) {cpr.proc.lastProviderTime = SystemClock.uptimeMillis();}}stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name);return true;}return false;}cpr.removeExternalProcessHandleLocked(externalProcessToken);return false;}

3.8.4 AT.incProviderRefLocked

[->ActivityThread.java]

 private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) {//stable providerif (stable) {prc.stableCount += 1;if (prc.stableCount == 1) {//数量为1,状态为removePending,发送REMOVE_PROVIDER信息// We are acquiring a new stable reference on the provider.int unstableDelta;if (prc.removePending) {// We have a pending remove operation, which is holding the// last unstable reference.  At this point we are converting// that unstable reference to our new stable reference.unstableDelta = -1;// Cancel the removal of the provider.if (DEBUG_PROVIDER) {Slog.v(TAG, "incProviderRef: stable "+ "snatched provider from the jaws of death");}prc.removePending = false;// There is a race! It fails to remove the message, which// will be handled in completeRemoveProvider().mH.removeMessages(H.REMOVE_PROVIDER, prc);} else {unstableDelta = 0;}try {if (DEBUG_PROVIDER) {Slog.v(TAG, "incProviderRef Now stable - "+ prc.holder.info.name + ": unstableDelta="+ unstableDelta);}//调用AMS方法ActivityManager.getService().refContentProvider(prc.holder.connection, 1, unstableDelta);} catch (RemoteException e) {//do nothing content provider object is dead any way}}} else {prc.unstableCount += 1;if (prc.unstableCount == 1) {// We are acquiring a new unstable reference on the provider.if (prc.removePending) {// Oh look, we actually have a remove pending for the// provider, which is still holding the last unstable// reference.  We just need to cancel that to take new// ownership of the reference.if (DEBUG_PROVIDER) {Slog.v(TAG, "incProviderRef: unstable "+ "snatched provider from the jaws of death");}prc.removePending = false;mH.removeMessages(H.REMOVE_PROVIDER, prc);} else {// First unstable ref, increment our count in the// activity manager.try {if (DEBUG_PROVIDER) {Slog.v(TAG, "incProviderRef: Now unstable - "+ prc.holder.info.name);}ActivityManager.getService().refContentProvider(prc.holder.connection, 0, 1);} catch (RemoteException e) {//do nothing content provider object is dead any way}}}}}

3.8.5 AT.installProviderAuthoritiesLocked

[->ActivityThread.java]

 private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,ContentProvider localProvider, ContentProviderHolder holder) {final String auths[] = holder.info.authority.split(";");final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);if (provider != null) {// If this provider is hosted by the core OS and cannot be upgraded,// then I guess we're okay doing blocking calls to it.for (String auth : auths) {switch (auth) {case ContactsContract.AUTHORITY:case CallLog.AUTHORITY:case CallLog.SHADOW_AUTHORITY:case BlockedNumberContract.AUTHORITY:case CalendarContract.AUTHORITY:case Downloads.Impl.AUTHORITY:case "telephony":Binder.allowBlocking(provider.asBinder());}}}final ProviderClientRecord pcr = new ProviderClientRecord(auths, provider, localProvider, holder);for (String auth : auths) {final ProviderKey key = new ProviderKey(auth, userId);final ProviderClientRecord existing = mProviderMap.get(key);if (existing != null) {Slog.w(TAG, "Content provider " + pcr.mHolder.info.name+ " already published as " + auth);} else {//发布mProviderMap.put(key, pcr);}}return pcr;}

3.8.6 ProviderRefCount

[->ActivityThread.java::ProviderRefCount]

  private static final class ProviderRefCount {public final ContentProviderHolder holder;public final ProviderClientRecord client;public int stableCount;public int unstableCount;//当该标记设置时,stableCount和unstableCount引用都会设置为0// When this is set, the stable and unstable ref counts are 0 and// we have a pending operation scheduled to remove the ref count// from the activity manager.  On the activity manager we are still// holding an unstable ref, though it is not reflected in the counts// here.public boolean removePending;ProviderRefCount(ContentProviderHolder inHolder,ProviderClientRecord inClient, int sCount, int uCount) {holder = inHolder;client = inClient;stableCount = sCount;unstableCount = uCount;}}

获取到Provider后,就可以进行查询操作了。

3.9 CPP.query

[->ContentProviderNative.java::ContentProviderProxy]

  @Overridepublic Cursor query(String callingPkg, Uri url, @Nullable String[] projection,@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)throws RemoteException {BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();try {data.writeInterfaceToken(IContentProvider.descriptor);data.writeString(callingPkg);url.writeToParcel(data, 0);int length = 0;if (projection != null) {length = projection.length;}data.writeInt(length);for (int i = 0; i < length; i++) {data.writeString(projection[i]);}data.writeBundle(queryArgs);data.writeStrongBinder(adaptor.getObserver().asBinder());data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);//发送给binder服务mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);DatabaseUtils.readExceptionFromParcel(reply);if (reply.readInt() != 0) {BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);Binder.copyAllowBlocking(mRemote, (d.cursor != null) ? d.cursor.asBinder() : null);adaptor.initialize(d);} else {adaptor.close();adaptor = null;}return adaptor;} catch (RemoteException ex) {adaptor.close();throw ex;} catch (RuntimeException ex) {adaptor.close();throw ex;} finally {data.recycle();reply.recycle();}}

3.10 CPN.onTransact

[->ContentProviderNative.java]

   @Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws RemoteException {try {switch (code) {case QUERY_TRANSACTION:{data.enforceInterface(IContentProvider.descriptor);String callingPkg = data.readString();Uri url = Uri.CREATOR.createFromParcel(data);// String[] projectionint num = data.readInt();String[] projection = null;if (num > 0) {projection = new String[num];for (int i = 0; i < num; i++) {projection[i] = data.readString();}}Bundle queryArgs = data.readBundle();IContentObserver observer = IContentObserver.Stub.asInterface(data.readStrongBinder());ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(data.readStrongBinder());//见3.11节      Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal);if (cursor != null) {CursorToBulkCursorAdaptor adaptor = null;try {adaptor = new CursorToBulkCursorAdaptor(cursor, observer,getProviderName());cursor = null;BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();adaptor = null;reply.writeNoException();reply.writeInt(1);d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);} finally {// Close cursor if an exception was thrown while constructing the adaptor.if (adaptor != null) {adaptor.close();}if (cursor != null) {cursor.close();}}} else {reply.writeNoException();reply.writeInt(0);}return true;}case GET_TYPE_TRANSACTION:case INSERT_TRANSACTION:case BULK_INSERT_TRANSACTION: case APPLY_BATCH_TRANSACTION:case DELETE_TRANSACTION:case UPDATE_TRANSACTION:...}} catch (Exception e) {DatabaseUtils.writeExceptionToParcel(reply, e);return true;}

3.11 Transport.query

[->ContentProvider.java::Transport]

  public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {uri = validateIncomingUri(uri);uri = maybeGetUriWithoutUserId(uri);if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {// The caller has no access to the data, so return an empty cursor with// the columns in the requested order. The caller may ask for an invalid// column and we would not catch that but this is not a problem in practice.// We do not call ContentProvider#query with a modified where clause since// the implementation is not guaranteed to be backed by a SQL database, hence// it may not handle properly the tautology where clause we would have created.if (projection != null) {return new MatrixCursor(projection, 0);}// Null projection means all columns but we have no idea which they are.// However, the caller may be expecting to access them my index. Hence,// we have to execute the query as if allowed to get a cursor with the// columns. We then use the column names to return an empty cursor.Cursor cursor = ContentProvider.this.query(uri, projection, queryArgs,CancellationSignal.fromTransport(cancellationSignal));if (cursor == null) {return null;}// Return an empty cursor for all columns.return new MatrixCursor(cursor.getColumnNames(), 0);}final String original = setCallingPackage(callingPkg);try {//调用目标provider的query方法return ContentProvider.this.query(uri, projection, queryArgs,CancellationSignal.fromTransport(cancellationSignal));} finally {setCallingPackage(original);}}

四、总结

本文通过分析了发布ContentProvider的过程,并分析query过程,先获取provider在安装provider信息,最后才查询。而查询操作主要分为两种情况:

4.1 进程未启动

provider进程不存在,需要创建进程并发布相关的provider

1.client进程:通过Binder向systemserver进程请求相应的provider

2.systemserver进程:如果目标进程未启动,则调用startProcessLocked启动进程,当启动完成,当cpr.provider ==null,则systemserver进入wait阶段,等待目标provider发布;

3.provider进程:进程启动后执行attach到systemserver,而后bindApplication,在这个过程会installProvider和PublishContentProviders,再binder到systemserver进程;

4.systemserver进程:回到systemserver发布provider信息,并且通过notify机制唤醒当前处于wait状态的binder线程,并将结果返回给client进程;

5.client进程:回到client进程,执行installProvider操作,安装provider

关于CONTENT_PROVIDER_PUBLISH_TIMEOUT超时机制所统计的时机区间是指在startProcessLocked之后会调用AMS.attachApplicationLocked为起点,一直到AMS.publishContentProviders的过程。

4.2 进程已启动

provider进程已启动但未发布,需要发布相关的provider


Client进程:获取provider发现cpr为空,则调用scheduleInstallProvider来向provider所在的进程发出一个oneway的binder请求,进入wait状态;

provider进程:安装完成provider信息后,通过notify机制唤醒当前处于wait状态的binder线程。

如果provider在publish之后,这是在请求provider则没用最右边的过程,直接AMS.getContentProvierImpl之后便进入AT.installProvider的过程,而不会再进入wait过程。

4.3 引用计数

provider分为stable provider和unstable provider,主要在于引用计数的不同。

provider引用计数的增加与减少关系,removePending是指即将被移除的引用,lastRef表示当前引用为0。

方法 stableCount unstableCount 条件
acquireProvider +1 0 removePending=false
acquireProvider +1 -1 removePending=true
acquireUnstableProvider 0 +1 removePending=false
acquireUnstableProvider 0 0 removePending=true
releaseProvider -1 0 lastRef=false
releaseProvider -1 1 lastRef=true
releaseUnstableProvider 0 -1 lastRef=false
releaseUnstableProvider 0 0 lastRef=true

当Client进程存在对某个provider的引用时,Provider进程死亡则会根据provider类型进行不同的处理:

  • 对于stable provider,会杀掉所有和该provider建立stable连接的非persistent进程;

  • 对于unstable provider,不会导致client进程被级联所杀,会回调unstableProviderDied来清理相关信息。

当stable和unstable引用计数都为0则移除connection信息

  • AMS.removeContentProvider过程移除connection相关所有信息。

附录

源码路径

frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks/base/core/java/android/content/ContentResolver.java
frameworks/base/core/java/android/content/ContentProviderNative.java
frameworks/base/services/core/java/com/android/server/am/ContentProviderRecord.java

Android10.0 ContentProvider原理分析相关推荐

  1. Spring3.1.0实现原理分析(七).填充Bean属性,初始化Bean,登记善后处理,注册单例Bean

    大家好,上篇博客我较详细分析了实例化过程,今天继续探讨实例化之后的其它步骤,分别是"填充Bean属性","初始化Bean","登记善后处理" ...

  2. Spring3.1.0实现原理分析(七).填充Bean属性,初始化Bean,登记善后处理,注册单例Bean...

    大家好,上篇博客我较详细分析了实例化过程,今天继续探讨实例化之后的其它步骤,分别是"填充Bean属性","初始化Bean","登记善后处理" ...

  3. Android 6.0 JNI原理分析 和 Linux系统调用(syscall)原理

    JNI原理 引言:分析Android源码6.0的过程,一定离不开Java与C/C++代码直接的来回跳转,那么就很有必要掌握JNI,这是链接Java层和Native层的桥梁,本文涉及相关源码: fram ...

  4. android contentprovider原理,ContentProvider原理分析

    前景知识:对于ContentProvider理解必须对IBinder有所了解(一个提供跨进程信息交换的一个工具),会ContentProvider的简单使用.本文是基于你已经具备以上知识点阅读 1.名 ...

  5. Android10.0 四大组件与进程启动间关系

    原文地址:https://skytoby.github.io/2019/Android%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6%E4%B8%8E%E8%BF%9B%E7 ...

  6. 高通Quick Charge快速充电原理分析

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zoosenpin/article/details/29799709 1 QC2.0 1.1 高通Qu ...

  7. Android10.0 Binder通信原理(八)-Framework层分析

    摘要:本节主要来讲解Android10.0 Binder 在Framework的使用分析 阅读本文大约需要花费15分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,An ...

  8. Android10.0 Binder通信原理(五)-Binder驱动分析

    摘要:本节主要来讲解Android10.0 Binder的驱动层分析 阅读本文大约需要花费35分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计 ...

  9. Android10.0 Binder通信原理(四)-Native-C\C++实例分析

    摘要:本节主要来讲解Android10.0 Binder的Native层实例流程 阅读本文大约需要花费35分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Androi ...

最新文章

  1. SQL Server 2016新特性:列存储索引新特性
  2. python学习 day19
  3. 摆脱“人肉”审核,从0搭建可视化SQL自动审核平台
  4. CF1101D GCD Counting
  5. ribbon和feign的区别
  6. MVC5 - ASP.NET Identity登录原理 - Claims-based认证和OWIN
  7. 自动驾驶路径规划论文解析(6)
  8. 自动驾驶车辆转向控制(通过支持转角控制的EPS实现角速度控制)
  9. Avro 数据格式和命令行
  10. RH413--在RHEL6.4下测试nosuid和noexec选项
  11. ubuntu 10.04 源
  12. 4. 正则表达式(4)
  13. 一文了解什么是DoIP协议(超详细)
  14. stc12c5a60s2c语言程序,STC12C5A60S2单片机 花样流水灯(小鸟归巢)C程序
  15. POJ 3422 - Kaka's Matrix Travels(最小费用流)
  16. python爬虫实战---网易云音乐评论抓取
  17. 用sync toy做增量备份
  18. 适合零基础编程学员学习的网站,APP
  19. 技巧分享-如何给电脑“重装”系统(win10)
  20. SpringDataRedis使用

热门文章

  1. sqlserver 无法远程连接到服务器,SQLServer2019无法连接远程服务器
  2. 我的Java设计模式-工厂方法模式
  3. Javascript 控制 Flash FLV视频播放器 --国外开源
  4. Windows Server 2016 + Exchange 2016 +Office365混合部署(四)
  5. Nginx+Tomcat动静分离及Nginx优化(企业案例)
  6. 错误:无法作为数据库主体执行,因为主体 dbo 不存在、无法模拟这种类型的主体,或您没有所需的权限...
  7. 在可编辑div中插入文字或图片的问题解决思路
  8. RouterOS和艾泰路由建立ipsec ×××连接
  9. Spark RDD-行动算子
  10. JavaScript (二)