ContentProvider启动浅析
一、自己的理解
对于content provide的启动我是这样认为的,要用ContentResolver去获得一个contentProvider,在这的获得的过程中,
1、如果本应用之前有contentProvider的引用,则直接返回。
2、如果没用,则向AMS(ActivityManagerService)去申请,然后AMS返回一个ContentProvideHolder对象,这时又分两种情况:
2.1如果此contentprovider需要在本应用进程中创建,则返回一个Holder对象,然后在本进程中创建一个contentprovider,并保存一系列引用。
2.2 如果此contentprovider在其他进程中,这时又要分情况:
2.2.1如果此contentprovider已经存在,并且可以在运行在本应用进程中,则直接返回;如果不可以,则需要设置一个ContentProviderConnection对象 conn,然后将contentProvider添加到conn中,即为contentProvider添加了一个异地引用。
2.2.2如果此contentProvider不存在,则需要去创建一个,如果这个contentProvider所在的进程已经被创建,则直接在此进程中加载,如果没有被创建,则先启动这个应用进程,然后加载。在这个加载过程中会有一个while循环,直到contentprovider被加载完毕。
最后,会返回一个带有contentProvider的Holder对象。
ps:其实返回的ContentProvider对象并不真正是一个ContentProvider对象,因为这是两个进程之间的通信,他们不能直接获得彼此进程中的一些引用,而客户进程得到的ContentProvider对象引用,本质上是一个Binder对象。因为Android中只有BInder才能在两个进程之间通信。在这里,客户端得到的其实是Transport对象,通过它来操作服务端的ContentProvider
二、源码浅析
在分析源码前,先说明几个变量:
1、AMS的mProviderMap,它保存了整个系统中所有的ContentProviderRecord对象,而这个记录里面包含了contentProvider,mProviderMap有两种获得ContentProviderRecord的方法,一是通过类名获得,另一个是通过contentProvider的授权URI获得。
2、ProcessRecord的pubProviders和conProviders,他们是被存放在每个应用各自的进程中,以键值对的方式记录着ContentProviderRecord,pubProviders保存着进程中所有创建的ContentProvider,conProviders保存着进程中所有使用的ContentProvider。
3、ContentProviderHolder中保存了ContentProvider的相关信息,它含有ProviderInfo(包含了contentProvider的授权URI,读写权限等信息),ContentProvider的引用,IBInder(这其实是一个服务端的ContentProviderConnection对象,其继承了BInder,用来作为客户端与服务端的链接)
下面我们来看一下源码中使怎么写的:
1、ContentResolver的query
在query()中,会首先获得contentProvider对象,然后用此对象去进行查询。要获得contentProvider对象会调用acquireUnstableProvider(uri)或acquireProvider(uri)。其实这两个方法的最终调用都是一样的,现在,我们选择acquireUnstableProvider(uri)来分析。
2、ContentResolver的acquireUnstableProvider()
要分析此方法,首先要知道resolver对象是如何获得的。resolver对象是通过context对象的getContentResolver()获得的。其实是ContextImpl实现了Context,然后由ContextWrapper对其进行了修饰,即有ContextImpl对象的一个引用——mBase,然后Service,Activity又继承了ContextWrapper ,由此就可以使用上下文的资源。在ContextImpl中,有一个ApplicationContentResolver类继承了ContentResolver,并在ContextImplement初始化时,对其进行实例化,保存为mContentResolver引用。在getContentResolver()中会使用mBase的getContentResolver(),得到的就是这个mContentResolver,然后会在它的acquireUnstableProvider()去调用mMainThread(ActivityThread)的acquireProvider().
3、ActivityThread的acquireProvider()方法
public final IContentProvider acquireProvider( Context c, String auth, int userId, boolean stable) { //查看是否有已经存在的ContentProvider,若存在,则将其返回 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 { //向AMS请求一个含有ContentProvider的Holder holder = ActivityManagerNative.getDefault().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; } // Install provider will increment the reference count for us, and break // any ties in the race. //如注释所说,对ContentProvider添加引用 holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider; }
关于此方法的作用,已在注释中写明,从这个方法中我们的值ContentProvider主要是从两个方面去获取的,一,从本地存在的COntentProvider集合中获取,即第4步;二,向AMS索取,即5,6,7步
4、ActivityThread的acquireExistingProvider()
public final IContentProvider acquireExistingProvider( Context c, String auth, int userId, boolean stable) { synchronized (mProviderMap) { final ProviderKey key = new ProviderKey(auth, userId); //查看本应用中是否存有要求的ContentProvider,没有则返回null final ProviderClientRecord pr = mProviderMap.get(key); if (pr == null) { return null; } //查看provider是否可用,不可用返回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"); handleUnstableProviderDiedLocked(jBinder, true); return null; } //至此,说明provider是可用的,然后对contentProvider的引用数量进行改变 // 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; } }
关于incProviderRefLocked(),当请求一个新的ContentProvider时会调用此方法,它会将ContentProvider的引用计数加1,即将ContentProviderConnection对象进行更新,告诉它有某个应用需要使用新的ContentProvider,令其更新他的计数器。
5、AMS的getContentProvider()
调用此方法说明,在本应用没有已经存在的ContentProvider,需要向AMS申请一个。在此方法中会进行参数检查,最终去调用getContentProviderImpl()
5.1、AMS的getContentProviderImpl()
这个方法较长我们将它分为几个部分:
5.1.1若ContentProvider已经存在,则返回引用
5.1.2若其不存在,但是提供它的进程存在,则令此进程加载ContentProvider,然后返回新加载的对象
5.1.3若其不存,且提供它的进程也不存在,则开启此进程,并加载ContentProvider ,而后返回新加载的对象
好的下面来分析5.1.1:
先声明一些变量来保存数据
ContentProviderRecord cpr; ContentProviderConnection conn = null; ProviderInfo cpi = null;
接下来是一个同步代码块,再此代码块中获得ContentProvider,并防止多个进程进行争抢,导致出错
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); } }
获得客户端的进程,用来判断获得ContentProvider后,能否直接在客户端进程运行。
// 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) { cpr = 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; } } }
在此代码块中,首先查看,授权URI对应的进程中是否有ContentProvider,若没有,则去系统进程中查找,找到则证明此ContentProvider是单实例的,即系统中只有一个。
boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
这一句是判断是否有存在且存活的ContentProvider,若有则为true,否则为false
if (providerRunning) { cpi = cpr.info; ...... //查看是否可以在请求contentProvider的进程中运行,如果可以或者此contentProvider就是请求,提供的 //则直接返回 if (r != null && cpr.canRunHere(r)) { ContentProviderHolder holder = cpr.newHolder(null); holder.provider = null; return holder; } ...... // In this case the provider instance already exists, so we can // return it right away. //为contentProvider增加引用数 conn = incProviderCountLocked(r, cpr, token, stable); if (conn != null && (conn.stableCount+conn.unstableCount) == 1) { if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { // 更新提供contentProvider进程的位置 ...... updateLruProcessLocked(cpr.proc, false, null); } } //下面检查提供contentprovider的进程是否存活,若已死亡,等待新的进程启动 //它是通过oom_adj的值来检查的 final int verifiedAdj = cpr.proc.verifiedAdj; boolean success = updateOomAdjLocked(cpr.proc); if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) { success = false; } ...... if (!success) { // 进程已为空,等待新进程的启动 appDiedLocked(cpr.proc); ...... providerRunning = false; conn = null; } else { cpr.proc.verifiedAdj = cpr.proc.setAdj; } Binder.restoreCallingIdentity(origId); }
上面的代码是当有contentProvider时,该如何做。应在注释中写明
这两个方法的作用详见:
下面我们来分析contentProvider不存在的情况,这里我们把5.1.2和5.1.3合并到一起分析
if (!providerRunning) {
try{
//在此获得provider的信息
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; } // 参数检查
......
ComponentName comp = new ComponentName(cpi.packageName, cpi.name); ......
//以类名的来获得provider cpr = mProviderMap.getProviderByClass(comp, userId); ......final boolean firstClass = cpr == null; if (firstClass) { ......try { checkTime(startTime, "getContentProviderImpl: before getApplicationInfo"); ApplicationInfo ai = AppGlobals.getPackageManager(). getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId); ...... ai = getAppInfoForUser(ai, userId);
//因为我们需要provider,但是provider记录不存在,所以在此创建一个ContentProviderRecord来保存要获得provider cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton); } catch (RemoteException ex) { // pm is in same process, this will never happen. } finally { Binder.restoreCallingIdentity(ident); } } ......
//到这里我们已经获得了contentProviderRecord对象cpr,不管它含不含有provider
if (r != null && cpr.canRunHere(r)) {
//条件成立,表示provider可以在请求者进程运行,或在请求者进程创建,则可以直接返回一个Holder 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)); //查看是否有正在启动的provider,若有等待启动完成 final int N = mLaunchingProviders.size(); int i; for (i = 0; i < N; i++) { if (mLaunchingProviders.get(i) == cpr) { break; } } // 没有正在启动的provider,去启动它 if (i >= N) { final long origId = Binder.clearCallingIdentity(); try { // 加载provider所在的包 try { ...... 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); } // 获得provider所在的进程,因为我们要在此进程中启动provider 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 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); ...... } cpr.launchingApp = proc; mLaunchingProviders.add(cpr); } finally { Binder.restoreCallingIdentity(origId); } } //此处保存这个新provider的一些引用信息 if (firstClass) { mProviderMap.putProviderByClass(comp, cpr); } mProviderMap.putProviderByName(name, cpr); conn = incProviderCountLocked(r, cpr, token, stable); if (conn != null) { conn.waiting = true; } }
完成上面的代码,表示provider已经存在,或正在启动,下面的代码用来检查
synchronized (cpr) { while (cpr.provider == null) { ......try { ...... cpr.wait(); } catch (InterruptedException ex) { } finally { if (conn != null) { conn.waiting = false; } } } } return cpr != null ? cpr.newHolder(conn) : null;
新的provider成功获得后,把它放在一个Holder中返回
下面我们来说一下,上面的两个进程中启动provider的方法startProcessLocked()和scheduleInstallProvider(),他们最终都会调用installProvider()。现在,我们以scheduleInstallProvider()来分析。首先,会调用ApplicationThread的scheduleInstallProvider(),而在这个方法中会给消息队列发送一个消息,然后,会转到ActivityThread中的Handler对象中去处理。继而调用handleInstallProvider().
ActivityThread的handleInstallProvider():
public void handleInstallProvider(ProviderInfo info) { final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); try { installContentProviders(mInitialApplication, Lists.newArrayList(info)); } finally { StrictMode.setThreadPolicy(oldPolicy); } }
此方法会转而去调用installContentProviders()
ActivityThread的installContentProviders()
private void installContentProviders( Context context, List<ProviderInfo> providers) { final ArrayList<IActivityManager.ContentProviderHolder> results = new ArrayList<IActivityManager.ContentProviderHolder>(); 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()); } IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); if (cph != null) { cph.noReleaseNeeded = true; results.add(cph); } } try { ActivityManagerNative.getDefault().publishContentProviders( getApplicationThread(), results); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } }
在这个方法中,首先会获得要加载的每一个provider的信息,因为我们这里只传入了一个,所以providers的长度为1.然后调用installProvider()去启动provider,启动完成之后会去告诉AMS,此provider已经启动完毕,让AMS去更新一些信息。installProvider()这个方法我们在前面请求者请求provider时见过,所以在这我们结合着两种不同的场景来分这方法的实现
ActivityThread的installProviders()
private IActivityManager.ContentProviderHolder installProvider(Context context, I
ActivityManager.ContentProviderHolder holder, ProviderInfo inf
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) { ......
//在此首先要获得与要加载provider相关的Context,因为provider是null,所以我们在后面要去实例化一个provider,这是当前情况下的处理做法
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 } } ......try { final java.lang.ClassLoader cl = c.getClassLoader(); localProvider = (ContentProvider)cl. loadClass(info.name).newInstance();
//实例化一个provider,并获得可以在进程间通信的Transport对象 provider = localProvider.getIContentProvider(); ......
// 为新创建的provider配置一些信息,如读写权限之类的 localProvider.attachInfo(c, info); } catch (java.lang.Exception e) { ...... } } else {
//这是在请求者请求时的处理,传进来的是一个在其他进程已经启动好了的provider provider = holder.provider; ...... } IActivityManager.ContentProviderHolder retHolder; synchronized (mProviderMap) { ...... IBinder jBinder = provider.asBinder(); if (localProvider != null) {
//条件成立,表示此provider是新建的,需要保存一些引用 ComponentName cname = new ComponentName(info.packageName, info.name); ProviderClientRecord pr = mLocalProvidersByName.get(cname); if (pr != null) { //为null表示,在多个进程同时请求时,竞争失败,已经有其他进程先获得了provider,在此不需要在此保存 provider = pr.mProvider; } else {
//需要将provider保存在holder中 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 {
//表示此provider是用其他进程传入的,要在此保存provider的引用数量,当Pro不为null时,是第一次传入,可以根据要求判断是否进行更新
//pro为null,则需要创建一个provider远程引用数,并进行保存
ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null) { ...... 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; }
然后,我们可以通知AMS去发布provider了
AMS的publishContentProviders()
public final void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) { .....synchronized (this) { final ProcessRecord r = getRecordForAppLocked(caller); ......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; }
//dst为之前在getContentProviderImpl中创建的provider记录 ContentProviderRecord dst = r.pubProviders.get(src.info.name); if (dst != null) { ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name); mProviderMap.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--; } } //在此表示,请求的provider已经被启动,并向record中添加provider,然后打断前面的while循环。synchronized (dst) { dst.provider = src.provider; dst.proc = r; dst.notifyAll(); } updateOomAdjLocked(r); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); } }
传入的providers 是已经加载好的provider,会与加载的provider进行比较,若相同则说明已经启动,并将其从待启动队列中移除。并向provider记录中添加provider,这样前面的while循环就可以被打断,从而AMS就可以将provider返回,给请求者。
至此contentProvider的启动就已经分析结束。