1. 程式人生 > >ContentProvider啟動流程分析(二)

ContentProvider啟動流程分析(二)

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

## 0x02 ContentProvider啟動流程分析

step6: ActivityManagerProxy#getContentProvider()

代理類ActivityManagerProxy位於ActivityManagerNative.java檔案中,其getContentProvider()成員函式的原始碼如下:

class ActivityManagerProxy implements IActivityManager {
    ....
    public ContentProviderHolder getContentProvider(IApplicationThread caller,
            String name, int userId, boolean stable) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(name);
        data.writeInt(userId);
        data.writeInt(stable ? 1 : 0);
        mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        ContentProviderHolder cph = null;
        if (res != 0) {
            cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
        }
        data.recycle();
        reply.recycle();
        return cph;
    }
}
  • getContentProvider()函式中,前面7行程式碼,將傳進來的引數封裝到一個Parcel物件中;
  • 然後再通過ActivityManagerProxy內部的一個Binder代理物件mRemote,向ActivityManagerService傳送一個型別為GET_CONTENT_PROVIDER_TRANSACTION的程序間通訊的請求;
  • 最後,再將ActivityManagerService返回的物件封裝成一個ContentProviderHolder物件返回給呼叫者。

需要注意:以上step1—6是在應用程式AxxApp所在程序中執行的;接下來的step7—9在ActivityManagerService中執行,主要用來處理MainActivity元件發出的型別為GET_CONTENT_PROVIDER_TRANSACTION的程序間通訊的請求!

step7: ActivityManagerService#getContentProvider()

ActivityManagerService類的成員函式getContentProvider()原始碼如下:

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    ....
    @Override
    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        ....
        return getContentProviderImpl(caller, name, null, stable, userId);
    }
    ....
}
  • 成員函式getContentProvider(),是用來處理型別為 GET_CONTENT_PROVIDER_TRANSACTION 的程序間通訊的請求的;
  • 其內部直接呼叫getContentProviderImpl(),來進一步獲得引數name對應的ContentProviderHolder物件。

step8: ActivityManagerService#getContentImpl()

getCOntentImpl()函式的作用是,獲得引數name對應的ContentProviderHolder物件,這個物件用來描述一個ContentProvider元件的代理物件;getCOntentImpl()函式原始碼較長,我做了部分刪減後如下:

AMS#getContentProviderImpl()

  • 第10行,呼叫成員函式getRecordForAppLocked(),用來獲取發出訪問請求的應用程式所在程序的相關資訊,在我們的場景中,也就是AxxApp應用程式所在程序的相關資訊。這些資訊,用一個ProcessRecord物件 r 來描述!
  • 在ActivityManagerService中,每一個已經啟動的ContentProvider元件都使用一個ContentProviderRecord物件來描述。這些ContentProviderRecord物件儲存在ActivityManagerService類的成員變數mProviderMap中!
  • mProviderMap實質上是一個HashMap,可通過兩種方式進行查詢,查詢結果是一個ContentProviderRecord物件cpr:
    1. mProviderMap.getProviderByName()函式,通過引數name和引數userId進行查詢;引數name指向了將要啟動的ContentProvider元件的android:authorities屬性值,也就是URI值對應的許可權部分;引數userId則是呼叫方應用程式的Linux使用者ID;
    2. mProviderMap.getProviderByClass()函式,通過ContentProvider元件的類名進行查詢;

需要注意的是,mProvidersByName和mProvidersByClass的內部實現是雜湊表,雖然都是儲存的ContentProviderRecord物件,但是mProvidersByName變數是以ContentProvider元件的android:authorities屬性值為關鍵字的,而mProvidersByClass變數是以ContentProvider元件的類名作為關鍵字的!

