Android ContentProvider 啟動分析


對於 ContentProvider 還不是很熟悉的同學,可以閱讀上一篇 Android ContentProvider 基本原理和使用詳解。本文主要是對 contentProvider 的源碼進行分析,從而了解 ContentProvider 的實現原理。

本文分析基於 android 10 的源碼, API 級別 29。 

 ContentProvider 啟動流程

ContentProvider (CP) 啟動流程圖如下:可以對着這個來閱讀下面的內容。

1、ActivityThread.handleBindApplication

對於了解 Activity 啟動流程的,可以知道 Application 實例是在 ActivityThread 的 handleBindApplication 方法中創建。在講解這個方法時疏漏了一點,那就是 ContentProvider 會在這個方法中創建。 

// ActivityThread
private void handleBindApplication(AppBindData data) {
    // ......
        Application app;try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            app = data.info.makeApplication(data.restrictedBackupMode, null);

            // Propagate autofill compat state
            app.setAutofillOptions(data.autofillOptions);

            // Propagate Content Capture options
            app.setContentCaptureOptions(data.contentCaptureOptions);

            mInitialApplication = app;

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
 installContentProviders(app, data.providers);                 }
            }

            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            try {
         // Instrumentation onCreate 方法 mInstrumentation.onCreate(data.instrumentationArgs); }
catch (Exception e) { throw new RuntimeException( "Exception thrown in onCreate() of " + data.instrumentationName + ": " + e.toString(), e); } 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); } } } finally { // If the app targets < O-MR1, or doesn't change the thread policy // during startup, clobber the policy to maintain behavior of b/36951662 if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1 || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) { StrictMode.setThreadPolicy(savedPolicy); } } // ...... }

上面簡化了大量代碼,但重要部分還在。AppBindData 對象 data 的成員變量 providers 保存了要在當前應用程序進程中啟動的 CP 組件,接下會調用 installContentProviders 方法。

可以看到 installContentProviders 在 Application 的 onCreate 之前調用,所以可以得出結論:

ContentProvider 的 onCreate 在 Application 的 onCreate 之前調用。

因為 onCreate 是在啟動過程中關掉用的,因此盡量避免在里面執行耗時的操作,例如與IO相關的操作;否則,就可能造成 Content Provider 組件啟動超時。

ActivityThread.installContentProviders

    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();
for (ProviderInfo cpi : providers) { ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); if (cph != null) { cph.noReleaseNeeded = true; results.add(cph); } } try {
       // 發布到 AMS 中 ActivityManager.getService().publishContentProviders( getApplicationThread(), results); }
catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } }

這個方法主要做了兩件事。

  • 第一.通過循環變量 providerinfo 信息,調用 installProvider 方法將 provider 信息安裝完成並封裝成了一個 ContentProviderHolder 類型的對象,里面包含 IContentProvider 接口。

  • 第二.調用AMS服務的publishContentProviders方法,將這些安裝完成的 Provider 信息發布到AMS 服務,以便其他進程訪問。

ContentProviderHolder

它是什么呢?它其實是一個可以在進程間傳遞的數據對象 (aidl),看一下它的定義:

public class ContentProviderHolder implements Parcelable {
    public final ProviderInfo info;
    public IContentProvider provider;
    public IBinder connection;
    ...

里面包含了 CP 的很多信息,所以 AMS 拿到 ContentProviderHolder (CPH),就等於拿到了所有 CP 的信息,后面發不到 AMS 就是依賴該對象。

ActivityThread.installProvider

接下來會調用  ActivityThread 的 installProvider 方法,如果傳入的 holder 為 null,所以就會在 installProvider 中創建 ContentProvider 實例並加入 HashMap 中進行緩存。

那就先來看 installProvider 的處理過程,在來看 AMS 發布 ContentProvider 的過程。

    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) { 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. 調用 attachInfo 方法來進行初始化,可以參看流程圖 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);
          // 第一次創建 pr 為空
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). if (!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; }

在 installContentProviders 方法中調用這個方法的時候,holder 參數傳遞的值為 Null,也是因為這些 ContentProvider 是第一次安裝。所以 holder 肯定為 Null。所以此時滿足 if 的條件。在 If 語句中,首先根據條件獲取相應的 Context 上下文信息。

