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進程間通信請求,其源碼如下:
- 參數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啟動流程分析到此結束!