0x01 扯東扯西的前言&概述
本片博客對應(yīng)時序圖上的step15—26:上接第二篇博客:ContentProvider啟動流程分析二!
同時此系列博客同步在博客園發(fā)布:ContentProvider啟動流程分析系列!詳情戳這里即可訪問~
0x02 ContentProvider啟動流程分析
step15: ApplicationThread#bindApplication()
上一步,在ApplicationThreadProxy類的bindApplication()函數(shù)中,通過Binder對象mRemote發(fā)出了一個進程間通信請求!
反查源碼很容易知道,bindApplication()函數(shù),其實是IApplicationThread接口類的接口函數(shù),而ActivityThread類的內(nèi)部類ApplicationThread實現(xiàn)了這一接口,所以自然也就實現(xiàn)了接口函數(shù)bindApplication(),我們來看ApplicationThread類的函數(shù)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類的成員函數(shù)bindApplication(),就是類型為BIND_APPLICATION_TRANSACTION的進程間通信請求的真正處理者!處理的邏輯是:先把將要啟動的ContentProvider組件列表,封裝成一個AppBindData對象;
- 然后再調(diào)用外部類ActivityThread的成員函數(shù)sendMessage(),再次將這個AppBindData對象封裝成一個類型為BIND_APPLICATION的消息,以便可以發(fā)送到新建應(yīng)用程序進程的主線程的消息隊列中!
step16: ActivityThread#sendMessage()
ActivityThread類持有一個mH成員變量,mH實際上是一個Handler對象,通過mH.sendMessage()發(fā)送消息到新建應(yīng)用程序進程的主線程的消息隊列中!所以,這個消息最后是最終是在mH.handleMessage()函數(shù)中處理的。
step17: ActivityThread#handleMessage()
ActivityThread內(nèi)部類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()函數(shù)中,我們只考慮BIND_APPLICATION的情況,首先將Message對象msg的成員變量obj強轉(zhuǎn)成個AppBindData對象,它包含了要啟動的ContentProvider列表。
- 然后,調(diào)用handleBindApplication()函數(shù)把這些ContentProvider組件啟動起來!
接下來,關(guān)注handleBindApplication()函數(shù),是怎樣把這些ContentProvider組件啟動起來的?
step18: ActivityThread#handleBindApplication()
ActivityThread類的成員函數(shù)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);
......
}
}
}
參數(shù)data是一個AppBindData對象,data的成員變量providers保存了,需要在當(dāng)前進程中啟動的ContentProvider組件,接下來則會調(diào)用ActivityThread類的成員函數(shù)installContentProviders()來啟動這些組件!
step19: ActivityThread#installContentProviders()
ActivityThread類的成員函數(shù)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循環(huán)得到保存在providers中的每一個組件ProviderInfo對象cpi,然后調(diào)用installProvider()函數(shù)啟動每個cpi對應(yīng)的ContnetProvider組件;
- 這些組件啟動之后,就可以獲得其對應(yīng)的一個IContentProvider接口;然后,把這個接口封裝成一個ContentProviderHolder對象;
- 最后調(diào)用AMS代理對象(也即,ActivityManagerProxy)的成員函數(shù)publishContentProviders(),將這些ContentProviderHolder對象傳遞給AMS;AMS正是通過這些ContentProviderHolder對象獲取它所描述的ContentProvider組件的一個訪問接口;
接下來,先分析ActivityThread類成員函數(shù)installProvider()在當(dāng)前應(yīng)用程序進程中啟動一個ContentProvider組件的過程;然后,分析AMS代理對象ActivityManagerProxy成員函數(shù)publishContentProviders()將啟動完成的組件發(fā)布到AMS的過程!
step20: ActivityThread#installProvider()
ActivityThread類成員函數(shù)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為空,表示參數(shù)info所描述的ContentProvider組件需要在當(dāng)前進程中啟動
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;
}
- 對傳進來的參數(shù)holder判空,如果為null,表示要在當(dāng)前應(yīng)用程序進程中,將參數(shù)info所描述的ContentProvider組件啟動起來;另一個參數(shù)context描述了將要啟動的ContentProvider組件運行的上下文環(huán)境;
- localProvider表示一個ContentProvider組件對象,通過localProvider.getIContentProvider(),也即調(diào)用localProvider的成員函數(shù)getIContentProvider()來獲得該組件對應(yīng)的一個IContentProvider接口provider;這個IContentProvider最終需要發(fā)布到AMS,AMS會將它返回給那些需要訪問它所描述的ContentProvider組件的調(diào)用方應(yīng)用程序進程;
- 接下來通過localProvider.attachInfo()來進一步初始化前面所創(chuàng)建的ContentProvider組件;
- 接下來對全局變量mProviderMap加同步鎖,然后對localProvider判空,正常情況下此時的localProvider應(yīng)該是非空的,所以,自然要把它保存到mLocalProviders和mLocalProvidersByName中;
- 但是如果localProvider仍然為空,接下來會出現(xiàn)一個ProviderRefCount對象prc,用來表示和記錄ContentProvider組件的引用計數(shù),如果prc為空,則把這個localProvider封裝成一個ProviderClientRecord對象,并保存在prc變量中;
- 最后,返回這個ContentProviderHolder對象!
接下來,繼續(xù)分析ContentProvider組件的成員函數(shù)getIContentProvider()和成員函數(shù)attachInfo()的實現(xiàn)細節(jié)!
step21: ContentProvider#getIContnetProvider()
ContentProvider類成員函數(shù)getIContentProvider()源碼如下:
public abstract class ContentProvider implements ComponentCallbacks2 {
......
private Transport mTransport = new Transport();
......
class Transport extends ContentProviderNative {......}
......
public IContentProvider getIContentProvider() {
return mTransport;
}
......
}
- 每一個ContentProvider組件都有一個內(nèi)部類Transport,其本質(zhì)是一個Binder本地對象;并且持有一個類型為Transport的全局變量mTransPort來表示一個Binder本地對象。
- 通過將這個Binder本地對象傳遞給AMS,然后AMS會將引用了這個Binder本地對象的一個Binder代理對象返回給需要訪問該ContentProvider組件的其他應(yīng)用程序進程;這樣,其他應(yīng)用程序進程就可以通過這個Binder代理對象來間接的訪問一個ContentProvider組件中的數(shù)據(jù)了!
- ContentProvider類成員函數(shù)getIContentProvider()的實現(xiàn)很簡單,只是簡單地將全局變量mTransPort描述的一個IContentProvider接口返回給調(diào)用者。
step22: ContentProvider#attachInfo()
ContentProvider類的成員函數(shù)attachInfo(),作用是初始化前面所創(chuàng)建的一個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();
}
}
......
}
- 依然是兩個重載函數(shù),接收兩個參數(shù)的attachInfo()函數(shù)內(nèi)部調(diào)用了接收三個參數(shù)的attachInfo()函數(shù),我們直接關(guān)注接收三個參數(shù)的attachInfo()函數(shù);
- setReadPermission(info.readPermission),setWritePermission(info.writePermission)和setPathPermissions(info.pathPermissions)這三個函數(shù)的作用是,設(shè)置ContentProvider組件的讀寫權(quán)限和訪問權(quán)限;
- 最后ContentProvider.this.onCreate()函數(shù),實際調(diào)用的是COntentProvider子類的onCreate()函數(shù),以便在子類的onCreate()函數(shù)中,執(zhí)行一些業(yè)務(wù)相關(guān)的初始化操作!
- 在我們最開始的場景中,ContentProvider組件指的就是BxxApp應(yīng)用程序進程中SubContentProvider組件,所以上面調(diào)用的時加上就是SubContentProvider類的onCreate()函數(shù),而我們的業(yè)務(wù)邏輯相關(guān)的一些初始化工作,也正是放在SubContentProvider類的onCreate()函數(shù)中執(zhí)行的!
step23: SubContentProvider#onCreate()
public class SubContentProvider extends ContentProvider {
......
@Override
public boolean onCreate() {
ContentResolver resolver = getContext().getContentResolver();
DatabaseHelper helper = new DtatabaseHelper(......);
......
return true;
}
......
}
- 需要注意,由于一個ContentProvider組件再啟動過程中需要執(zhí)行onCreate()函數(shù),因此,我們應(yīng)該避免在onCeate()方法中執(zhí)行耗時操作,例如和IO相關(guān)的操作,否則可能造成這個ContentProvider組件啟動超時!
這一步執(zhí)行完成后,就會返回到前面的step20,然后再次返回到前面的step19,即ActivityThread類成員函數(shù)installContentProviders()中,接下來就會調(diào)用AMS代理對象的成員函數(shù)publishContentProviders(),也即ActivityManagerProxy的成員函數(shù)publishContentProviders(),將前面所有啟動的ContentProvider組件的一個IContentProvider訪問接口發(fā)布到ActivityManagerService中~
step24: ActivityManagerProxy#publishContentProviders()
ActivityManagerProxy類成員函數(shù)publishContentProviders(),會將所有啟動的ContentProvider組件的一個IContentProvider訪問接口發(fā)布到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();
}
- 先把接收的參數(shù)保存到Parcel對象data中;
- 接著再通過ActivityManagerProxy類內(nèi)部的一個Binder代理對象mRemote向ActivityMnagerService發(fā)送一個類型為PUBLISH_CONTENT_PROVIDERS_TRANSACTION進程間通信請求;
step25: ActivityManagerService#publishContentProviders()
以上10步都是在新建的應(yīng)用程序進程中執(zhí)行的!step25是在ActivityManagerService中執(zhí)行的,AMS類成員函數(shù)publishContentProviders(),用來處理類型為PUBLISH_CONTENT_PROVIDERS_TRANSACTION進程間通信請求,其源碼如下:
- 參數(shù)caller是一個類型為ApplicationThread的Binder代理對象,它引用了運行在新建應(yīng)用程序進程中的一個ApplicationThread對象,getRecordForAppLocked(caller)方法通過caller來獲取一個用來描述新建應(yīng)用程序進程的ProcessRecord對象r;
- 新建應(yīng)用程序進程在啟動時,會將需要在它里面運行的ContentProvider組件啟動起來!從前面的step13可知,在AMS中,這些ContentProvider組件使用一個ContentProviderRecord對象來描述,它們保存在用來描述新建應(yīng)用程序進程的一個ProcessRecord對象r的一個成員變量pubProviders中;
- 第10到14行,第一個for循環(huán),取出providers中的每一個ContentProvider組件,并且拿到ContentProvider組件對應(yīng)的ContentProviderRecord對象dst;
- 第15到22行,第二個for循環(huán),通過兩種方式,把ContentProviderRecord對象dst保存到全局變量mProviderMap;
- 參數(shù)providers包含了要發(fā)布到AMS中的ContentProvider組件,每一個ContentProvider組件都使用一個ContentProviderHolder對象來描述,它里面包含了要發(fā)布的ContentProvider組件的一個IContentProvider接口,如圖第34行到40行所示!
從前面的step8可知,一個應(yīng)用程序進程請求ActivityManagerService返回一個ContentProvider組件的代理對象時,如果這個ContentProvider組件還未啟動起來,那么AMS就會先創(chuàng)建一個新的應(yīng)用程序進程來啟動該ContentProvider組件,然后再在一個while循環(huán)中等待該ContentProvider組件啟動完成,并且將他的一個代理對象發(fā)布到AMS中。
現(xiàn)在既然這個ContentProvider已經(jīng)啟動完成,并且將它的一個代理對象,即一個類型為Transport的Binder代理對象發(fā)布到AMS,因此,前面正在等待的一個AMS線程就可以停止等待,并且將這個類型為Transport的Binder代理對象封裝成一個ContentProvider對象返回給請求它的應(yīng)用程序進程。
這一步執(zhí)行完畢,就會使得AMS從前面的step8返回step5,即返回到MainActivity組件所運行的應(yīng)用程序進程中,即AxxApp應(yīng)用程序進程!然后,繼續(xù)執(zhí)行ActivityThread類成員函數(shù)installProvider(),用來保存前面從ActivityManagerService獲得的一個ContentProvider組件的一個IContentProvider訪問接口。
step26: ActivityThread#installProvider()
ActivityThread類成員函數(shù)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類成員函數(shù)installProvider()。不過,前面step20是在啟動ContentProvider組件的應(yīng)用程序進程中執(zhí)行的;而這步是在AxxApp應(yīng)用程序的MainActivity中執(zhí)行的!另一個區(qū)別是:這步得到參數(shù)provider不等于null,它用來描述一個在其他應(yīng)用程序進程中啟動的ContentProvider組件的一個IContentProvider訪問接口。
這步執(zhí)行完成后,就返回到前面的step1,這時在AxxApp應(yīng)用的MainActivity中,就獲得了與URI值對應(yīng)的SubContentProvider組件的一個IContentProvider訪問接口,最后就可以通過這個接口訪問另一個應(yīng)用程序的數(shù)據(jù)內(nèi)容了!
0x03 參考文獻與簡單的結(jié)語
至此,ContentProvider啟動流程分析到此結(jié)束!