ContentProvider啟動流程分析(三)

0x01 扯東扯西的前言&概述


本片博客對應時序圖上的step15—26上接第二篇博客:ContentProvider啟動流程分析二!

同時此系列博客同步在博客園發布:ContentProvider啟動流程分析系列!詳情戳這里即可訪問~

0x02 ContentProvider啟動流程分析


step15: ApplicationThread#bindApplication()

上一步,在ApplicationThreadProxy類的bindApplication()函數中,通過Binder對象mRemote發出了一個進程間通信請求!

反查源碼很容易知道,bindApplication()函數,其實是IApplicationThread接口類的接口函數,而ActivityThread類的內部類ApplicationThread實現了這一接口,所以自然也就實現了接口函數bindApplication(),我們來看ApplicationThread類的函數bindApplication()源碼刪減如下:

public final void bindApplication(String processName, ApplicationInfo appInfo,
            List<ProviderInfo> providers, ComponentName instrumentationName,
            ProfilerInfo profilerInfo, Bundle instrumentationArgs,
            IInstrumentationWatcher instrumentationWatcher,
            IUiAutomationConnection instrumentationUiConnection, int debugMode,
            boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
            Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
            Bundle coreSettings) {
    AppBindData data = new AppBindData();
    data.processName = processName;
    data.appInfo = appInfo;
    data.providers = providers;
    data.instrumentationName = instrumentationName;
    data.instrumentationArgs = instrumentationArgs;
    data.instrumentationWatcher = instrumentationWatcher;
    data.instrumentationUiAutomationConnection = instrumentationUiConnection;
    data.debugMode = debugMode;
    data.enableOpenGlTrace = enableOpenGlTrace;
    data.restrictedBackupMode = isRestrictedBackupMode;
    data.persistent = persistent;
    data.config = config;
    data.compatInfo = compatInfo;
    data.initProfilerInfo = profilerInfo;
    sendMessage(H.BIND_APPLICATION, data);
}
  • 沒錯!ApplicationThread類的成員函數bindApplication(),就是類型為BIND_APPLICATION_TRANSACTION的進程間通信請求的真正處理者!處理的邏輯是:先把將要啟動的ContentProvider組件列表,封裝成一個AppBindData對象;
  • 然后再調用外部類ActivityThread的成員函數sendMessage(),再次將這個AppBindData對象封裝成一個類型為BIND_APPLICATION的消息,以便可以發送到新建應用程序進程的主線程的消息隊列中!

step16: ActivityThread#sendMessage()

ActivityThread類持有一個mH成員變量,mH實際上是一個Handler對象,通過mH.sendMessage()發送消息到新建應用程序進程的主線程的消息隊列中!所以,這個消息最后是最終是在mH.handleMessage()函數中處理的。

step17: ActivityThread#handleMessage()

ActivityThread內部類H的程艷方法handleMessage()源碼刪減如下:

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        ......
    case BIND_APPLICATION:
        AppBindData data = (AppBindData)msg.obj;
        handleBindApplication(data);
        break;
        ......
    }
    if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
  • 在handleMessage()函數中,我們只考慮BIND_APPLICATION的情況,首先將Message對象msg的成員變量obj強轉成個AppBindData對象,它包含了要啟動的ContentProvider列表。
  • 然后,調用handleBindApplication()函數把這些ContentProvider組件啟動起來!

接下來,關注handleBindApplication()函數,是怎樣把這些ContentProvider組件啟動起來的?

step18: ActivityThread#handleBindApplication()

ActivityThread類的成員函數handleBindApplication()源碼如下:

private void handleBindApplication(AppBindData data) {
    ......
    // don't bring up providers in restricted mode; they may depend on the
    // app's custom Application class
    if (!data.restrictedBackupMode) {
        List<ProviderInfo> providers = data.providers;
        if (providers != null) {
            installContentProviders(app, providers);
            ......
        }
    }
}

