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()函式原始碼較長,我做了部分刪減後如下:
- 第10行,呼叫成員函式getRecordForAppLocked(),用來獲取發出訪問請求的應用程式所在程序的相關資訊,在我們的場景中,也就是AxxApp應用程式所在程序的相關資訊。這些資訊,用一個ProcessRecord物件 r 來描述!
- 在ActivityManagerService中,每一個已經啟動的ContentProvider元件都使用一個ContentProviderRecord物件來描述。這些ContentProviderRecord物件儲存在ActivityManagerService類的成員變數mProviderMap中!
- mProviderMap實質上是一個HashMap,可通過兩種方式進行查詢,查詢結果是一個ContentProviderRecord物件cpr:
- mProviderMap.getProviderByName()函式,通過引數name和引數userId進行查詢;引數name指向了將要啟動的ContentProvider元件的android:authorities屬性值,也就是URI值對應的許可權部分;引數userId則是呼叫方應用程式的Linux使用者ID;
- mProviderMap.getProviderByClass()函式,通過ContentProvider元件的類名進行查詢;
需要注意的是,mProvidersByName和mProvidersByClass的內部實現是雜湊表,雖然都是儲存的ContentProviderRecord物件,但是mProvidersByName變數是以ContentProvider元件的android:authorities屬性值為關鍵字的,而mProvidersByClass變數是以ContentProvider元件的類名作為關鍵字的!
第15行,首先通過引數name和userId,mProviderMap.getProviderByName()檢查是否存在對應的ContentProvider元件?接下來的邏輯很簡單,分為兩種情況:
- 第18行到27行程式碼,就是表示這個ContentProvider元件已經發布(啟動)了,此時呼叫方(AxxApp)就可以直接通過ContentProviderRecord物件cpr來建立一個ContentProviderHolder物件;需要做兩點說明:
- ContentProviderRecord物件用來記錄ContentProvider元件的釋出(啟動)資訊,通過其物件cpr的provider成員變數來記錄ContentProvider元件,它們是一對一的關係;
- ContentProviderHolder是ContentProvider元件的代理物件,它持有一個ContentProvider物件;
- 第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
(O)Telephony分析之通話流程分析(二)撥打電話流程分析(上)
撥打電話,是從通話的撥號盤開始的,而撥號盤的介面是在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啟動流程分析(二)