然后 ClassLoader 加載對應的 ContentProvider 類,並創建該類的對象,然后調用 ContentProvider 的 attachInfo 方法。該方法作用是將新創建的 ContentProvider 和 Context,ProviderInfo 關聯起來,最后調用該 Provider 的 onCreate 方法啟動 ContentProvider。這個一個 ContentProvider 就創建完成了,下一步就是將它保存到應用進程的中,以方便查找和管理。並發布到 AMS 服務中,方便其他進程調用。

獲取 ContetProvider 的 IContentProvider(ICP) 賦值給 provider 變量,IContentProvider 是 ContentProvider 客戶端和服務端通信的接口,getIcontentProvider 理解為得到一個 Binder 類型的對象,用於ContentProvider 客戶端和服務端之間的通信。

由於是第一次啟動 ContentProvider,所以該信息還沒有保存,所以變量 pr 為空,此時根據 ProviderInfo 的信息和 Binder 類型 IContentProvider 對象,創建一個 ContentProviderHolder 對象,它里邊封裝了這個 ContentProvider 的 ProviderInfo 和 IContentProvider 信息。

方法最后返回創建的這個 ContentProviderHolder 的對象。 

Transport

Transport 是 ContentProvider 一個內部類,繼承自 ContentProviderNative,是一個 binder, 具有遠程通信能力。

getIcontentProvider 具體代碼可以參看下面的代碼。

  //  ContentProvider.java
   /**
     * Binder object that deals with remoting.  是一個 binder ,可以遠程通信
     */
    class Transport extends ContentProviderNative {
        volatile AppOpsManager mAppOpsManager = null;
        volatile int mReadOp = AppOpsManager.OP_NONE;
        volatile int mWriteOp = AppOpsManager.OP_NONE;
        volatile ContentInterface mInterface = ContentProvider.this;
    }


  private Transport mTransport = new Transport();


    /**
     * Returns the Binder object for this provider.
     *
     * @return the Binder object for this provider
     * @hide
     */
    @UnsupportedAppUsage
    public IContentProvider getIContentProvider() {
        return mTransport;
    }

// ContentProviderNative.java
abstract public class ContentProviderNative extends Binder implements IContentProvider {}
 

IContentProvider 其實就是一個 binder 。

ContentProviderRecord

上面有個 ContentProviderRecord(CPR), 它是系統 (ActivityManagerService) 用來記錄一個 ContentProvider 相關信息的對象。