參數data是一個AppBindData對象,data的成員變量providers保存了,需要在當前進程中啟動的ContentProvider組件,接下來則會調用ActivityThread類的成員函數installContentProviders()來啟動這些組件!

step19: ActivityThread#installContentProviders()

ActivityThread類的成員函數installContentProviders()源碼如下:

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

    for (ProviderInfo cpi : providers) {
        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) {
    }
}
  • for-each循環得到保存在providers中的每一個組件ProviderInfo對象cpi,然后調用installProvider()函數啟動每個cpi對應的ContnetProvider組件;
  • 這些組件啟動之后,就可以獲得其對應的一個IContentProvider接口;然后,把這個接口封裝成一個ContentProviderHolder對象;
  • 最后調用AMS代理對象(也即,ActivityManagerProxy)的成員函數publishContentProviders(),將這些ContentProviderHolder對象傳遞給AMS;AMS正是通過這些ContentProviderHolder對象獲取它所描述的ContentProvider組件的一個訪問接口;

接下來,先分析ActivityThread類成員函數installProvider()在當前應用程序進程中啟動一個ContentProvider組件的過程;然后,分析AMS代理對象ActivityManagerProxy成員函數publishContentProviders()將啟動完成的組件發布到AMS的過程!

step20: ActivityThread#installProvider()

ActivityThread類成員函數installProvider()源碼如下:

/**
 * 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;
    // holder為空,表示參數info所描述的ContentProvider組件需要在當前進程中啟動
    if (holder == null || holder.provider == null) {
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        if (context.getPackageName().equals(ai.packageName)) {
            c = context;
        }
        ......
        final java.lang.ClassLoader cl = c.getClassLoader();
        localProvider = (ContentProvider)cl.
                        loadClass(info.name).newInstance();
        provider = localProvider.getIContentProvider();
        if (provider == null) {
            return null;
        }
        // XXX Need to create the correct context for this provider.
        localProvider.attachInfo(c, info);
    } else {
        provider = holder.provider;
    }

    IActivityManager.ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                provider = pr.mProvider;
            } else {
                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 {
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                ......
            } 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;
}
  • 對傳進來的參數holder判空,如果為null,表示要在當前應用程序進程中,將參數info所描述的ContentProvider組件啟動起來;另一個參數context描述了將要啟動的ContentProvider組件運行的上下文環境;
  • localProvider表示一個ContentProvider組件對象,通過localProvider.getIContentProvider(),也即調用localProvider的成員函數getIContentProvider()來獲得該組件對應的一個IContentProvider接口provider;這個IContentProvider最終需要發布到AMS,AMS會將它返回給那些需要訪問它所描述的ContentProvider組件的調用方應用程序進程;
  • 接下來通過localProvider.attachInfo()來進一步初始化前面所創建的ContentProvider組件;
  • 接下來對全局變量mProviderMap加同步鎖,然后對localProvider判空,正常情況下此時的localProvider應該是非空的,所以,自然要把它保存到mLocalProviders和mLocalProvidersByName中;
  • 但是如果localProvider仍然為空,接下來會出現一個ProviderRefCount對象prc,用來表示和記錄ContentProvider組件的引用計數,如果prc為空,則把這個localProvider封裝成一個ProviderClientRecord對象,并保存在prc變量中;
  • 最后,返回這個ContentProviderHolder對象!

接下來,繼續分析ContentProvider組件的成員函數getIContentProvider()和成員函數attachInfo()的實現細節!

step21: ContentProvider#getIContnetProvider()

ContentProvider類成員函數getIContentProvider()源碼如下:

public abstract class ContentProvider implements ComponentCallbacks2 {
    ......
    private Transport mTransport = new Transport();
    ......
    class Transport extends ContentProviderNative {......}
    ......
    public IContentProvider getIContentProvider() {
        return mTransport;
    }
    ......
}
  • 每一個ContentProvider組件都有一個內部類Transport,其本質是一個Binder本地對象;并且持有一個類型為Transport的全局變量mTransPort來表示一個Binder本地對象。
  • 通過將這個Binder本地對象傳遞給AMS,然后AMS會將引用了這個Binder本地對象的一個Binder代理對象返回給需要訪問該ContentProvider組件的其他應用程序進程;這樣,其他應用程序進程就可以通過這個Binder代理對象來間接的訪問一個ContentProvider組件中的數據了!
  • ContentProvider類成員函數getIContentProvider()的實現很簡單,只是簡單地將全局變量mTransPort描述的一個IContentProvider接口返回給調用者。

step22: ContentProvider#attachInfo()

ContentProvider類的成員函數attachInfo(),作用是初始化前面所創建的一個ContentProvider組件!attachInfo()源碼如下:

public abstract class ContentProvider implements ComponentCallbacks2 {
    ......
    /**
     * After being instantiated, this is called to tell the content provider
     * about itself.
     *
     * @param context The context this provider is running in
     * @param info Registered information about this content provider
     */
    public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
    }

    private void attachInfo(Context context, ProviderInfo info, boolean 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) {
                setReadPermission(info.readPermission);
                setWritePermission(info.writePermission);
                setPathPermissions(info.pathPermissions);
                ......
            }
            ContentProvider.this.onCreate();
        }
    }
    ......
}
  • 依然是兩個重載函數,接收兩個參數的attachInfo()函數內部調用了接收三個參數的attachInfo()函數,我們直接關注接收三個參數的attachInfo()函數;
  • setReadPermission(info.readPermission),setWritePermission(info.writePermission)和setPathPermissions(info.pathPermissions)這三個函數的作用是,設置ContentProvider組件的讀寫權限和訪問權限;
  • 最后ContentProvider.this.onCreate()函數,實際調用的是COntentProvider子類的onCreate()函數,以便在子類的onCreate()函數中,執行一些業務相關的初始化操作!
  • 在我們最開始的場景中,ContentProvider組件指的就是BxxApp應用程序進程中SubContentProvider組件,所以上面調用的時加上就是SubContentProvider類的onCreate()函數,而我們的業務邏輯相關的一些初始化工作,也正是放在SubContentProvider類的onCreate()函數中執行的!