第15行,首先通過引數name和userId,mProviderMap.getProviderByName()檢查是否存在對應的ContentProvider元件?接下來的邏輯很簡單,分為兩種情況:

  1. 第18行到27行程式碼,就是表示這個ContentProvider元件已經發布(啟動)了,此時呼叫方(AxxApp)就可以直接通過ContentProviderRecord物件cpr來建立一個ContentProviderHolder物件;需要做兩點說明:
    • ContentProviderRecord物件用來記錄ContentProvider元件的釋出(啟動)資訊,通過其物件cpr的provider成員變數來記錄ContentProvider元件,它們是一對一的關係;
    • ContentProviderHolder是ContentProvider元件的代理物件,它持有一個ContentProvider物件;
  2. 第29到93行程式碼,就是表示這個ContentProvider元件還沒有釋出,需要進一步驗證,第31行到35行通過mProviderMap.getProviderByClass()方式進行二次查詢。如果還是沒有找到?接下來就要啟動這個ContentProvider元件了!
    • 第37到43行,先根據name引數和userId引數建立一個ContentProviderRecord物件;
    • 在啟動cpr描述的ContentProvider元件之前,先判斷這個COntentProvider元件的android:multiprocess屬性值!如果為true,表示這個ContentProvider元件需要在訪問它的應用程式程序中啟動;為false表示需要建立第三方程序並在第三方程序中啟動這個ContentProvider元件;也即第44到53行;
    • 第57行,關注常量mLaunchingProviders,ActivityManagewrService就是把正在啟動的ContentProvider元件儲存在mLaunchingProviders中;
    • 第59到65行,通過for迴圈判斷目標ContentProvider元件的狀態是否處於正在啟動中。如果是,則等待它繼續啟動完成;
    • 如果不是,即66到83行,ActivityManagerService會呼叫成員函式startProcessLocked()新建一個應用程式程序,專門用來啟動目標ContnrtProvider元件
    • 第85到89行,分別通過兩種方式mProviderMap.putProviderByClass()和mProviderMap.putProviderByName()把啟動的ContentProvider元件儲存在mProviderMap變數中;
    • 最後,第93到99行,等待ContentProvider元件釋出完成後,直接返回其對應的ContentProviderRecord物件即可;

step9: ActivityMangerService#startProcessLocked()

在step8中,目標ContentProvider元件是通過一個新建的第三方應用程式程序來啟動!而這個第三方應用程式程序,就是通過ActivityMangerService的成員函式startProcessLocked()新建的。它主要通過呼叫Process的靜態成員函式start()建立一個新的應用程式程序,並且把這個新建的應用程式程序的入口設定為ActivityThread類的靜態成員函式main()!

以上三步都是在ActivityManagerService類中執行的!接下來我們分析新建立的應用程式程序,啟動ContentProvider元件的過程,所以step10和step11是在第三方應用程式程序中完成的。

step10: ActivityThread#main()

ActivityThread.main()函式,會在新建立的應用程式程序中,建立一個ActivityThread物件和一個ApplicationThread物件,然後呼叫ActivityManagerProxy.attchApplication()函式,將ApplicationThread物件傳遞給ActivityManagerService!

step11: ActivityManagerProxy#attchApplication()

ActivityManagerProxy.attchApplication()函式,先向ActivityManagerService發出一個型別為ATTACH_APPLICATION_TRANSACTION的程序間通訊請求,這個請求通過Binder機制處理!然後,就是把step10建立的ApplicationThread物件傳遞給ActivityManagerService!

接下來step12—14又回到ActivityManagerService中執行!主要用來處理新建應用程式程序發出的型別為ATTACH_APPLICATION_TRANSACTION的程序間通訊請求。

step12: ActivityManagerService#attachApplication()

ActivityManagerService.attachApplication()函式,它呼叫了ApplicationManagerService類的成員函式attachApplicationLocked()來處理型別為ATTACH_APPLICATION_TRANSACTION的程序間通訊請求,用來執行啟動目標ContentProvider元件的操作!

接下來重點看看ApplicationManagerService類的成員函式attachApplicationLocked()!

step13: ApplicationManagerService#attachApplicationLocked()

ApplicationManagerService類的成員函式attachApplicationLocked()較長,刪減後如下:

private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
    // Find the application record that is being attached...  either via
    // the pid if we are running in multiple processes, or just pull the
    // next app record if we are emulating process with anonymous threads
    ProcessRecord app;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid);
        }
    } else {app = null;}

    try {
        AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
    }
    
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    try {
        ProfilerInfo profilerInfo = profileFile == null ? null :
                                    new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
        thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                               profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                               app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                               isRestrictedBackupMode || !normalMode, app.persistent,
                               new Configuration(mConfiguration), app.compat,
                               getCommonServicesLocked(app.isolated),
                               mCoreSettingsObserver.getCoreSettingsLocked());
    }
    return true;
}
  • 注意到,引數pid指向前面所建立的應用程式程序PID,也即第三方應用程序PID,ProcessRecord物件app就是用來記錄新建應用的程序資訊的。
  • 在前面的step8中,ActivityManagerService以這個PID為關鍵字將一個ProcessRecord物件儲存在了mPidsSelfLocked變數中,這裡,首先通過引數pid取回ProcessRecord物件並儲存在區域性變數app中,然後呼叫generateApplicationProvidersLocked(app)函式需要在app所描述的第三方程序中啟動的ContentProvider元件!

generateApplicationProvidersLocked()函式原始碼如下:

private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord       app) {
    List<ProviderInfo> providers = null;
    try {
        ParceledListSlice<ProviderInfo> slice = AppGlobals.getPackageManager().
            queryContentProviders(app.processName, app.uid,
                    STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
        providers = slice != null ? slice.getList() : null;
    }
    int userId = app.userId;
    if (providers != null) {
        int N = providers.size();
        app.pubProviders.ensureCapacity(N + app.pubProviders.size());
        for (int i=0; i<N; i++) {
            ProviderInfo cpi =
                (ProviderInfo)providers.get(i);
            boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                    cpi.name, cpi.flags);
            if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_OWNER) {
                // This is a singleton provider, but a user besides the
                // default user is asking to initialize a process it runs
                // in...  well, no, it doesn't actually run in this process,
                // it runs in the process of the default user.  Get rid of it.
                providers.remove(i);
                N--;
                i--;
                continue;
            }

            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
            if (cpr == null) {
                cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
                mProviderMap.putProviderByClass(comp, cpr);
            }
            app.pubProviders.put(cpi.name, cpr);
            if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
                // Don't add this if it is a platform component that is marked
                // to run in multiple processes, because this is actually
                // part of the framework so doesn't make sense to track as a
                // separate apk in the process.
                app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode, mProcessStats);
            }
            ensurePackageDexOpt(cpi.applicationInfo.packageName);
        }
    }
    return providers;
}
  • 先去PackageMangerService找目標ContentProvider元件,並儲存到列表providers中;
  • 然後,for迴圈檢查ActivityManagerService是否已經為這些ContentProvider元件建立過對應的ContnetProviderRecord物件來記錄每一個ContentProvider元件;
  • 如果沒有,則分別為它們建立一個ContentProviderRecord物件,並以它們的類名作為關鍵字儲存在AMS的全域性變數mProviderMap中!

從這裡可以看出,一個應用程式程序在啟動的時候,會將它所需要的所有ContentProvider元件全部啟動起來!

最後,繼續關注thread.bindApplication()函式,也即ActivityThread.bindApplication()函式!

step14: ActivityThreadProxy#bindApplication()

ActivityThreadProxy.bindApplication()函式原始碼如下:

public final void bindApplication(String packageName, ApplicationInfo info,
        List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo,
        Bundle testArgs, IInstrumentationWatcher testWatcher,
        IUiAutomationConnection uiAutomationConnection, int debugMode,
        boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
        Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
        Bundle coreSettings) throws RemoteException {
    Parcel data = Parcel.obtain();
    data.writeInterfaceToken(IApplicationThread.descriptor);
    data.writeString(packageName);
    info.writeToParcel(data, 0);
    data.writeTypedList(providers);
    if (testName == null) {
        data.writeInt(0);
    } else {
        data.writeInt(1);
        testName.writeToParcel(data, 0);
    }
    if (profilerInfo != null) {
        data.writeInt(1);
        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        data.writeInt(0);
    }
    data.writeBundle(testArgs);
    data.writeStrongInterface(testWatcher);
    data.writeStrongInterface(uiAutomationConnection);
    data.writeInt(debugMode);
    data.writeInt(openGlTrace ? 1 : 0);
    data.writeInt(restrictedBackupMode ? 1 : 0);
    data.writeInt(persistent ? 1 : 0);
    config.writeToParcel(data, 0);
    compatInfo.writeToParcel(data, 0);
    data.writeMap(services);
    data.writeBundle(coreSettings);
    mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,IBinder.FLAG_ONEWAY);
    data.recycle();
}
  • 先建立一個Parcel物件data用來儲存接收的引數;
  • 然後,mRemote是ActivityThreadProxy內部的一個Binder代理物件,通過mRemote向新建的應用程式程序(也即,第三方程序)傳送一個型別為BIND_APPLICATION_TRANSACTION的程序間通訊請求。

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

以上三步都是在ActivityManagerService中執行的,接下來的step15—24在新建的應用程式程序中執行,主要用來處理型別為BIND_APPLICATION_TRANSACTION的程序間通訊請求!對應時序圖上的step15—24

相關推薦

ContentProvider啟動流程分析

## 0x01 扯東扯西的前言&概述 ## 0x02 ContentProvider啟動流程分析 step6: ActivityManagerProxy#getContentProvider() 代理類ActivityManagerProxy位於ActivityManagerNative.j

Android9.0 Activity啟動流程分析

文章目錄 1、ActivityThread的main函式 2. AMS的attachApplication函式 2.1 Part-I 2.2 Part-II 2.2.1 ApplicationThread的bindApp

ContentProvider啟動流程分析

## 0x01 扯東扯西的前言&概述 ## 0x02 ContentProvider啟動流程分析 step15: ApplicationThread#bindApplication() 上一步,在ApplicationThreadProxy類的bindApplication()函式中,通過B

ContentProvider啟動流程分析

## 0x01 扯東扯西的前言&概述 作為安卓設計的四大元件之一,是跨程序共享資料的一把利器,所謂跨程序共享資料,通俗理解就是,應用程式A可以訪問操作應用程式B共享出來的資料,這些共享出來的資料一般都有其對應的URI(統一資源識別符號),那麼就涉及到兩個過程: 提供資料內容的過程: A應用(