final class ContentProviderRecord implements ComponentName.WithComponentName {
    final ActivityManagerService service;
    public final ProviderInfo info;
    final int uid;
    final ApplicationInfo appInfo;
    final ComponentName name;
    final boolean singleton;
    public IContentProvider provider;
    public boolean noReleaseNeeded;
    // All attached clients
    final ArrayList<ContentProviderConnection> connections
            = new ArrayList<ContentProviderConnection>();
    //final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
    // Handles for non-framework processes supported by this provider
    HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
    // Count for external process for which we have no handles.
    int externalProcessNoHandleCount;
    ProcessRecord proc; // if non-null, hosting process.
    ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
    String stringName;
    String shortStringName;

可以看到 record 里面記錄了相當多的信息。

ProviderInfo 

用來保存一個 ContentProvider 的信息( manifest 中的 <provider>), 比如 authorityreadPermission 等。

public final class ProviderInfo extends ComponentInfo
        implements Parcelable {
    
    /** The name provider is published under content:// */
    public String authority = null;
    
    /** Optional permission required for read-only access this content
     * provider. */
    public String readPermission = null;
    
    /** Optional permission required for read/write access this content
     * provider. */
    public String writePermission = null;
    
    /** If true, additional permissions to specific Uris in this content
     * provider can be granted, as per the
     * {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions
     * grantUriPermissions} attribute.
     */
    public boolean grantUriPermissions = false;
    
    /**
     * If non-null, these are the patterns that are allowed for granting URI
     * permissions.  Any URI that does not match one of these patterns will not
     * allowed to be granted.  If null, all URIs are allowed.  The
     * {@link PackageManager#GET_URI_PERMISSION_PATTERNS
     * PackageManager.GET_URI_PERMISSION_PATTERNS} flag must be specified for
     * this field to be filled in.
     */
    public PatternMatcher[] uriPermissionPatterns = null;
    
    /**
     * If non-null, these are path-specific permissions that are allowed for
     * accessing the provider.  Any permissions listed here will allow a
     * holding client to access the provider, and the provider will check
     * the URI it provides when making calls against the patterns here.
     */
    public PathPermission[] pathPermissions = null;
    
    /** If true, this content provider allows multiple instances of itself
     *  to run in different process.  If false, a single instances is always
     *  run in {@link #processName}. */
    public boolean multiprocess = false;

ActivityThread.installProviderAuthoritiesLocked

接着看看一下 installProviderAuthoritiesLocked 的實現

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

根據 Provider 的信息創建了一個 ProviderClientRecord (PCR) 對象,authority 是一個多屬性值,變量這個 Provider 對應的所有 authority,每個 authority 屬性為 key,保存這個 ProviderClientReocrd 到 mProviderMap 描述的 HashMap 中。

在一個應用進程中 (ActivityThread) 有三個列表來保存本進程中的 ContentProvider 的信息。

  • ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap

    • 主要以 authority 為 key,保存 providerClientRecord 信息
  • ArrayMap<IBinder, ProviderClientRecord> mLocalProviders

    • 以通信的接口 Binder 對象為 key 保存 ProviderClientRecord 對象。主要保存了本進程的 ContentProvider 的信息
  • ArrayMap<ComponentName,ProviderClientRecord> mLocalProvidersByName

    • 以 Provider 的 ComponentName 信息為key 保存 ProviderClientRecord 對象。主要保存了本進程的 ContentProvider 的信息

通過 installProvider 方法將 ContentProvider 的類加載到內存中來,並創建了 ContentProvider 的對象,調用了 ContentProvider的onCreate 來啟動它。然后將它按照不同的存儲類型分別保存不同的 ContentProvider 集合中。

AMS.publishContentProviders 

ContentProvider 本地創建完成並保存后,將它封裝成立一個 ContentProviderHolder 對象返回,然后我們調用 AMS 的 publishContentProviders 方法(實際上是通過 AMP (ActivityManagerProxy) 發送一個類型為 PUBLISH_CONTENT_PROVIDERS_TRANSACTION的進程間通信請求),將這些 Holder 對象發送給 AMS 服務將他們發布到 AMS 服務中。

    // AMS
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; }
          // 獲取 cpr 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);
            // 組件名字為 key 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); }
// Make sure the package is associated with the process. // XXX We shouldn't need to do this, since we have added the package // when we generated the providers in generateApplicationProvidersLocked(). // But for some reason in some cases we get here with the package no longer // added... for now just patch it in to make things happy. r.addPackage(dst.info.applicationInfo.packageName, dst.info.applicationInfo.longVersionCode, mProcessStats); synchronized (dst) { dst.provider = src.provider; dst.setProcess(r); dst.notifyAll(); } updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); } }

參數 caller 是一個類型為 ApplicationThread 的 binder 代理對象,它引用了運行在新創建的應用程序進程中的一個 ApplicationThread 對象,第 8 行代碼通過它來獲得用來描述新創建的應用程序進程的一個 ProcessRecord 對象。

新創建的應用程序進程在啟動時,會將需要在它里面運行的 Content Provider 組件啟動起來。Content provider 組件在 AMS 中使用一個 ContentProviderRecord 對象來描述,它們保存在用來描述新創建的應用程序進程的一個 ProcessRecord 對象 r 成員變量 pubProviders 中。

參數 providers 包含了要發布到 AMS 中的 Content provider 組件,每一個 content provider 組件都使用 ContentProviderHolder 對象來描述,它里面包含了要發布的 content provider 組件的一個 IContentProvider 接口。