step23: SubContentProvider#onCreate()

public class SubContentProvider extends ContentProvider {
    ......
    @Override
    public boolean onCreate() {
        ContentResolver resolver = getContext().getContentResolver();
        DatabaseHelper helper = new DtatabaseHelper(......);
        ......
        return true;
    }
    ......
}
  • 需要注意,由于一個ContentProvider組件再啟動過程中需要執行onCreate()函數,因此,我們應該避免在onCeate()方法中執行耗時操作,例如和IO相關的操作,否則可能造成這個ContentProvider組件啟動超時!

這一步執行完成后,就會返回到前面的step20,然后再次返回到前面的step19,即ActivityThread類成員函數installContentProviders()中,接下來就會調用AMS代理對象的成員函數publishContentProviders(),也即ActivityManagerProxy的成員函數publishContentProviders(),將前面所有啟動的ContentProvider組件的一個IContentProvider訪問接口發布到ActivityManagerService中~

step24: ActivityManagerProxy#publishContentProviders()

ActivityManagerProxy類成員函數publishContentProviders(),會將所有啟動的ContentProvider組件的一個IContentProvider訪問接口發布到ActivityManagerService中,源碼如下:

public void publishContentProviders(IApplicationThread caller,
        List<ContentProviderHolder> providers) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeTypedList(providers);
    mRemote.transact(PUBLISH_CONTENT_PROVIDERS_TRANSACTION, data, reply, 0);
    reply.readException();
    data.recycle();
    reply.recycle();
}
  • 先把接收的參數保存到Parcel對象data中;
  • 接著再通過ActivityManagerProxy類內部的一個Binder代理對象mRemote向ActivityMnagerService發送一個類型為PUBLISH_CONTENT_PROVIDERS_TRANSACTION進程間通信請求;

