通過前面的學(xué)習(xí),我們知道在Android系統(tǒng)中,Content Provider可以為不同的應(yīng)用程序訪問相同的數(shù)據(jù)提供統(tǒng)一的入口。Content Provider一般是運(yùn)行在獨(dú)立的進(jìn)程中的,每一個(gè)Content Provider在系統(tǒng)中只有一個(gè)實(shí)例存在,其它應(yīng)用程序首先要找到這個(gè)實(shí)例,然后才能訪問它的數(shù)據(jù)。那么,系統(tǒng)中的Content Provider實(shí)例是由誰來負(fù)責(zé)啟動(dòng)的呢?本文將回答這個(gè)問題。
Content Provider和應(yīng)用程序組件Activity、Service一樣,需要在AndroidManifest.xml文件中配置之后才能使用。系統(tǒng)在安裝包含Content Provider的應(yīng)用程序的時(shí)候,會(huì)把這些Content Provider的描述信息保存起來,其中最重要的就是Content Provider的Authority信息,Android應(yīng)用程序的安裝過程具體可以參考Android應(yīng)用程序安裝過程源代碼分析一文。注意,安裝應(yīng)用程序的時(shí)候,并不會(huì)把相應(yīng)的Content Provider加載到內(nèi)存中來,系統(tǒng)采取的是懶加載的機(jī)制,等到第一次要使用這個(gè)Content Provider的時(shí)候,系統(tǒng)才會(huì)把它加載到內(nèi)存中來,下次再要使用這個(gè)Content Provider的時(shí)候,就可以直接返回了。
本文以前面一篇文章Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例中的例子來詳細(xì)分析Content Provider的啟動(dòng)過程。在Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例這篇文章介紹的應(yīng)用程序Article中,第一次使用ArticlesProvider這個(gè)Content Provider的地方是ArticlesAdapter類的getArticleCount函數(shù),因?yàn)镸ainActivity要在ListView中顯示文章信息列表時(shí), 首先要知道ArticlesProvider中的文章信息的數(shù)量。從ArticlesAdapter類的getArticleCount函數(shù)調(diào)用開始,一直到ArticlesProvider類的onCreate函數(shù)被調(diào)用,就是ArticlesProvider的完整啟動(dòng)過程,下面我們就先看看這個(gè)過程的序列圖,然后再詳細(xì)分析每一個(gè)步驟:

