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

Step 1. ArticlesAdapter.getArticleCount
這個函數定義在前面一篇文章Android應用程序組件Content Provider應用實例介紹的應用程序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;
}
......
}
這個函數通過應用程序上下文的ContentResolver接口resolver的acquireProvider函數來獲得與Articles.CONTENT_URI對應的Content Provider對象的IContentProvider接口。常量Articles.CONTENT_URI是在應用程序ArticlesProvider中定義的,它的值為“content://shy.luo.providers.articles/item”,對應的Content Provider就是ArticlesProvider了。
Step 2. ContentResolver.acqireProvider
這個函數定義在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;
}
......
}
函數首先驗證參數uri的scheme是否正確,即是否是以content://開頭,然后取出它的authority部分,最后調用另外一個成員函數acquireProvider執行獲取ContentProvider接口的操作。在我們這個情景中,參數uri的authority的內容便是“shy.luo.providers.articles”了。
從ContentResolver類的定義我們可以看出,它是一個抽象類,兩個參數版本的acquireProvider函數是由它的子類來實現的。回到Step 1中,這個ContentResolver接口是通過應用程序上下文Context對象的getContentResolver函數來獲得的,而應用程序上下文Context是由ContextImpl類來實現的,它定義在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函數是在應用程序啟動的時候調用的,具體可以參考
Android應用程序啟動過程源代碼分析一文中的Step 34。
因此,在上面的ContentResolver類的acquireProvider函數里面接下來要調用的ApplicationContentResolver類的acquireProvider函數。
Step 3. ApplicationContentResolve.acquireProvider
這個函數定義在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;
}
......
}
它調用ActivityThread類的acquireProvider函數進一步執行獲取Content Provider接口的操作。
Step 4. ActivityThread.acquireProvider
這個函數定義在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;
}
......
}
它又是調用了另外一個成員函數getProvider來進一步執行獲取Content Provider接口的操作。
Step 5. ActivityThread.getProvider
這個函數定義在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;
}
......
}
這個函數首先會通過getExistingProvider函數來檢查本地是否已經存在這個要獲取的ContentProvider接口,如果存在,就直接返回了。本地已經存在的ContextProvider接口保存在ActivityThread類的mProviderMap成員變量中,以ContentProvider對應的URI的authority為鍵值保存。在我們這個情景中,因為是第一次調用ArticlesProvider接口,因此,這時候通過getExistingProvider函數得到的IContentProvider接口為null,于是下面就會調用ActivityManagerService服務的getContentProvider接口來獲取一個ContentProviderHolder對象holder,這個對象就包含了我們所要獲取的ArticlesProvider接口,在將這個接口返回給調用者之后,還會調用installProvider函數來把這個接口保存在本地中,以便下次要使用這個ContentProvider接口時,直接就可以通過getExistingProvider函數獲取了。
我們先進入到ActivityManagerService服務的getContentProvider函數中看看它是如何獲取我們所需要的ArticlesProvider接口的,然后再返回來看看installProvider函數的實現。
Step 6. ActivityManagerService.getContentProvider
這個函數定義在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);
}
......
}
它調用getContentProviderImpl函數來進一步執行操作。
Step 7. ActivityManagerService.getContentProviderImpl
這個函數定義在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;
}
......
}
這個函數比較長,我們一步一步地分析。
函數首先是獲取調用者的進程記錄塊信息:
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
......
}
在我們這個情景中,要獲取的就是應用程序Article的進程記錄塊信息了,后面會用到。
在ActivityManagerService中,有兩個成員變量是用來保存系統中的Content Provider信息的,一個是mProvidersByName,一個是mProvidersByClass,前者是以Content Provider的authoriry值為鍵值來保存的,后者是以Content Provider的類名為鍵值來保存的。一個Content Provider可以有多個authority,而只有一個類來和它對應,因此,這里要用兩個Map來保存,這里為了方便根據不同條件來快速查找而設計的。下面的代碼就是用來檢查要獲取的Content Provider是否已經加存在的了:
// 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.
}
}
在我們這個情景中,由于是第一次調用ArticlesProvider接口,因此,在mProvidersByName和mProvidersByClass兩個Map中都不存在ArticlesProvider的相關信息,因此,這里會通過AppGlobals.getPackageManager函數來獲得PackageManagerService服務接口,然后分別通過它的resolveContentProvider和getApplicationInfo函數來分別獲取ArticlesProvider應用程序的相關信息,分別保存在cpi和cpr這兩個本地變量中。這些信息都是在安裝應用程序的過程中保存下來的,具體可以參考
Android應用程序安裝過程源代碼分析一文。
接下去這個代碼判斷當前要獲取的Content Provider是否允許在客戶進程中加載,即查看一個這個Content Provider否配置了multiprocess屬性為true,如果允許在客戶進程中加載,就直接返回了這個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;
}
在我們這個情景中,要獲取的ArticlesProvider設置了要在獨立的進程中運行,因此,繼續往下執行:
// 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;
}
}
系統中所有正在加載的Content Provider都保存在mLaunchingProviders成員變量中。在加載相應的Content Provider之前,首先要判斷一下它是可否正在被其它應用程序加載,如果是的話,就不用重復加載了。在我們這個情景中,沒有其它應用程序也正在加載ArticlesProvider這個Content Provider,繼續往前執行:
// 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,就表明沒有其它應用程序正在加載這個Content Provider,因此,就要調用startProcessLocked函數來啟動一個新的進程來加載這個Content Provider對應的類了,然后把這個正在加載的信息增加到mLaunchingProviders中去。我們先接著分析這個函數,然后再來看在新進程中加載Content Provider的過程,繼續往下執行:
// 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);
這段代碼把這個Content Provider的信息分別保存到mProvidersByName和mProviderByCalss兩個Map中去,以方便后續查詢。
因為我們需要獲取的Content Provider是在新的進程中加載的,而getContentProviderImpl這個函數是在系統進程中執行的,它必須要等到要獲取的Content Provider是在新的進程中加載完成后才能返回,這樣就涉及到進程同步的問題了。這里使用的同步方法是不斷地去檢查變量cpr的provider域是否被設置了。當要獲取的Content Provider在新的進程加載完成之后,它會通過Binder進程間通信機制調用到系統進程中,把這個cpr變量的provider域設置為已經加載好的Content Provider接口,這時候,函數getContentProviderImpl就可以返回了。下面的代碼就是用來等待要獲取的Content Provider是在新的進程中加載完成的:
// Wait for the provider to be published...
synchronized (cpr) {
while (cpr.provider == null) {
......
try {
cpr.wait();
} catch (InterruptedException ex) {
}
}
}
下面我們再分析在新進程中加載ArticlesProvider這個Content Provider的過程。
Step 8. ActivityManagerService.startProcessLocked
Step 9. Process.start
Step 10. ActivityThread.main
Step 11. ActivityThread.attach
Step 12. ActivityManagerService.attachApplication
這五步是標準的Android應用程序啟動步驟,具體可以參考Android應用程序啟動過程源代碼分析一文中的Step 23到Step 27,或者Android系統在新進程中啟動自定義服務過程(startService)的原理分析一文中的Step 4到Step 9,這里就不再詳細描述了。
Step 13. ActivityManagerService.attachApplicationLocked
這個函數定義在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;
}
......
}
這個函數首先是根據傳進來的進程ID找到相應的進程記錄塊,注意,這個進程ID是應用程序ArticlesProvider的ID,然后對這個進程記錄塊做一些初傾始化的工作。再接下來通過調用generateApplicationProvidersLocked獲得需要在這個過程中加載的Content Provider列表,在我們這個情景中,就只有ArticlesProvider這個Content Provider了。最后調用從參數傳進來的IApplicationThread對象thread的bindApplication函數來執行一些應用程序初始化工作。從
Android應用程序啟動過程源代碼分析一文中我們知道,在Android系統中,每一個應用程序進程都加載了一個ActivityThread實例,在這個ActivityThread實例里面,有一個成員變量mAppThread,它是一個Binder對象,類型為ApplicationThread,實現了IApplicationThread接口,它是專門用來和ActivityManagerService服務進行通信的。因此,調用下面語句:
thread.bindApplication(processName, app.instrumentationInfo != null
? app.instrumentationInfo : app.info, providers,
app.instrumentationClass, app.instrumentationProfileFile,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
isRestrictedBackupMode || !normalMode,
mConfiguration, getCommonServicesLocked());
就會進入到應用程序ArticlesProvider進程中的ApplicationThread對象的bindApplication函數中去。在我們這個情景場,這個函數調用中最重要的參數便是第三個參數providers了,它是我們要處理的對象。
Step 14. ApplicationThread.bindApplication
這個函數定義在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);
}
......
}
......
}
這個函數把相關的信息都封裝成一個AppBindData對象,然后以一個消息的形式發送到主線程的消息隊列中去等等待處理。這個消息最終是是在ActivityThread類的handleBindApplication函數中進行處理的。
Step 15. ActivityThread.handleBindApplication
這個函數定義在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);
......
}
......
}
......
}
這個函數的內容比較多,我們忽略了其它無關的部分,只關注和Content Provider有關的邏輯,這里主要就是調用installContentProviders函數來在本地安裝Content Providers信息。
Step 16. ActivityThread.installContentProviders
這個函數定義在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) {
}
}
......
}
這個函數主要是做了兩件事情,一是調用installProvider來在本地安裝每一個Content Proivder的信息,并且為每一個Content Provider創建一個ContentProviderHolder對象來保存相關的信息。ContentProviderHolder對象是一個Binder對象,是用來把Content Provider的信息傳遞給ActivityManagerService服務的。當這些Content Provider都處理好了以后,還要調用ActivityManagerService服務的publishContentProviders函數來通知ActivityManagerService服務,這個進程中所要加載的Content Provider,都已經準備完畢了,而ActivityManagerService服務的publishContentProviders函數的作用就是用來喚醒在前面Step 7等待的線程的了。我們先來看installProvider的實現,然后再來看ActivityManagerService服務的publishContentProviders函數的實現。
Step 17. ActivityThread.installProvider
這個函數定義在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;
}
......
}
這個函數的作用主要就是在應用程序進程中把相應的Content Provider類加載進來了,在我們這個種情景中,就是要在ArticlesProvider這個應用程序中把ArticlesProvider這個Content Provider類加載到內存中來了:
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
接著通過調用localProvider的getIContentProvider函數來獲得一個Binder對象,這個Binder對象返回給installContentProviders函數之后,就會傳到ActivityManagerService中去,后續其它應用程序就是通過獲得這個Binder對象來和相應的Content Provider進行通信的了。我們先看一下這個函數的實現,然后再回到installProvider函數中繼續分析。
Step 18. ContentProvider.getIContentProvider
這個函數定義在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類的關系就類似于ActivityThread和ApplicationThread的關系,其它應用程序不是直接調用ContentProvider接口來訪問它的數據,而是通過調用它的內部對象mTransport來間接調用ContentProvider的接口,這一點我們在下一篇文章中分析調用Content Provider接口來獲取共享數據時將會看到。
回到前面的installProvider函數中,它接下來調用下面接口來初始化剛剛加載好的Content Provider:
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
同樣,我們先進入到ContentProvider類的attachInfo函數去看看它的實現,然后再回到installProvider函數來。
Step 19. ContentProvider.attachInfo
這個函數定義在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();
}
}
......
}
這個函數很簡單,主要就是根據這個Content Provider的信息info來設置相應的讀寫權限,然后調用它的子類的onCreate函數來讓子類執行一些初始化的工作。在我們這個情景中,這個子類就是ArticlesProvide應用程序中的ArticlesProvider類了。
Step 20. ArticlesProvider.onCreate
這個函數定義在前面一篇文章Android應用程序組件Content Provider應用實例介紹的應用程序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;
}
......
}
這個函數主要執行一些簡單的工作,例如,獲得應用程序上下文的ContentResolver接口和創建數據庫操作輔助對象,具體可以參考前面一篇文章
Android應用程序組件Content Provider應用實例。
回到前面Step 17中的installProvider函數中,它接下來就是把這些在本地中加載的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為鍵值來把這個Content Provider的信息保存在mProviderMap成員變量中,因為一個Content Provider可以對應多個authority,因此這里用一個for循環來處理,同時又以這個Content Provider對應的Binder對象provider來鍵值來把這個Content Provider的信息保存在mLocalProviders成員變量中,表明這是一個在本地加載的Content Provider。
函數installProvider執行完成以后,返回到Step 16中的instalContentProviders函數中,執行下面語句:
try {
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
}
前面已經提到,這個函數調用的作用就是通知ActivityMangerService,需要在這個進程中加載的Content Provider已經完加載完成了,參數results就包含了這些已經加載好的Content Provider接口。
Step 21. ActivityMangerService.publishContentProviders
這個函數定義在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();
}
......
}
}
}
}
......
}
在我們這個情景中,只有一個Content Provider,因此,這里的N等待1。在中間的for循環里面,最重要的是下面這個語句:
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
從這里得到的ContentProviderRecord對象dst,就是在前面Step 7中創建的ContentProviderRecord對象cpr了。在for循環中,首先是把這個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);
}
前面已經說過,這兩個Map中,一個是以類名為鍵值保存Content Provider信息,一個是以authority為鍵值保存Content Provider信息。
因為這個Content Provider已經加載好了,因此,把它從mLaunchingProviders列表中刪除:
int NL = mLaunchingProviders.size();
int j;
for (j=0; j<NL; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
j--;
NL--;
}
}
最后,設置這個ContentProviderRecord對象dst的provider域為從參數傳進來的Content Provider遠程接口:
synchronized (dst) {
dst.provider = src.provider;
dst.app = r;
dst.notifyAll();
}
執行了dst.notiryAll語句后,在Step 7中等待要獲取的Content Provider接口加載完畢的線程就被喚醒了。喚醒之后,它檢查本地ContentProviderRecord變量cpr的provider域不為null,于是就返回了。它最終返回到Step 5中的ActivityThread類的getProvider函數中,繼續往下執行:
IContentProvider prov = installProvider(context, holder.provider,
holder.info, true);
注意,這里是在Article應用程序中進程中執行installProvider函數的,而前面的Step 17的installProvider函數是在ArticlesProvider應用程序進程中執行的。
Step 22. ActivityThread.installProvider
這個函數定義在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;
}
......
}
同樣是執行installProvider函數,與Step 17不同,這里傳進來的參數provider是不為null的,因此,它不需要執行在本地加載Content Provider的工作,只需要把從ActivityMangerService中獲得的Content Provider接口保存在成員變量mProviderMap中就可以了。
這樣,獲取與"shy.luo.providers.artilces"這個uri對應的Content Provider(shy.luo.providers.articles.ArticlesProvider)就完成了,它同時也是啟動Content Provider的完整過程。第三方應用程序獲得了這個Content Provider的接口之后,就可以訪問它里面的共享數據了。在下面一篇文章中,我們將重點分析Android應用程序組件Content Provider在不同進程中傳輸數據的過程,即Content Provider在不同應用程序中共享數據的原理,敬請關注。
作者:Luoshengyang 發表于2011-11-28 0:58:59
原文鏈接
posted on 2012-04-17 21:32
mixer-a 閱讀(1219)
評論(0) 編輯 收藏