springboot啟動流程分析

現在繼續看啟動過程的詳情,詳細描述下SpringApplication建構函式: 1.載入過程中的SpringApplication初始化如下: public SpringApplication(ResourceLoader resourceLoader

SpringBoot啟動流程分析:SpringApplication的run方法

SpringBoot系列文章簡介 SpringBoot原始碼閱讀輔助篇:   Spring IoC容器與應用上下文的設計與實現 SpringBoot啟動流程原始碼分析: SpringBoot啟動流程分析(一):SpringApplication類初始化過程 SpringBoot啟動流程分析(二)

AliOS Things的啟動過程分析

AliOS Things的啟動過程分析(二) 在AliOS Things的啟動過程分析(一)中分析了developerkit從系統上電到呼叫main函式所經歷的一些步驟,接下來詳細分析一下main函式的一些工作,主要是核心的相關初始化工作。main函式所處的位置位於    

Android9.0 Activity啟動流程分析

1、ActivityRecord、TaskRecord、ActivityStack和ActivityDisplay介紹   本篇文章是基於Android refs/tags/android-9.0.0_r8分支的程式碼進行分析的   在分析Activity啟動的原始碼之前先介紹一下Act

HBase原始碼分析之HRegion上compact流程分析

  2016年03月03日 21:38:04 辰辰爸的技術部落格 閱讀數:2767 版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/lipeng_bigdata/article/details/50791205

dart 非同步事件執行流程分析

// use two list to test the async envet exe order. // one record the emitted order; // and the other record the captured order; import 'dart:

nu-lb-nuc140 RTX 流程 分析

0 參考資料 http://www.stmcu.org.cn/module/forum/thread-605101-1-1.html 【安富萊】【RTX作業系統教程】第18章 記憶體管理 1 巨集定義 __TARGET_ARCH_6S_M __USE_EXCLUSIVE_AC

springboot啟動流程分析

以springboot 2.0.2.RELEASE版本為例 1.pom.xml引入依賴 <parent> <groupId>org.springframework.boot</groupId>

Oracle11g RAC 啟動流程梳理OHASD簡析和啟停實驗

簡單說明: 11gRAC啟動分為四個層次,第一個層次是OHASD和子代理程序啟動: init——>init.ohasd——>ohasd——>agent子程序啟動 即: OS啟動——>/etc/rc.d/init.d/init.o

OTelephony分析之通話流程分析撥打電話流程分析

撥打電話,是從通話的撥號盤開始的,而撥號盤的介面是在DialpadFragment,因此,需要從這個地方進行分析一.撥號盤介面撥號流程 public void onClick(View view) { ...... if (resId == R.id.dia

[Android6.0][RK3399] 雙屏異顯程式碼實現流程分析

Platform: RK3399 OS: Android 6.0 Version: v2016.08 LCD interface: eDP + mipi Patch Code Date: Fri, 09 Dec 2016 10:53:11

Android 呼吸燈流程分析

一、Android呼吸燈Driver實現       1、註冊驅動       程式碼位置:mediatek/kernel/drivers/leds/leds_drv.c 602static struct platform_driver mt65xx_leds_drive

Quartz原始碼——scheduler.start()啟動原始碼分析

scheduler.start()是Quartz的啟動方式!下面進行分析,方便自己檢視! 我都是分析的jobStore 方式為jdbc的SimpleTrigger!RAM的方式類似分析方式! 解釋: {0} : 表的字首 ,如表qrtz_

Android OTA升級原理和流程分析---update.zip差分包問題的解決

Android OTA升級原理和流程分析(二)—update.zip差分包問題的解決 在上一篇末尾提到的生成差分包時出現的問題,現已解決,由於最近比較忙,相隔的時間也比較長,所以單列一個篇幅提示大家。這個問題居然是原始碼中的問題,可能你已經制作成功了,不過我的

electron-vue架構解析3-開發環境啟動流程分析

上一節我們介紹了生產環境的打包流程,這一節我們來看開發環境的啟動流程。 該框架主要修改是對開發環境的優化,包括了於開發環境的配置檔案隔離,主程序和渲染程序配置檔案隔離,編譯過程提示等功能,因此這一節內容才是整個框架的核心。 我們從開發人員用到的啟動命令

SpringBoot啟動流程分析:SpringApplication類初始化過程

SpringBoot系列文章簡介 SpringBoot原始碼閱讀輔助篇:   Spring IoC容器與應用上下文的設計與實現 SpringBoot啟動流程原始碼分析: SpringBoot啟動流程分析(一):SpringApplication類初始化過程 SpringBoot啟動流程分析(二)