Step 1. ArticlesAdapter.getArticleCount
這個(gè)函數(shù)定義在前面一篇文章Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例介紹的應(yīng)用程序Artilce源代碼工程目錄下,在文件為packages/experimental/Article/src/shy/luo/article/ArticlesAdapter.java中:
public class ArticlesAdapter {
......
private ContentResolver resolver = null;
public ArticlesAdapter(Context context) {
resolver = context.getContentResolver();
}
......
public int getArticleCount() {
int count = 0;
try {
IContentProvider provider = resolver.acquireProvider(Articles.CONTENT_URI);
Bundle bundle = provider.call(Articles.METHOD_GET_ITEM_COUNT, null, null);
count = bundle.getInt(Articles.KEY_ITEM_COUNT, 0);
} catch(RemoteException e) {
e.printStackTrace();
}
return count;
}
......
}
這個(gè)函數(shù)通過應(yīng)用程序上下文的ContentResolver接口resolver的acquireProvider函數(shù)來獲得與Articles.CONTENT_URI對(duì)應(yīng)的Content Provider對(duì)象的IContentProvider接口。常量Articles.CONTENT_URI是在應(yīng)用程序ArticlesProvider中定義的,它的值為“content://shy.luo.providers.articles/item”,對(duì)應(yīng)的Content Provider就是ArticlesProvider了。
Step 2. ContentResolver.acqireProvider
這個(gè)函數(shù)定義在frameworks/base/core/java/android/content/ContentResolver.java文件中:
public abstract class ContentResolver {
......
public final IContentProvider acquireProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
return acquireProvider(mContext, uri.getAuthority());
}
return null;
}
......
}
函數(shù)首先驗(yàn)證參數(shù)uri的scheme是否正確,即是否是以content://開頭,然后取出它的authority部分,最后調(diào)用另外一個(gè)成員函數(shù)acquireProvider執(zhí)行獲取ContentProvider接口的操作。在我們這個(gè)情景中,參數(shù)uri的authority的內(nèi)容便是“shy.luo.providers.articles”了。
從ContentResolver類的定義我們可以看出,它是一個(gè)抽象類,兩個(gè)參數(shù)版本的acquireProvider函數(shù)是由它的子類來實(shí)現(xiàn)的。回到Step 1中,這個(gè)ContentResolver接口是通過應(yīng)用程序上下文Context對(duì)象的getContentResolver函數(shù)來獲得的,而應(yīng)用程序上下文Context是由ContextImpl類來實(shí)現(xiàn)的,它定義在frameworks/base/core/java/android/app/ContextImpl.java文件中:
class ContextImpl extends Context {
......
private ApplicationContentResolver mContentResolver;
......
final void init(LoadedApk packageInfo,
IBinder activityToken, ActivityThread mainThread,
Resources container) {
......
mContentResolver = new ApplicationContentResolver(this, mainThread);
......
}
......
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
......
}
ContextImpl類的init函數(shù)是在應(yīng)用程序啟動(dòng)的時(shí)候調(diào)用的,具體可以參考
Android應(yīng)用程序啟動(dòng)過程源代碼分析一文中的Step 34。
因此,在上面的ContentResolver類的acquireProvider函數(shù)里面接下來要調(diào)用的ApplicationContentResolver類的acquireProvider函數(shù)。
Step 3. ApplicationContentResolve.acquireProvider
這個(gè)函數(shù)定義在frameworks/base/core/java/android/app/ContextImpl.java文件中:
class ContextImpl extends Context {
......
private static final class ApplicationContentResolver extends ContentResolver {
......
@Override
protected IContentProvider acquireProvider(Context context, String name) {
return mMainThread.acquireProvider(context, name);
}
......
private final ActivityThread mMainThread;
}
......
}
它調(diào)用ActivityThread類的acquireProvider函數(shù)進(jìn)一步執(zhí)行獲取Content Provider接口的操作。
Step 4. ActivityThread.acquireProvider
這個(gè)函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
public final IContentProvider acquireProvider(Context c, String name) {
IContentProvider provider = getProvider(c, name);
if(provider == null)
return null;
......
return provider;
}
......
}
它又是調(diào)用了另外一個(gè)成員函數(shù)getProvider來進(jìn)一步執(zhí)行獲取Content Provider接口的操作。
Step 5. ActivityThread.getProvider
這個(gè)函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
private final IContentProvider getExistingProvider(Context context, String name) {
synchronized(mProviderMap) {
final ProviderClientRecord pr = mProviderMap.get(name);
if (pr != null) {
return pr.mProvider;
}
return null;
}
}
......
private final IContentProvider getProvider(Context context, String name) {
IContentProvider existing = getExistingProvider(context, name);
if (existing != null) {
return existing;
}
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), name);
} catch (RemoteException ex) {
}
IContentProvider prov = installProvider(context, holder.provider,
holder.info, true);
......
return prov;
}
......
}
這個(gè)函數(shù)首先會(huì)通過getExistingProvider函數(shù)來檢查本地是否已經(jīng)存在這個(gè)要獲取的ContentProvider接口,如果存在,就直接返回了。本地已經(jīng)存在的ContextProvider接口保存在ActivityThread類的mProviderMap成員變量中,以ContentProvider對(duì)應(yīng)的URI的authority為鍵值保存。在我們這個(gè)情景中,因?yàn)槭堑谝淮握{(diào)用ArticlesProvider接口,因此,這時(shí)候通過getExistingProvider函數(shù)得到的IContentProvider接口為null,于是下面就會(huì)調(diào)用ActivityManagerService服務(wù)的getContentProvider接口來獲取一個(gè)ContentProviderHolder對(duì)象holder,這個(gè)對(duì)象就包含了我們所要獲取的ArticlesProvider接口,在將這個(gè)接口返回給調(diào)用者之后,還會(huì)調(diào)用installProvider函數(shù)來把這個(gè)接口保存在本地中,以便下次要使用這個(gè)ContentProvider接口時(shí),直接就可以通過getExistingProvider函數(shù)獲取了。
我們先進(jìn)入到ActivityManagerService服務(wù)的getContentProvider函數(shù)中看看它是如何獲取我們所需要的ArticlesProvider接口的,然后再返回來看看installProvider函數(shù)的實(shí)現(xiàn)。
Step 6. ActivityManagerService.getContentProvider
這個(gè)函數(shù)定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name) {
......
return getContentProviderImpl(caller, name);
}
......
}
它調(diào)用getContentProviderImpl函數(shù)來進(jìn)一步執(zhí)行操作。
Step 7. ActivityManagerService.getContentProviderImpl
這個(gè)函數(shù)定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
private final ContentProviderHolder getContentProviderImpl(
IApplicationThread caller, String name) {
ContentProviderRecord cpr;
ProviderInfo cpi = null;
synchronized(this) {
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
......
}
// First check if this content provider has been published...
cpr = mProvidersByName.get(name);
if (cpr != null) {
......
} else {
try {
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
}
......
}
cpr = mProvidersByClass.get(cpi.name);
final boolean firstClass = cpr == null;
if (firstClass) {
try {
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS);
......
cpr = new ContentProviderRecord(cpi, ai);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
}
if (r != null && cpr.canRunHere(r)) {
// If this is a multiprocess provider, then just return its
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
return cpr;
}
......
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
final int N = mLaunchingProviders.size();
int i;
for (i=0; i<N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// If the provider is not already being launched, then get it
// started.
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
ProcessRecord proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false);
......
mLaunchingProviders.add(cpr);
......
}
// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
mProvidersByClass.put(cpi.name, cpr);
}
cpr.launchingApp = proc;
mProvidersByName.put(name, cpr);
......
}
// Wait for the provider to be published...
synchronized (cpr) {
while (cpr.provider == null) {
......
try {
cpr.wait();
} catch (InterruptedException ex) {
}
}
}
return cpr;
}
......
}
這個(gè)函數(shù)比較長(zhǎng),我們一步一步地分析。
函數(shù)首先是獲取調(diào)用者的進(jìn)程記錄塊信息:
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
......
}
在我們這個(gè)情景中,要獲取的就是應(yīng)用程序Article的進(jìn)程記錄塊信息了,后面會(huì)用到。
在ActivityManagerService中,有兩個(gè)成員變量是用來保存系統(tǒng)中的Content Provider信息的,一個(gè)是mProvidersByName,一個(gè)是mProvidersByClass,前者是以Content Provider的authoriry值為鍵值來保存的,后者是以Content Provider的類名為鍵值來保存的。一個(gè)Content Provider可以有多個(gè)authority,而只有一個(gè)類來和它對(duì)應(yīng),因此,這里要用兩個(gè)Map來保存,這里為了方便根據(jù)不同條件來快速查找而設(shè)計(jì)的。下面的代碼就是用來檢查要獲取的Content Provider是否已經(jīng)加存在的了:
// First check if this content provider has been published...
cpr = mProvidersByName.get(name);
if (cpr != null) {
......
} else {
try {
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
}
......
}
cpr = mProvidersByClass.get(cpi.name);
final boolean firstClass = cpr == null;
if (firstClass) {
try {
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS);
......
cpr = new ContentProviderRecord(cpi, ai);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
}
在我們這個(gè)情景中,由于是第一次調(diào)用ArticlesProvider接口,因此,在mProvidersByName和mProvidersByClass兩個(gè)Map中都不存在ArticlesProvider的相關(guān)信息,因此,這里會(huì)通過AppGlobals.getPackageManager函數(shù)來獲得PackageManagerService服務(wù)接口,然后分別通過它的resolveContentProvider和getApplicationInfo函數(shù)來分別獲取ArticlesProvider應(yīng)用程序的相關(guān)信息,分別保存在cpi和cpr這兩個(gè)本地變量中。這些信息都是在安裝應(yīng)用程序的過程中保存下來的,具體可以參考
Android應(yīng)用程序安裝過程源代碼分析一文。
接下去這個(gè)代碼判斷當(dāng)前要獲取的Content Provider是否允許在客戶進(jìn)程中加載,即查看一個(gè)這個(gè)Content Provider否配置了multiprocess屬性為true,如果允許在客戶進(jìn)程中加載,就直接返回了這個(gè)Content Provider的信息了:
if (r != null && cpr.canRunHere(r)) {
// If this is a multiprocess provider, then just return its
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
return cpr;
}
在我們這個(gè)情景中,要獲取的ArticlesProvider設(shè)置了要在獨(dú)立的進(jìn)程中運(yùn)行,因此,繼續(xù)往下執(zhí)行:
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
final int N = mLaunchingProviders.size();
int i;
for (i=0; i<N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
系統(tǒng)中所有正在加載的Content Provider都保存在mLaunchingProviders成員變量中。在加載相應(yīng)的Content Provider之前,首先要判斷一下它是可否正在被其它應(yīng)用程序加載,如果是的話,就不用重復(fù)加載了。在我們這個(gè)情景中,沒有其它應(yīng)用程序也正在加載ArticlesProvider這個(gè)Content Provider,繼續(xù)往前執(zhí)行:
// If the provider is not already being launched, then get it
// started.
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
ProcessRecord proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false);
......
mLaunchingProviders.add(cpr);
......
}
這里的條件i >= N為true,就表明沒有其它應(yīng)用程序正在加載這個(gè)Content Provider,因此,就要調(diào)用startProcessLocked函數(shù)來啟動(dòng)一個(gè)新的進(jìn)程來加載這個(gè)Content Provider對(duì)應(yīng)的類了,然后把這個(gè)正在加載的信息增加到mLaunchingProviders中去。我們先接著分析這個(gè)函數(shù),然后再來看在新進(jìn)程中加載Content Provider的過程,繼續(xù)往下執(zhí)行:
// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
mProvidersByClass.put(cpi.name, cpr);
}
cpr.launchingApp = proc;
mProvidersByName.put(name, cpr);
這段代碼把這個(gè)Content Provider的信息分別保存到mProvidersByName和mProviderByCalss兩個(gè)Map中去,以方便后續(xù)查詢。
因?yàn)槲覀冃枰@取的Content Provider是在新的進(jìn)程中加載的,而getContentProviderImpl這個(gè)函數(shù)是在系統(tǒng)進(jìn)程中執(zhí)行的,它必須要等到要獲取的Content Provider是在新的進(jìn)程中加載完成后才能返回,這樣就涉及到進(jìn)程同步的問題了。這里使用的同步方法是不斷地去檢查變量cpr的provider域是否被設(shè)置了。當(dāng)要獲取的Content Provider在新的進(jìn)程加載完成之后,它會(huì)通過Binder進(jìn)程間通信機(jī)制調(diào)用到系統(tǒng)進(jìn)程中,把這個(gè)cpr變量的provider域設(shè)置為已經(jīng)加載好的Content Provider接口,這時(shí)候,函數(shù)getContentProviderImpl就可以返回了。下面的代碼就是用來等待要獲取的Content Provider是在新的進(jìn)程中加載完成的:
// Wait for the provider to be published...
synchronized (cpr) {
while (cpr.provider == null) {
......
try {
cpr.wait();
} catch (InterruptedException ex) {
}
}
}
下面我們?cè)俜治鲈谛逻M(jìn)程中加載ArticlesProvider這個(gè)Content Provider的過程。
Step 8. ActivityManagerService.startProcessLocked
Step 9. Process.start
Step 10. ActivityThread.main
Step 11. ActivityThread.attach
Step 12. ActivityManagerService.attachApplication
這五步是標(biāo)準(zhǔn)的Android應(yīng)用程序啟動(dòng)步驟,具體可以參考Android應(yīng)用程序啟動(dòng)過程源代碼分析一文中的Step 23到Step 27,或者Android系統(tǒng)在新進(jìn)程中啟動(dòng)自定義服務(wù)過程(startService)的原理分析一文中的Step 4到Step 9,這里就不再詳細(xì)描述了。
Step 13. ActivityManagerService.attachApplicationLocked
這個(gè)函數(shù)定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
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 if (mStartingProcesses.size() > 0) {
......
} else {
......
}
......
app.thread = thread;
app.curAdj = app.setAdj = -100;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.forcingToForeground = null;
app.foregroundServices = false;
app.debugging = false;
......
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
try {
......
thread.bindApplication(processName, app.instrumentationInfo != null
? app.instrumentationInfo : app.info, providers,
app.instrumentationClass, app.instrumentationProfileFile,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
isRestrictedBackupMode || !normalMode,
mConfiguration, getCommonServicesLocked());
......
} catch (Exception e) {
......
}
......
return true;
}
......
private final List generateApplicationProvidersLocked(ProcessRecord app) {
List providers = null;
try {
providers = AppGlobals.getPackageManager().
queryContentProviders(app.processName, app.info.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
}
if (providers != null) {
final int N = providers.size();
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
ContentProviderRecord cpr = mProvidersByClass.get(cpi.name);
if (cpr == null) {
cpr = new ContentProviderRecord(cpi, app.info);
mProvidersByClass.put(cpi.name, cpr);
}
app.pubProviders.put(cpi.name, cpr);
app.addPackage(cpi.applicationInfo.packageName);
ensurePackageDexOpt(cpi.applicationInfo.packageName);
}
}
return providers;
}
......
}
這個(gè)函數(shù)首先是根據(jù)傳進(jìn)來的進(jìn)程ID找到相應(yīng)的進(jìn)程記錄塊,注意,這個(gè)進(jìn)程ID是應(yīng)用程序ArticlesProvider的ID,然后對(duì)這個(gè)進(jìn)程記錄塊做一些初傾始化的工作。再接下來通過調(diào)用generateApplicationProvidersLocked獲得需要在這個(gè)過程中加載的Content Provider列表,在我們這個(gè)情景中,就只有ArticlesProvider這個(gè)Content Provider了。最后調(diào)用從參數(shù)傳進(jìn)來的IApplicationThread對(duì)象thread的bindApplication函數(shù)來執(zhí)行一些應(yīng)用程序初始化工作。從
Android應(yīng)用程序啟動(dòng)過程源代碼分析一文中我們知道,在Android系統(tǒng)中,每一個(gè)應(yīng)用程序進(jìn)程都加載了一個(gè)ActivityThread實(shí)例,在這個(gè)ActivityThread實(shí)例里面,有一個(gè)成員變量mAppThread,它是一個(gè)Binder對(duì)象,類型為ApplicationThread,實(shí)現(xiàn)了IApplicationThread接口,它是專門用來和ActivityManagerService服務(wù)進(jìn)行通信的。因此,調(diào)用下面語句:
thread.bindApplication(processName, app.instrumentationInfo != null
? app.instrumentationInfo : app.info, providers,
app.instrumentationClass, app.instrumentationProfileFile,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
isRestrictedBackupMode || !normalMode,
mConfiguration, getCommonServicesLocked());
就會(huì)進(jìn)入到應(yīng)用程序ArticlesProvider進(jìn)程中的ApplicationThread對(duì)象的bindApplication函數(shù)中去。在我們這個(gè)情景場(chǎng),這個(gè)函數(shù)調(diào)用中最重要的參數(shù)便是第三個(gè)參數(shù)providers了,它是我們要處理的對(duì)象。
Step 14. ApplicationThread.bindApplication
這個(gè)函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
private final class ApplicationThread extends ApplicationThreadNative {
......
public final void bindApplication(String processName,
ApplicationInfo appInfo, List<ProviderInfo> providers,
ComponentName instrumentationName, String profileFile,
Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
int debugMode, boolean isRestrictedBackupMode, Configuration config,
Map<String, IBinder> services) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.profileFile = profileFile;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.debugMode = debugMode;
data.restrictedBackupMode = isRestrictedBackupMode;
data.config = config;
queueOrSendMessage(H.BIND_APPLICATION, data);
}
......
}
......
}
這個(gè)函數(shù)把相關(guān)的信息都封裝成一個(gè)AppBindData對(duì)象,然后以一個(gè)消息的形式發(fā)送到主線程的消息隊(duì)列中去等等待處理。這個(gè)消息最終是是在ActivityThread類的handleBindApplication函數(shù)中進(jìn)行處理的。
Step 15. ActivityThread.handleBindApplication
這個(gè)函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
private final void handleBindApplication(AppBindData data) {
......
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
......
}
......
}
......
}
這個(gè)函數(shù)的內(nèi)容比較多,我們忽略了其它無關(guān)的部分,只關(guān)注和Content Provider有關(guān)的邏輯,這里主要就是調(diào)用installContentProviders函數(shù)來在本地安裝Content Providers信息。
Step 16. ActivityThread.installContentProviders
這個(gè)函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
private final void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
Iterator<ProviderInfo> i = providers.iterator();
while (i.hasNext()) {
ProviderInfo cpi = i.next();
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
IContentProvider cp = installProvider(context, null, cpi, false);
if (cp != null) {
IActivityManager.ContentProviderHolder cph =
new IActivityManager.ContentProviderHolder(cpi);
cph.provider = cp;
results.add(cph);
// Don't ever unload this provider from the process.
synchronized(mProviderMap) {
mProviderRefCountMap.put(cp.asBinder(), new ProviderRefCount(10000));
}
}
}
try {
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
}
}
......
}
這個(gè)函數(shù)主要是做了兩件事情,一是調(diào)用installProvider來在本地安裝每一個(gè)Content Proivder的信息,并且為每一個(gè)Content Provider創(chuàng)建一個(gè)ContentProviderHolder對(duì)象來保存相關(guān)的信息。ContentProviderHolder對(duì)象是一個(gè)Binder對(duì)象,是用來把Content Provider的信息傳遞給ActivityManagerService服務(wù)的。當(dāng)這些Content Provider都處理好了以后,還要調(diào)用ActivityManagerService服務(wù)的publishContentProviders函數(shù)來通知ActivityManagerService服務(wù),這個(gè)進(jìn)程中所要加載的Content Provider,都已經(jīng)準(zhǔn)備完畢了,而ActivityManagerService服務(wù)的publishContentProviders函數(shù)的作用就是用來喚醒在前面Step 7等待的線程的了。我們先來看installProvider的實(shí)現(xiàn),然后再來看ActivityManagerService服務(wù)的publishContentProviders函數(shù)的實(shí)現(xiàn)。
Step 17. ActivityThread.installProvider
這個(gè)函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
private final IContentProvider installProvider(Context context,
IContentProvider provider, ProviderInfo info, boolean noisy) {
ContentProvider localProvider = null;
if (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) {
}
}
......
try {
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
......
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
......
}
} else if (localLOGV) {
......
}
synchronized (mProviderMap) {
// Cache the pointer for the remote provider.
String names[] = PATTERN_SEMICOLON.split(info.authority);
for (int i=0; i<names.length; i++) {
ProviderClientRecord pr = new ProviderClientRecord(names[i], provider,
localProvider);
try {
provider.asBinder().linkToDeath(pr, 0);
mProviderMap.put(names[i], pr);
} catch (RemoteException e) {
return null;
}
}
if (localProvider != null) {
mLocalProviders.put(provider.asBinder(),
new ProviderClientRecord(null, provider, localProvider));
}
}
return provider;
}
......
}
這個(gè)函數(shù)的作用主要就是在應(yīng)用程序進(jìn)程中把相應(yīng)的Content Provider類加載進(jìn)來了,在我們這個(gè)種情景中,就是要在ArticlesProvider這個(gè)應(yīng)用程序中把ArticlesProvider這個(gè)Content Provider類加載到內(nèi)存中來了:
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
接著通過調(diào)用localProvider的getIContentProvider函數(shù)來獲得一個(gè)Binder對(duì)象,這個(gè)Binder對(duì)象返回給installContentProviders函數(shù)之后,就會(huì)傳到ActivityManagerService中去,后續(xù)其它應(yīng)用程序就是通過獲得這個(gè)Binder對(duì)象來和相應(yīng)的Content Provider進(jìn)行通信的了。我們先看一下這個(gè)函數(shù)的實(shí)現(xiàn),然后再回到installProvider函數(shù)中繼續(xù)分析。
Step 18. ContentProvider.getIContentProvider
這個(gè)函數(shù)定義在frameworks/base/core/java/android/content/ContentProvider.java文件中:
public abstract class ContentProvider implements ComponentCallbacks {
......
private Transport mTransport = new Transport();
......
class Transport extends ContentProviderNative {
......
}
public IContentProvider getIContentProvider() {
return mTransport;
}
......
}
從這里我們可以看出,ContentProvider類和Transport類的關(guān)系就類似于ActivityThread和ApplicationThread的關(guān)系,其它應(yīng)用程序不是直接調(diào)用ContentProvider接口來訪問它的數(shù)據(jù),而是通過調(diào)用它的內(nèi)部對(duì)象mTransport來間接調(diào)用ContentProvider的接口,這一點(diǎn)我們?cè)谙乱黄恼轮蟹治稣{(diào)用Content Provider接口來獲取共享數(shù)據(jù)時(shí)將會(huì)看到。
回到前面的installProvider函數(shù)中,它接下來調(diào)用下面接口來初始化剛剛加載好的Content Provider:
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
同樣,我們先進(jìn)入到ContentProvider類的attachInfo函數(shù)去看看它的實(shí)現(xiàn),然后再回到installProvider函數(shù)來。
Step 19. ContentProvider.attachInfo
這個(gè)函數(shù)定義在frameworks/base/core/java/android/content/ContentProvider.java文件中:
public abstract class ContentProvider implements ComponentCallbacks {
......
public void attachInfo(Context context, ProviderInfo info) {
/*
* 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;
mMyUid = Process.myUid();
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
}
ContentProvider.this.onCreate();
}
}
......
}
這個(gè)函數(shù)很簡(jiǎn)單,主要就是根據(jù)這個(gè)Content Provider的信息info來設(shè)置相應(yīng)的讀寫權(quán)限,然后調(diào)用它的子類的onCreate函數(shù)來讓子類執(zhí)行一些初始化的工作。在我們這個(gè)情景中,這個(gè)子類就是ArticlesProvide應(yīng)用程序中的ArticlesProvider類了。
Step 20. ArticlesProvider.onCreate
這個(gè)函數(shù)定義在前面一篇文章Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例介紹的應(yīng)用程序ArtilcesProvider源代碼工程目錄下,在文件為packages/experimental/ArticlesProvider/src/shy/luo/providers/articles/ArticlesProvider.java中:
public class ArticlesProvider extends ContentProvider {
......
@Override
public boolean onCreate() {
Context context = getContext();
resolver = context.getContentResolver();
dbHelper = new DBHelper(context, DB_NAME, null, DB_VERSION);
return true;
}
......
}
這個(gè)函數(shù)主要執(zhí)行一些簡(jiǎn)單的工作,例如,獲得應(yīng)用程序上下文的ContentResolver接口和創(chuàng)建數(shù)據(jù)庫(kù)操作輔助對(duì)象,具體可以參考前面一篇文章
Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例。
回到前面Step 17中的installProvider函數(shù)中,它接下來就是把這些在本地中加載的Content Provider信息保存下來了,以方便后面查詢和使用:
synchronized (mProviderMap) {
// Cache the pointer for the remote provider.
String names[] = PATTERN_SEMICOLON.split(info.authority);
for (int i=0; i<names.length; i++) {
ProviderClientRecord pr = new ProviderClientRecord(names[i], provider,
localProvider);
try {
provider.asBinder().linkToDeath(pr, 0);
mProviderMap.put(names[i], pr);
} catch (RemoteException e) {
return null;
}
}
if (localProvider != null) {
mLocalProviders.put(provider.asBinder(),
new ProviderClientRecord(null, provider, localProvider));
}
}
和ActivityMangerService類似,在ActivityThread中,以Content Provider的authority為鍵值來把這個(gè)Content Provider的信息保存在mProviderMap成員變量中,因?yàn)橐粋€(gè)Content Provider可以對(duì)應(yīng)多個(gè)authority,因此這里用一個(gè)for循環(huán)來處理,同時(shí)又以這個(gè)Content Provider對(duì)應(yīng)的Binder對(duì)象provider來鍵值來把這個(gè)Content Provider的信息保存在mLocalProviders成員變量中,表明這是一個(gè)在本地加載的Content Provider。
函數(shù)installProvider執(zhí)行完成以后,返回到Step 16中的instalContentProviders函數(shù)中,執(zhí)行下面語句:
try {
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
}
前面已經(jīng)提到,這個(gè)函數(shù)調(diào)用的作用就是通知ActivityMangerService,需要在這個(gè)進(jìn)程中加載的Content Provider已經(jīng)完加載完成了,參數(shù)results就包含了這些已經(jīng)加載好的Content Provider接口。
Step 21. ActivityMangerService.publishContentProviders
這個(gè)函數(shù)定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
......
synchronized(this) {
final ProcessRecord r = getRecordForAppLocked(caller);
......
final int N = providers.size();
for (int i=0; i<N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
mProvidersByClass.put(dst.info.name, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProvidersByName.put(names[j], dst);
}
int NL = mLaunchingProviders.size();
int j;
for (j=0; j<NL; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
j--;
NL--;
}
}
synchronized (dst) {
dst.provider = src.provider;
dst.app = r;
dst.notifyAll();
}
......
}
}
}
}
......
}
在我們這個(gè)情景中,只有一個(gè)Content Provider,因此,這里的N等待1。在中間的for循環(huán)里面,最重要的是下面這個(gè)語句:
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
從這里得到的ContentProviderRecord對(duì)象dst,就是在前面Step 7中創(chuàng)建的ContentProviderRecord對(duì)象cpr了。在for循環(huán)中,首先是把這個(gè)Content Provider信息保存好在mProvidersByClass和mProvidersByName中:
mProvidersByClass.put(dst.info.name, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProvidersByName.put(names[j], dst);
}
前面已經(jīng)說過,這兩個(gè)Map中,一個(gè)是以類名為鍵值保存Content Provider信息,一個(gè)是以authority為鍵值保存Content Provider信息。
因?yàn)檫@個(gè)Content Provider已經(jīng)加載好了,因此,把它從mLaunchingProviders列表中刪除:
int NL = mLaunchingProviders.size();
int j;
for (j=0; j<NL; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
j--;
NL--;
}
}
最后,設(shè)置這個(gè)ContentProviderRecord對(duì)象dst的provider域?yàn)閺膮?shù)傳進(jìn)來的Content Provider遠(yuǎn)程接口:
synchronized (dst) {
dst.provider = src.provider;
dst.app = r;
dst.notifyAll();
}
執(zhí)行了dst.notiryAll語句后,在Step 7中等待要獲取的Content Provider接口加載完畢的線程就被喚醒了。喚醒之后,它檢查本地ContentProviderRecord變量cpr的provider域不為null,于是就返回了。它最終返回到Step 5中的ActivityThread類的getProvider函數(shù)中,繼續(xù)往下執(zhí)行:
IContentProvider prov = installProvider(context, holder.provider,
holder.info, true);
注意,這里是在Article應(yīng)用程序中進(jìn)程中執(zhí)行installProvider函數(shù)的,而前面的Step 17的installProvider函數(shù)是在ArticlesProvider應(yīng)用程序進(jìn)程中執(zhí)行的。
Step 22. ActivityThread.installProvider
這個(gè)函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
private final IContentProvider installProvider(Context context,
IContentProvider provider, ProviderInfo info, boolean noisy) {
......
if (provider == null) {
......
} else if (localLOGV) {
......
}
synchronized (mProviderMap) {
// Cache the pointer for the remote provider.
String names[] = PATTERN_SEMICOLON.split(info.authority);
for (int i=0; i<names.length; i++) {
ProviderClientRecord pr = new ProviderClientRecord(names[i], provider,
localProvider);
try {
provider.asBinder().linkToDeath(pr, 0);
mProviderMap.put(names[i], pr);
} catch (RemoteException e) {
return null;
}
}
......
}
return provider;
}
......
}
同樣是執(zhí)行installProvider函數(shù),與Step 17不同,這里傳進(jìn)來的參數(shù)provider是不為null的,因此,它不需要執(zhí)行在本地加載Content Provider的工作,只需要把從ActivityMangerService中獲得的Content Provider接口保存在成員變量mProviderMap中就可以了。
這樣,獲取與"shy.luo.providers.artilces"這個(gè)uri對(duì)應(yīng)的Content Provider(shy.luo.providers.articles.ArticlesProvider)就完成了,它同時(shí)也是啟動(dòng)Content Provider的完整過程。第三方應(yīng)用程序獲得了這個(gè)Content Provider的接口之后,就可以訪問它里面的共享數(shù)據(jù)了。在下面一篇文章中,我們將重點(diǎn)分析Android應(yīng)用程序組件Content Provider在不同進(jìn)程中傳輸數(shù)據(jù)的過程,即Content Provider在不同應(yīng)用程序中共享數(shù)據(jù)的原理,敬請(qǐng)關(guān)注。
作者:Luoshengyang 發(fā)表于2011-11-28 0:58:59
原文鏈接
posted on 2012-04-17 21:32
mixer-a 閱讀(1219)
評(píng)論(0) 編輯 收藏