 代碼中的第一個大 for 循環,首先去除保存在參數 Providers 中的每一個 ContentProviderHolder 對象 src,然后在 AMS 中找到與對應的一個CPR 對象 dst ,最后將 ContentProviderHolder 對象 src 所描述的一個 CP 組件的一個 IContentProvider 訪問接口保存在 CPR 對象 dst 的成員變量 provider 中。

關於 ContentProvider 隨着應用的啟動而加載、初始化的流程到這里就結束了。

下面就來看使用 ContentProvider 的工作流程。 

數據傳輸過程

前面講了 content Provider 的啟動過程,接下來看看其數據是如何傳輸的。

通常我們獲取 ContentResolver 的代碼如下:

ContentResolver cr = context.getContentResolver();  //獲取ContentResolver

Context 的所有實現都是在 ContextImpl 中,所以 context.getContentResolver() 方法的實現也是一樣。

ContextImpl.getContentResolver().query()

我們從 ContextImpl.getContentResolver().query() 開始看:


  // ContentResolver.java
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
     // 獲取訪問接口 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 {
          // 調用 query 方法獲取數據 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 unstableProviderDied(unstableProvider);
          // 再次獲取訪問接口 stableProvider
= acquireProvider(uri); if (stableProvider == null) { return null; } qCursor = stableProvider.query( mPackageName, uri, projection, queryArgs, remoteCancellationSignal); } if (qCursor == null) { return null; } // Force query execution. Might fail and throw a runtime exception here. qCursor.getCount(); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs); // 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); } } }

該方法主要是獲取到 IContentProvider,從而根據 uri 拿到數據。

要想理解上述過程的具體實現細節,需要先分析 ContentResolver 類的 acquireProvider 方法的調用過程,然后分析 IContentProvider 接口方法 query 的實現。

ContentResolver 的 acquireProvider 是一個抽象方法,具體實現可以看 ApplicationContentResolver:

    private final ActivityThread mMainThread;
protected IContentProvider acquireProvider(Context context, String auth) { return mMainThread.acquireProvider(context, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), true); }

ApplicationContentResolver 成員變量 mMainThread 指向了一個 ActivityThread 對象,它是在構造函數里面初始化的。因此,實際上是通過 ActivityThread 來獲取 contentProvider 的代理對象。

ActivityThread.acquireProvider

