Android之ContentProvider的启动过程源码分析
2016-07-26 19:52
477 查看
ContentProvider组件一次传递的数据量可能非常大,如果直接使用Binder进程间通信机制来传递数据的话,传输效率是个问题。不同的应用程序进程可以通过匿名共享内存来传输大数据,因为无论多大的数据,对匿名共享内存来说,需要在进程间传递的仅仅是一个文件描述符而已。这样,结合Binder进程间通信机制和匿名共享内存机制,ContentProvider组件就可以实现高效的文件传输了。
ContentProvider中query方法返回的保存在cursor对象中的数据是通过匿名共享内存来传输的,call方法返回的数据是保存在Bundle对象中的,它直接使用Binder进程间通信机制来传输的。当传输的数据量比较小时,使用匿名共享内存就得不偿失了,因为它的创建过程和映射过程都是需要开销的。
Provider的启动过程大致分为几个步骤:
1.调用者通过URI访问目标Provider。
2.调用者所运行在的应用程序进程发现不存在要访问的目标Provider的代理对象,就会通过URI来请求ActivityManagerService返回一个用来访问目标Provider组件的代理对象。
3.AMS发现这个Provider组件还没有启动起来,就会先创建一个新的应用程序进程,然后在这个进程中启动目标Provider组件。
4.目标Provider组件启动起来后,就会将自己发布到AMS中,以便AMS可以将它的一个代理对象返回给调用者使用。
使用Provider时需要先调用getContentProvider()方法来获取ContentResolver对象,惯例先看时序图:
先看ContextWrapper类中的getContentResolver方法:
调用到ContextImpl类中的getContentResolver方法:
上面ApplicationContentResolver是ContextImpl的内部类。到这里调用者就获取到了ContentResolver对象。
获取到对象后,假设调用者要访问目标Provider的insert方法(其他方法的流程基本一致),先看下时序图:
先看ContentResolver类的insert方法:
调用到这里,又回到了ContextImpl的内部类ApplicationContentResolver中的acquireProvider方法,该方法中又调用了ActivityThread类中的acquireProvider方法,看该方法之前,先看下传递的参数:
上面两个方法主要是截取不规则的authority和获取userId的。再接着看下后续的时序图:
根据上面时序图,先看下ActivityThread类中的acquireProvider方法:
接着看下ActivityManagerService类中的getContentProvider方法:
下面看ActivityThread类中的scheduleInstallProvider方法:
接下来先分析installProvider方法在当前应用程序进程中启动一个Provider组件的过程,然后再分析AMS的publishContentProviders方法将启动完成之后的Provider组件发布到AMS中的过程:
每一个Provider组件内部都有一个类型为Transport的Binder本地对象,这个Binder本地对象是用来传递给AMS的,然后AMS再将引用了它的一个Binder代理对象返回给需要访问该Provider组件的其他应用程序进程。这样,其他应用程序进程就可以通过这个Binder代理对象来间接地访问一个Provider组件中的数据了。
下面看ContentProvider类中的getIContentProvider方法和attachInfo方法:
最后看AMS中的publishContentProviders方法:
到这里Provider已经启动完成了,并且将它的一个代理对象即一个类型为Transport的Binder代理对象发布到AMS中了。
内容有理解错误或偏差之处,请包涵指正,谢谢!
ContentProvider中query方法返回的保存在cursor对象中的数据是通过匿名共享内存来传输的,call方法返回的数据是保存在Bundle对象中的,它直接使用Binder进程间通信机制来传输的。当传输的数据量比较小时,使用匿名共享内存就得不偿失了,因为它的创建过程和映射过程都是需要开销的。
Provider的启动过程大致分为几个步骤:
1.调用者通过URI访问目标Provider。
2.调用者所运行在的应用程序进程发现不存在要访问的目标Provider的代理对象,就会通过URI来请求ActivityManagerService返回一个用来访问目标Provider组件的代理对象。
3.AMS发现这个Provider组件还没有启动起来,就会先创建一个新的应用程序进程,然后在这个进程中启动目标Provider组件。
4.目标Provider组件启动起来后,就会将自己发布到AMS中,以便AMS可以将它的一个代理对象返回给调用者使用。
使用Provider时需要先调用getContentProvider()方法来获取ContentResolver对象,惯例先看时序图:
先看ContextWrapper类中的getContentResolver方法:
@Override public ContentResolver getContentResolver() { return mBase.getContentResolver(); }
调用到ContextImpl类中的getContentResolver方法:
private final ApplicationContentResolver mContentResolver; private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration, int createDisplayWithId) { mMainThread = mainThread; . . . mContentResolver = new ApplicationContentResolver(this, mainThread, user); } @Override public ContentResolver getContentResolver() { return mContentResolver; } private static final class ApplicationContentResolver extends ContentResolver { private final ActivityThread mMainThread; private final UserHandle mUser; public ApplicationContentResolver( Context context, ActivityThread mainThread, UserHandle user) { super(context); mMainThread = Preconditions.checkNotNull(mainThread); mUser = Preconditions.checkNotNull(user); } // 实现父类中的抽象方法,后面会调用 @Override protected IContentProvider acquireProvider(Context context, String auth) { return mMainThread.acquireProvider(context, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), true); } // 实现父类中的抽象方法,后面会调用 @Override public boolean releaseProvider(IContentProvider provider) { return mMainThread.releaseProvider(provider, true); } /** @hide */ protected int resolveUserIdFromAuthority(String auth) { return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier()); } . . . }
上面ApplicationContentResolver是ContextImpl的内部类。到这里调用者就获取到了ContentResolver对象。
获取到对象后,假设调用者要访问目标Provider的insert方法(其他方法的流程基本一致),先看下时序图:
先看ContentResolver类的insert方法:
public static final String SCHEME_CONTENT = "content"; public ContentResolver(Context context) { mContext = context != null ? context : ActivityThread.currentApplication(); mPackageName = mContext.getOpPackageName(); } public final @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues values) { android.util.SeempLog.record_uri(37, url); Preconditions.checkNotNull(url, "url"); // 获取provider IContentProvider provider = acquireProvider(url); if (provider == null) { throw new IllegalArgumentException("Unknown URL " + url); } try { long startTime = SystemClock.uptimeMillis(); // 执行插入操作 Uri createdRow = provider.insert(mPackageName, url, values); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */); return createdRow; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; } finally { // 释放provider releaseProvider(provider); } } /** * 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 acquireProvider(Uri uri) { // 判断scheme是否以content开头,如果不是说明这个uri不是用来访问provider的 if (!SCHEME_CONTENT.equals(uri.getScheme())) { return null; } final String auth = uri.getAuthority(); if (auth != null) { // 获取provider对象 return acquireProvider(mContext, auth); } return null; } // 抽象方法,由子类实现 /** @hide */ protected abstract IContentProvider acquireProvider(Context c, String name);
调用到这里,又回到了ContextImpl的内部类ApplicationContentResolver中的acquireProvider方法,该方法中又调用了ActivityThread类中的acquireProvider方法,看该方法之前,先看下传递的参数:
getUserIdFromAuthority方法获取userId: /** * Removes userId part from authority string. Expects format: * userId@some.authority * If there is no userId in the authority, it symply returns the argument * @hide */ public static String getAuthorityWithoutUserId(String auth) { if (auth == null) return null; int end = auth.lastIndexOf('@'); // 截取authority return auth.substring(end+1); } /** @hide */ public static int getUserIdFromAuthority(String auth, int defaultUserId) { if (auth == null) return defaultUserId; int end = auth.lastIndexOf('@'); if (end == -1) return defaultUserId; // 截取userId String userIdString = auth.substring(0, end); try { return Integer.parseInt(userIdString); } catch (NumberFormatException e) { Log.w(TAG, "Error parsing userId.", e); return UserHandle.USER_NULL; } }
上面两个方法主要是截取不规则的authority和获取userId的。再接着看下后续的时序图:
根据上面时序图,先看下ActivityThread类中的acquireProvider方法:
public final IContentProvider acquireProvider( Context c, String auth, int userId, boolean stable) { // 调用acquireExistingProvider检查该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. IActivityManager.ContentProviderHolder holder = null; try { // 通过BInder代理对象调用AMS的getContentProvider方法获取到目标Provider的代理对象 holder = ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(), auth, userId, stable); } 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; } // 用来保存当前应用程序进程中访问过的Content Provider组件代理对象 final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>(); // 各Provider被引用次数map final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap = new ArrayMap<IBinder, ProviderRefCount>(); public final IContentProvider acquireExistingProvider( Context c, String auth, int userId, boolean stable) { synchronized (mProviderMap) { final ProviderKey key = new ProviderKey(auth, userId); // 检查mProviderMap中是否已经有目标Provider代理对象 final ProviderClientRecord pr = mProviderMap.get(key); // 如果没有则返回null if (pr == null) { return null; } IContentProvider provider = pr.mProvider; IBinder jBinder = provider.asBinder(); // 判断目标Provider代理对象所运行在的进程是否还在运行 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; } // 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); // 判断mProviderRefCountMap中是否有目标Provider,如果有则其引用次数加1 if (prc != null) { incProviderRefLocked(prc, stable); } return provider; } }
接着看下ActivityManagerService类中的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); } final ProviderMap mProviderMap; private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { // 在AMS中,每个已经启动的Provider组件都使用一个ContentProviderRecord对象来描述,并保存在mProviderMap中 ContentProviderRecord cpr; ContentProviderConnection conn = null; ProviderInfo cpi = null; /*在这里可以准备自启动黑白名单的初始化操作*/ synchronized(this) { long startTime = SystemClock.elapsedRealtime(); ProcessRecord r = null; if (caller != null) { // 获取目标Provider组件代理对象的应用程序进程的信息 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; // 判断从startTime到这里的操作是否超过1秒 checkTime(startTime, "getContentProviderImpl: getProviderByName"); // First check if this content provider has been published... // mProviderMap在构造方法中初始化 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_OWNER) { cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER); 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_OWNER; checkCrossUser = false; } else { cpr = null; cpi = null; } } } 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"); // 判断目标Provider的android:multiprocess属性是否为true 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. // 为true说明目标Provider可以运行在调用者所在进程,故这里不用建立连接,因为调用者可以自己实例化目标Provider 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); // 如果稳定连接和不稳定连接之和为1 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"); // 开启Provider成本较高,故这里做下备份以后续访问时减小开销 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"); // 更新目标Provider所在进程的Adj值,如果返回false说明进程被杀 boolean success = updateOomAdjLocked(cpr.proc); // 更新目标Provider最近使用时间 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); // 连接数减1后是否还有连接,如果没有则为true 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); } boolean singleton; // 目标Provider没有在运行中 if (!providerRunning) { try { checkTime(startTime, "getContentProviderImpl: before resolveContentProvider"); // 到PMS中查找android:authorities属性值等于name的Provider组件的信息 cpi = AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); checkTime(startTime, "getContentProviderImpl: after resolveContentProvider"); } catch (RemoteException ex) { } // cpi为null说明没有任何应用提供该Provider 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; }else{ //SmartContainer modified begin if (!SmartContainerConfig.WITH_OUT_VIRTUAL_BOX){ userId = UserHandle.getUserId(cpi.applicationInfo.uid); } //SmartContainer modified end } 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; } // cpi.name是Provider组件的类名 ComponentName comp = new ComponentName(cpi.packageName, cpi.name); checkTime(startTime, "getContentProviderImpl: before getProviderByClass"); // 根据从PMS中查询到的Provider类名到mProviderMap查找是否有与类名一致的cpr对象 cpr = mProviderMap.getProviderByClass(comp, userId); checkTime(startTime, "getContentProviderImpl: after getProviderByClass"); final boolean firstClass = cpr == null; // firstClass为true说明目标Provider组件还没有启动起来 if (firstClass) { final long ident = Binder.clearCallingIdentity(); try { checkTime(startTime, "getContentProviderImpl: before getApplicationInfo"); // 到PMS中查找目标Provider所属应用程序的信息 ApplicationInfo ai = AppGlobals.getPackageManager(). getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId); checkTime(startTime, "getContentProviderImpl: after getApplicationInfo"); // 如果为null说明不存在,直接返回null if (ai == null) { Slog.w(TAG, "No package info for content provider " + cpi.name); return null; } ai = getAppInfoForUser(ai, userId); // 将cpi、ai等信息封装成一个cpr对象,用来描述即将要启动的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); } } checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord"); // 启动之前先检查先检查multiprocess属性是否为true 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)); // This is single process, and our app is now connecting to it. // See if we are already in the process of launching this // provider. // AMS把所有正在启动的Provider组件都保存在mLaunchingProviders中 final int N = mLaunchingProviders.size(); int i; // 遍历查找要启动的Provider是否正在启动中 for (i = 0; i < N; i++) { if (mLaunchingProviders.get(i) == cpr) { break; } } // If the provider is not already being launched, then get it // started. // i >= N说明要启动的Provider没有在启动中 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 started checkTime(startTime, "getContentProviderImpl: looking for process record"); ProcessRecord proc = getProcessRecordLocked( cpi.processName, cpr.appInfo.uid, false); // 如果要启动的Provider所在的进程已经启动,则直接在进程中安装该Provider即可 if (proc != null && proc.thread != null) { if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER, "Installing in existing process " + proc); // 判断目标Provider是否已经发布 if (!proc.pubProviders.containsKey(cpi.name)) { checkTime(startTime, "getContentProviderImpl: scheduling install"); // 把目标Provider保存在pubProviders中 proc.pubProviders.put(cpi.name, cpr); try { // 都有ActivityThread类中的scheduleInstallProvider方法安装模板Provider proc.thread.scheduleInstallProvider(cpi); } catch (RemoteException e) { } } // 目标Provider所运行在的进程还没有启动,则需要先启动进程再安装Provider } 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放到正在启动的Provider列表中 mLaunchingProviders.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) { // 保存cpr对象 mProviderMap.putProviderByClass(comp, cpr); } // 保存cpr对象 mProviderMap.putProviderByName(name, cpr); // 获取连接 conn = incProviderCountLocked(r, cpr, token, stable); if (conn != null) { // 因为目标Provider组件需要等待进程启动后才能启动,故需要等待进程启动完成 conn.waiting = true; } } checkTime(startTime, "getContentProviderImpl: done!"); } // Wait for the provider to be published... synchronized (cpr) { // 循环等待cpr所描述的Provider组件启动完成 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; } // 判断每个操作是否超过1秒 private void checkTime(long startTime, String where) { long now = SystemClock.elapsedRealtime(); if ((now-startTime) > 1000) { // If we are taking more than a second, log about it. Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where); } }
下面看ActivityThread类中的scheduleInstallProvider方法:
@Override public void scheduleInstallProvider(ProviderInfo provider) { sendMessage(H.INSTALL_PROVIDER, provider); } private class H extends Handler { public static final int INSTALL_PROVIDER = 145; . . . public void handleMessage(Message msg) { switch (msg.what) { . . . case INSTALL_PROVIDER: handleInstallProvider((ProviderInfo) msg.obj); break; } } } public void handleInstallProvider(ProviderInfo info) { final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); try { installContentProviders(mInitialApplication, Lists.newArrayList(info)); } finally { StrictMode.setThreadPolicy(oldPolicy); } } private void installContentProviders( Context context, List<ProviderInfo> providers) { final ArrayList<IActivityManager.ContentProviderHolder> results = new ArrayList<IActivityManager.ContentProviderHolder>(); // 循环调用installProvider方法在当前应用程序进程中启动保存在providers列表中的所有Provider组件 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 { // 将所有的cph对象传递给AMS,以便AMS可以获取到它们所描述的Provider组件的一个访问接口 ActivityManagerNative.getDefault().publishContentProviders( getApplicationThread(), results); } catch (RemoteException ex) { } }
接下来先分析installProvider方法在当前应用程序进程中启动一个Provider组件的过程,然后再分析AMS的publishContentProviders方法将启动完成之后的Provider组件发布到AMS中的过程:
publishContentProviders方法将启动完成之后的Provider组件发布到AMS中的过程: /** * Installs the provider. * * Providers that are local to the process or that come from the system server * may be installed permanently which is indicated by setting noReleaseNeeded to true. * Other remote providers are reference counted. The initial reference count * for all reference counted providers is one. Providers that are not reference * counted do not have a reference count (at all). * * This method detects when a provider has already been installed. When this happens, * it increments the reference count of the existing provider (if appropriate) * and returns the existing provider. This can happen due to concurrent * attempts to acquire the same provider. */ 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) { if (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; } try { final java.lang.ClassLoader cl = c.getClassLoader(); // info.name描述了要在当前进程中启动的Provider组件的类名 // 这里根据类名在进程中创建了一个Provider组件的实例 localProvider = (ContentProvider)cl. loadClass(info.name).newInstance(); // 获得一个IContentProvider接口,该接口是要发布到AMS中的,以便AMS可以将它返回给那些需要 // 访问它所描述的Provider组件的应用程序进程 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. // 进一步初始化创建的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); } IActivityManager.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); // 根据组件名到mLocalProvidersByName中查询是否已经存在 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; // 将provider组件封装成一个pr对象 pr = installProviderAuthoritiesLocked(provider, localProvider, holder); // 对一个应用程序进程来说,它所访问的Provider组件有可能是在当前应用程序进程中运行,也有可能是在 // 其他应用程序进程中运行。为了区分这两种情况,用mLocalProviders变量来保存运行在当前进程中的Provider mLocalProviders.put(jBinder, pr); mLocalProvidersByName.put(cname, pr); } retHolder = pr.mHolder; } else { // 根据Provider的Binder对象获取prc对象 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) { // Provider的引用数量加1 incProviderRefLocked(prc, stable); try { ActivityManagerNative.getDefault().removeContentProvider( holder.connection, stable); } catch (RemoteException e) { //do nothing content provider object is dead any way } } } else { // 如果prc对象为null,说明不存在目标Provider对象,需要创建 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; } private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider, ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) { // authority描述了android:authorities属性值,它是一个多值属性 final String auths[] = holder.info.authority.split(";"); final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid); // 创建一个pcr对象 final ProviderClientRecord pcr = new ProviderClientRecord( auths, provider, localProvider, holder); // 循环根据auth创建ProviderKey对象并保存在mProviderMap中 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; }
每一个Provider组件内部都有一个类型为Transport的Binder本地对象,这个Binder本地对象是用来传递给AMS的,然后AMS再将引用了它的一个Binder代理对象返回给需要访问该Provider组件的其他应用程序进程。这样,其他应用程序进程就可以通过这个Binder代理对象来间接地访问一个Provider组件中的数据了。
下面看ContentProvider类中的getIContentProvider方法和attachInfo方法:
private Transport mTransport = new Transport(); /** * Binder object that deals with remoting. * @hide */ class Transport extends ContentProviderNative { . . . } /** * Returns the Binder object for this provider. * @hide */ public IContentProvider getIContentProvider() { return mTransport; } /** * After being instantiated, this is called to tell the content provider * about itself. */ public void attachInfo(Context context, ProviderInfo info) { attachInfo(context, info, false); } private void attachInfo(Context context, ProviderInfo info, boolean testing) { mNoPerms = testing; /* * Only allow it to be set once, so after the content service gives * this to us clients can't change it. */ if (mContext == null) { mContext = context; if (context != null) { mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService( Context.APP_OPS_SERVICE); } mMyUid = Process.myUid(); if (info != null) { // 将Provider的访问权限保存 setReadPermission(info.readPermission); setWritePermission(info.writePermission); setPathPermissions(info.pathPermissions); mExported = info.exported; mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0; setAuthorities(info.authority); } // 回调ContentProvider的onCreate方法 ContentProvider.this.onCreate(); } }
最后看AMS中的publishContentProviders方法:
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(); // 循环取出保存在providers中的每一个ContentProviderHolder对象src, // 然后在AMS中找到与它对应的ContentProviderHolder对象dst 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); 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--; } } if (wasInLaunchingProviders) { mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r); } synchronized (dst) { // 将src所描述的一个Provider组件的一个IContentProvider访问接口保存在dst的成员变量provider中 dst.provider = src.provider; dst.proc = r; dst.notifyAll(); } updateOomAdjLocked(r); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); } }
到这里Provider已经启动完成了,并且将它的一个代理对象即一个类型为Transport的Binder代理对象发布到AMS中了。
内容有理解错误或偏差之处,请包涵指正,谢谢!
相关文章推荐
- Android中Context用法详解
- Android 开发环境目录结构
- Android for work总结(下)
- Android事件处理第一节(View对Touch事件的处理)
- android事件拦截处理机制详解
- 使用Android Studio查找文件中含有中文字符串位置(正则表达式)
- Android SyncAdapter同步实践
- 理解RxJava:(四)Reactive Android
- android背景选择器selector的用法
- Android 连接 SQL Server (jtds方式)——上
- Android提醒微技巧,你真的了解Dialog、Toast和Snackbar吗?
- Android Content Providers基础
- Android杂谈(8)关于自定义View的一些实践+遮罩理解
- Android 单元测试记录
- geekband android #5 第五周分享(数据持久化技术)
- Android基础总结(10)——手机多媒体的运用:通知、短信、相机、视频播放
- android stadio 设置编码区的字体
- Android基础总结(7)——异步消息处理
- Android MVP实战
- android—BroadcastReceiver 中的Context理解