step25: ActivityManagerService#publishContentProviders()

以上10步都是在新建的應用程序進程中執行的!step25是在ActivityManagerService中執行的,AMS類成員函數publishContentProviders(),用來處理類型為PUBLISH_CONTENT_PROVIDERS_TRANSACTION進程間通信請求,其源碼如下:

AMS#publishContentProviders()
  • 參數caller是一個類型為ApplicationThread的Binder代理對象,它引用了運行在新建應用程序進程中的一個ApplicationThread對象,getRecordForAppLocked(caller)方法通過caller來獲取一個用來描述新建應用程序進程的ProcessRecord對象r;
  • 新建應用程序進程在啟動時,會將需要在它里面運行的ContentProvider組件啟動起來!從前面的step13可知,在AMS中,這些ContentProvider組件使用一個ContentProviderRecord對象來描述,它們保存在用來描述新建應用程序進程的一個ProcessRecord對象r的一個成員變量pubProviders中;
  • 第10到14行,第一個for循環,取出providers中的每一個ContentProvider組件,并且拿到ContentProvider組件對應的ContentProviderRecord對象dst;
  • 第15到22行,第二個for循環,通過兩種方式,把ContentProviderRecord對象dst保存到全局變量mProviderMap;
  • 參數providers包含了要發布到AMS中的ContentProvider組件,每一個ContentProvider組件都使用一個ContentProviderHolder對象來描述,它里面包含了要發布的ContentProvider組件的一個IContentProvider接口,如圖第34行到40行所示!

從前面的step8可知,一個應用程序進程請求ActivityManagerService返回一個ContentProvider組件的代理對象時,如果這個ContentProvider組件還未啟動起來,那么AMS就會先創建一個新的應用程序進程來啟動該ContentProvider組件,然后再在一個while循環中等待該ContentProvider組件啟動完成,并且將他的一個代理對象發布到AMS中。

現在既然這個ContentProvider已經啟動完成,并且將它的一個代理對象,即一個類型為Transport的Binder代理對象發布到AMS,因此,前面正在等待的一個AMS線程就可以停止等待,并且將這個類型為Transport的Binder代理對象封裝成一個ContentProvider對象返回給請求它的應用程序進程。

這一步執行完畢,就會使得AMS從前面的step8返回step5,即返回到MainActivity組件所運行的應用程序進程中,即AxxApp應用程序進程!然后,繼續執行ActivityThread類成員函數installProvider(),用來保存前面從ActivityManagerService獲得的一個ContentProvider組件的一個IContentProvider訪問接口。

step26: ActivityThread#installProvider()

ActivityThread類成員函數installProvider()源碼如下:

/**
 * 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) {
        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) {
            return null;
        }
        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                            loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                return null;
            }
            // XXX Need to create the correct context for this provider.
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {......}
    } else {
        provider = holder.provider;
    }

    IActivityManager.ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                provider = pr.mProvider;
            } else {
                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 {
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                // 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 {
                        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;
}
  • 這步與前面的step20,都是在ActivityThread類成員函數installProvider()。不過,前面step20是在啟動ContentProvider組件的應用程序進程中執行的;而這步是在AxxApp應用程序的MainActivity中執行的!另一個區別是:這步得到參數provider不等于null,它用來描述一個在其他應用程序進程中啟動的ContentProvider組件的一個IContentProvider訪問接口。

  • 這步執行完成后,就返回到前面的step1,這時在AxxApp應用的MainActivity中,就獲得了與URI值對應的SubContentProvider組件的一個IContentProvider訪問接口,最后就可以通過這個接口訪問另一個應用程序的數據內容了!

0x03 參考文獻與簡單的結語


至此,ContentProvider啟動流程分析到此結束!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容