來看下具體的實現:

    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
     // 如果已經存在了,就直接返回,這里是從前面提到的 mProviderMap 來獲取
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)) {
          // 通過 ams 來獲取 holder 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; } // 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; }

即在本地沒有獲得過 IContentProvider 時,直接向 ActivityManagerService 發起 getContentProvider 的請求,最終調用ActivityManagerService.getContentProviderImpl(), 這個方法就是 ContentProvider 實例化邏輯的核心了:

首先來看一下這個方法的聲明:

ContentProviderHolder getContentProviderImpl(IApplicationThread caller,String name, IBinder token, boolean stable, int userId)

即最終是返回一個 ContentProviderHolder,前面已經解釋了。

繼續看  getContentProviderImpl(),這個方法比較長,所以接下來我們分段來看這個方法, 順序是(1)、(2)、(3)... 這種 : 

(1)ActivityManagerService.getContentProviderImpl() 

 //三個關鍵對象
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;
    ...
    cpr = mProviderMap.getProviderByName(name, userId); // 看看系統是否已經緩存了這個ContentProvider

 這里主要需要解釋下 ContentProviderConnection

/**
 * Represents a link between a content provider and client.
 */
public final class ContentProviderConnection extends Binder {
    public final ContentProviderRecord provider;
    public final ProcessRecord client;
    public final long createTime;
    public int stableCount;
    public int unstableCount;
    // The client of this connection is currently waiting for the provider to appear.
    // Protected by the provider lock.
    public boolean waiting;
    // The provider of this connection is now dead.
    public boolean dead;

它是一個 Binder。連接服務端 (ActivityManagerService) 和客戶端 (我們的app)。里面記錄着一個 ContentProvider 的狀態,比如是否已經死掉了等。其他幾個都已經解釋過了。

(2)ActivityManagerService.getContentProviderImpl()

cpr = mProviderMap.getProviderByName(name, userId); // 看看系統是否已經緩存了這個ContentProvider
    boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
    if (providerRunning) { 
        ...
    }

    if (!providerRunning) {
        ...
    }

 即根據 ContentProvider 所在的進程是否是活躍這個 ContentProvider 是否被啟動過(緩存下來)兩個狀態來進行不同的處理 :

ContentProvider 已被加載並且所在的進程正在運行

即:  if(providerRunning){ ... } 中的代碼

 ProcessRecord r = getRecordForAppLocked(caller); //獲取客戶端(獲得content provider的發起者)的進程信息
    if (r != null && cpr.canRunHere(r)) { //如果請求的ContentProvider和客戶端位於同一個進程
        ContentProviderHolder holder = cpr.newHolder(null); //ContentProviderConnection參數傳null
        holder.provider = null; //注意,這里置空是讓客戶端自己去實例化!!
        return holder;
    }

    //客戶端進程正在運行,但是和ContentProvider並不在同一個進程
    conn = incProviderCountLocked(r, cpr, token, stable); // 直接根據 ContentProviderRecord和ProcessRecord 構造一個 ContentProviderConnection

    ...

即如果請求的是同進程的 ContentProvider 則直接回到進程的主線程去實例化 ContentProvider。否則使用 ContentProviderRecord 和 ProcessRecord 構造一個ContentProviderConnection。

ContentProvider所在的進程沒有運行並且服務端(ActivityManagerService)沒有加載過它

即: if(!providerRunning){ ... }中的代碼

//先解析出來一個ProviderInfo
    cpi = AppGlobals.getPackageManager().resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
    ...
    ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
    cpr = mProviderMap.getProviderByClass(comp, userId); //這個content provider 沒有被加載過

    final boolean firstClass = cpr == null;
    if (firstClass) {
        ...
        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton); // 構造一個 ContentProviderRecord
    }

    ...

    final int N = mLaunchingProviders.size(); //  mLaunchingProviders它是用來緩存正在啟動的 ContentProvider的集合的
    int i;
    for (i = 0; i < N; i++) {
        if (mLaunchingProviders.get(i) == cpr) {  // 已經請求過一次了,provider正在啟動,不重復走下面的邏輯
            break;
        }
    }

    //這個 ContentProvider 不是在啟動狀態,也就是還沒啟動
    if (i >= N) {
        ProcessRecord proc = getProcessRecordLocked(cpi.processName, cpr.appInfo.uid, false);
        ...
        
         if (proc != null && proc.thread != null && !proc.killed) { //content provider所在的進程已經啟動
            proc.thread.scheduleInstallProvider(cpi); //安裝這個 Provider , 即客戶端實例化它
          } else {
            //啟動content provider 所在的進程, 並且喚起 content provider
            proc = startProcessLocked(cpi.processName,cpr.appInfo, false, 0, "content provider",new ComponentName(cpi.applicationInfo.packageName,cpi.name)...);
         }

        cpr.launchingApp = proc;
        mLaunchingProviders.add(cpr); //添加到正在啟動的隊列
    }

    //緩存 ContentProvider信息
    if (firstClass) {
        mProviderMap.putProviderByClass(comp, cpr);
    }
    mProviderMap.putProviderByName(name, cpr);

    //構造一個 ContentProviderConnection
    conn = incProviderCountLocked(r, cpr, token, stable);
    if (conn != null) {
        conn.waiting = true; //設置這個connection

(3)ActivityManagerService.getContentProviderImpl()

    // Wait for the provider to be published...
    synchronized (cpr) {
        while (cpr.provider == null) {
            ....
            if (conn != null) {
                conn.waiting = true;
            }
            cpr.wait();
        }
    }

    return cpr != null ? cpr.newHolder(conn) : null; //返回給請求這個客戶端的進程

根據前面的分析,ContentProvider 所在的進程沒有運行或者不是和獲取者同一個進程,就創建了一個 ContentProviderConnection,那么服務端就會掛起,啟動 ContentProvider 所在的進程,並等待它實例化 ContentProvider 。ok,通過前面的分析我們知道 ContentProvider 最終是在它所在的進程實例化的。

接下來就看一下客戶端相關代碼,前面分析我們知道,如果客戶端進程請求的 ContentProvider 位於同一個進程,則 ActivityManager.getService().getContentProvider(...);會返回一個內容為空的 ContentProviderHolder, 我們再拿剛開始客戶端向服務端請求 ContentProvider 的代碼看一下:

    holder = ActivityManager.getService().getContentProvider( getApplicationThread(), auth, userId, stable);

    //在向服務端獲取holder,服務端如果發現ContentProvider的進程和當前客戶端進程是同一個進程就會讓客戶端進程來實例化ContentProvider,具體細節可以在下面分析中看到
    holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable);

不在同一個進程中的 ContentProvider 實例化過程

如果客戶端進程請求的 ContentProvider 不在同一個進程,根據前面我們分析 ActivityManagerService 的邏輯可以知道,ActivityManagerService 會調用ContentProvider 所在進程的 proc.thread.scheduleInstallProvider(cpi), 其實最終調用到 ActivityThread.installContentProviders

 private void installContentProviders(Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        //ActivityManagerService 讓客戶端啟動的是一個ContentProvider列表
        for (ProviderInfo cpi : providers) {
            ContentProviderHolder cph = installProvider(context, null, cpi,false, true ,true);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        ActivityManager.getService().publishContentProviders(getApplicationThread(), results); //通知服務端,content provider ok啦
    }

到這里,就跟前面的啟動邏輯基本一樣了。 那么在獲取到接口后,又是怎么通過 query 拿到數據的呢?

query 過程解析

從前面的分析可知,IContentProvider 是 Transport 的實例,所以具體 query 的邏輯需要看

        @Override
        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) {
                if (projection != null) {
                    return new MatrixCursor(projection, 0);
                }

                Cursor cursor;
                final String original = setCallingPackage(callingPkg);
                try {
            // 調用的是 mInterface cursor
= mInterface.query( uri, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } finally { setCallingPackage(original); } if (cursor == null) { return null; } // Return an empty cursor for all columns. return new MatrixCursor(cursor.getColumnNames(), 0); } Trace.traceBegin(TRACE_TAG_DATABASE, "query"); final String original = setCallingPackage(callingPkg); try { return mInterface.query( uri, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } finally { setCallingPackage(original); Trace.traceEnd(TRACE_TAG_DATABASE); } }

這里是通過  mInterface 來調用 query () 方法

        volatile ContentInterface mInterface = ContentProvider.this;
而實際上就是調用的自己實現的 provider 方法。具體實現其實是通過 db 來查詢的。到這里關於 CP 的數據傳輸過程也講完了。
但是對於跨進程,其實應該是通過 ContentProviderNative 的內部類 ContentProviderProxy 來進行跨進程調用,最終會回調到 ContentProviderNative 的 onTransact 方法,然后再調用 mInterface 的 query 方法來實現查詢。
class ContentProviderProxy implements IContentProvider{}

ContentProviderProxy 是實現了 IContentProvider 接口的。那么是什么時候轉成 ContentProviderProxy 呢?

// ContentProviderHolder.java
private ContentProviderHolder(Parcel source) {
        info = ProviderInfo.CREATOR.createFromParcel(source);
        provider = ContentProviderNative.asInterface( source.readStrongBinder());
        connection = source.readStrongBinder();
        noReleaseNeeded = source.readInt() != 0;
    }

到這里才知道原來是創建 CPH 的時候,會根據是當前進程還是跨進程來返回對應的實例。

  // ContentProviderNative.java
  static public IContentProvider asInterface(IBinder obj)
    {
        if (obj == null) {
            return null;
        }
        IContentProvider in =
            (IContentProvider)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ContentProviderProxy(obj);
    }

到這里相信你應該能理解 CP 的啟動以及其跨進程通信能力了。

總結

對於 ContentProvider,有兩種啟動方式:
  1. 一種是啟動 App 的時候,啟動 CP;

  2. 另一種是需要訪問其他 App 的數據,如果對應的 App 並沒有啟動,這時候也會啟動 CP;

一旦 CP 啟動之后,就會將   ContentProviderHolder(內含 ICP 接口) 發布到 AMS 中,這樣其他 App 或自身都可以通過 AMS 獲取到 ICP 接口,從而獲取數據。 此外,ContentProvider 的 onCreate 在 Application 的 onCreate 之前調用。 

參考文章

Android系統源碼分析之-ContentProvider

ContentProvider原理分析

Android-ContentProvider源碼解讀


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM