ContentProvider的啟動過程原始碼分析
ContentProvider的啟動過程原始碼分析
因為我們是通過ContentResolver來跟ContentProvider進行互動的,所以ContentProvider的啟動的開始便從getContentResolver()開始分析。
1、獲取ContentResolver並向ContentProvider插入一條資料
//獲取ContentResolver並向ContentProvider插入一條資料
getContentResolver().insert(uri, values);
2、getContentResolver()具體實現是在ContextImpl中
//ContextImpl.java class ContextImpl extends Context { ...... private ApplicationContentResolver mContentResolver; ...... private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread, @NonNull LoadedApk packageInfo, @Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user, int flags, @Nullable ClassLoader classLoader) { ...... mContentResolver = new ApplicationContentResolver(this, mainThread); ...... } ...... @Override public ContentResolver getContentResolver() { return mContentResolver; } ...... }
可以看出通過getContentResolver獲得的是一個ApplicationContentResolver型別的例項。ApplicationContentResolver是ContextImpl的一個內部類,它的父類是ContentResolver。
private static final class ApplicationContentResolver extends ContentResolver { private final ActivityThread mMainThread; private final UserHandle mUser; public ApplicationContentResolver( Context context, ActivityThread mainThread, UserHandle user) { super(context); mMainThread = Preconditions.checkNotNull(mainThread); mUser = Preconditions.checkNotNull(user); } }
ContentResolver是Android提供的一個抽象類。所以getContentResolver獲取的是一個ContentResolver。
/** * This class provides applications access to the content model. * * <div class=“special reference”> * <h3>Developer Guides</h3> * <p>For more information about using a ContentResolver with content providers, read the * <a href=“{@docRoot}guide/topics/providers/content-providers.html”>Content Providers</a> * developer guide.</p> */ public abstract class ContentResolver { }
3、我們獲取到ContentResolver之後便呼叫insert方法向ContentProvider插入一條資料。所以我們來看下insert方法,它是在ContentResolver中定義的
public abstract class ContentResolver {
public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
@Nullable ContentValues values) {
Preconditions.checkNotNull(url, “url”);
IContentProvider provider = acquireProvider(url);//1
if (provider == null) {
throw new IllegalArgumentException("Unknown URL " + url);
}
try {
long startTime = SystemClock.uptimeMillis();
Uri createdRow = provider.insert(mPackageName, url, values);//2
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
releaseProvider(provider);
}
}
}
我們可以看到它在註釋1處首先呼叫了acquireProvider來獲取一個IContentProvider例項。然後在註釋2處呼叫獲取到的IContentProvider例項的insert方法進行插入資料操作。
IContentProvider是Android提供的用來跟ContentProvider進行程序間通訊的Binder介面。
public interface IContentProvider extends IInterface {
public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException;
public String getType(Uri url) throws RemoteException;
public Uri insert(String callingPkg, Uri url, ContentValues initialValues)
throws RemoteException;
public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
throws RemoteException;
public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
throws RemoteException;
public int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException;
}
繼續看acquireProvider方法,在註釋1處它驗證了傳入的uri格式是否正確,即Scheme是否為content,之後在註釋2處呼叫同名的抽象函式,因為我們通過getContentResolver()拿到的是一個ApplicationContentResolver例項所以這個抽象的acquireProvider具體實現是在ApplicationContentResolver類中。
public final IContentProvider acquireProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {//1
return null;
}
final String auth = uri.getAuthority();
if (auth != null) {
return acquireProvider(mContext, auth);//2
}
return null;
}
protected abstract IContentProvider acquireProvider(Context c, String name);
可以看出它什麼也沒做只是呼叫了ActivityThread的acquireProvider方法。
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
}
4、ActivityThread的acquireProvider首先呼叫了acquireExistingProvider來查詢是否已經存在要找的ContentProvider,這裡我們是第一次呼叫所以返回為null,接著呼叫註釋2處的AMS的getContentProvider獲取一個ContentProviderHolder物件,之後還會呼叫installProvider函式來把這個ContentProviderHolder儲存在本地,以便下次要使用這個ContentProvider介面時,直接就可以通過getExistingProvider函式獲取。
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);//1
if (provider != null) {
return provider;
}
ContentProviderHolder holder = null;
try {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);//2
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
Slog.e(TAG, “Failed to find provider info for “ + auth);
return null;
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);//3
return holder.provider;
}
5、ActivityManagerService.getContentProvider,它進一步呼叫getContentProviderImpl。
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = “null IApplicationThread when getting content provider “
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
return getContentProviderImpl(caller, name, null, stable, userId);
}
6、ActivityManagerService.getContentProviderImpl,該函式很長我們分段來看。
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized(this) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);//1、獲取呼叫程序的程序記錄塊
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
}
boolean checkCrossUser = true;
checkTime(startTime, "getContentProviderImpl: getProviderByName");
// First check if this content provider has been published…
cpr = mProviderMap.getProviderByName(name, userId);//2、檢查要獲取的provider是否已存在
// If that didn’t work, check if it exists for user 0 and then
// verify that it's a singleton provider before using it.
if (cpr == null && userId != UserHandle.USER_SYSTEM) {
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
if (isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
cpr = null;
cpi = null;
}
}
}
boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;//3、provider是否已經在執行
if (providerRunning) {//4、provider已經在運行了
cpi = cpr.info;
String msg;
checkTime(startTime, “getContentProviderImpl: before checkContentProviderPermission");
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
!= null) {
throw new SecurityException(msg);
}
checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
if (r != null && cpr.canRunHere(r)) {
// This provider has been published or is in the process
// of being published... but it is also allowed to run
// in the caller's process, so don't make a connection
// and just let the caller instantiate its own instance.
ContentProviderHolder holder = cpr.newHolder(null);
// don't give caller the provider object, it needs
// to make its own.
holder.provider = null;
return holder;
}
// Don’t expose providers between normal apps and instant apps
try {
if (AppGlobals.getPackageManager()
.resolveContentProvider(name, 0 /*flags*/, userId) == null) {
return null;
}
} catch (RemoteException e) {
}
final long origId = Binder.clearCallingIdentity();
checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
// In this case the provider instance already exists, so we can
// return it right away.
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
// If this is a perceptible app accessing the provider,
// make sure to count it as being accessed and thus
// back up on the LRU list. This is good because
// content providers are often expensive to start.
checkTime(startTime, “getContentProviderImpl: before updateLruProcess");
updateLruProcessLocked(cpr.proc, false, null);
checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
}
}
checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
final int verifiedAdj = cpr.proc.verifiedAdj;
boolean success = updateOomAdjLocked(cpr.proc, true);
// XXX things have changed so updateOomAdjLocked doesn't actually tell us
// if the process has been successfully adjusted. So to reduce races with
// it, we will check whether the process still exists. Note that this doesn't
// completely get rid of races with LMK killing the process, but should make
// them much smaller.
if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
success = false;
}
maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
checkTime(startTime, “getContentProviderImpl: after updateOomAdj");
if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
// NOTE: there is still a race here where a signal could be
// pending on the process even though we managed to update its
// adj level. Not sure what to do about this, but at least
// the race is now smaller.
if (!success) {
// Uh oh... it looks like the provider's process
// has been killed on us. We need to wait for a new
// process to be started, and make sure its death
// doesn't kill our process.
Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
checkTime(startTime, “getContentProviderImpl: before appDied”);
appDiedLocked(cpr.proc);
checkTime(startTime, "getContentProviderImpl: after appDied");
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we have now been killed, bail.
return null;
}
providerRunning = false;
conn = null;
} else {
cpr.proc.verifiedAdj = cpr.proc.setAdj;
}
Binder.restoreCallingIdentity(origId);
}
if (!providerRunning) {//5、provider還未執行
try {
checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
} catch (RemoteException ex) {
}
if (cpi == null) {
return null;
}
// If the provider is a singleton AND
// (it's a call within the same user || the provider is a
// privileged app)
// Then allow connecting to the singleton provider
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_SYSTEM;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
checkTime(startTime, "getContentProviderImpl: got app info for user");
String msg;
checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
!= null) {
throw new SecurityException(msg);
}
checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
if (!mProcessesReady
&& !cpi.processName.equals("system")) {
// If this content provider does not run in the system
// process, and the system is not yet ready to run other
// processes, then fail fast instead of hanging.
throw new IllegalArgumentException(
"Attempt to launch content provider before system ready");
}
// Make sure that the user who owns this provider is running. If not,
// we don’t want to allow it to run.
if (!mUserController.isUserRunningLocked(userId, 0)) {
Slog.w(TAG, “Unable to launch app “
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": user " + userId + " is stopped");
return null;
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
cpr = mProviderMap.getProviderByClass(comp, userId);//6、通過Classname 查詢是否存在對應的provider
checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
final boolean firstClass = cpr == null;//7、沒有找到對應的provider說明這是首次啟動
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
// If permissions need a review before any of the app components can run,
// we return no provider and launch a review activity if the calling app
// is in the foreground.
if (mPermissionReviewRequired) {
if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
return null;
}
}
try {
checkTime(startTime, “getContentProviderImpl: before getApplicationInfo”);
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
if (ai == null) {
Slog.w(TAG, "No package info for content provider "
+ cpi.name);
return null;
}
ai = getAppInfoForUser(ai, userId);
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);//8、建立對應的ContentProviderRecord
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} finally {
Binder.restoreCallingIdentity(ident);
}
}
checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
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.newHolder(null);
}
if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid "
+ (r != null ? r.uid : null) + “ pruid “ + cpr.appInfo.uid + “): “
+ cpr.info.name + “ callers=“ + Debug.getCallers(6));
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
//9、系統中所有正在載入的Content Provider都儲存在mLaunchingProviders成員變數中。
// 在載入相應的Content 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.
//10、該ContentProvider沒有被其它應用程式載入
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
try {
// Content provider is now in use, its package can’t be stopped.
try {
checkTime(startTime, "getContentProviderImpl: before set stopped state");
AppGlobals.getPackageManager().setPackageStoppedState(
cpr.appInfo.packageName, false, userId);
checkTime(startTime, "getContentProviderImpl: after set stopped state");
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, “Failed trying to unstop package “
+ cpr.appInfo.packageName + “: “ + e);
}
// Use existing process if already started
checkTime(startTime, “getContentProviderImpl: looking for process record");
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {
if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
"Installing in existing process " + proc);
if (!proc.pubProviders.containsKey(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
checkTime(startTime, "getContentProviderImpl: before start process");
//11、啟動一個新的程序來載入這個ContentProvider對應的類
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + “ for provider “
+ name + “: process is bad”);
return null;
}
}
cpr.launchingApp = proc;
//12、把載入的ContentProvider加到mLaunchingProviders中
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
checkTime(startTime, "getContentProviderImpl: updating data structures");
// Make sure the provider is published (the same provider class
// may be published under multiple names).
//13、把載入的ContentProvider的資訊分別儲存到mProvidersByName和mProviderByCalss兩個Map中去,以方便後續查詢
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
checkTime(startTime, “getContentProviderImpl: done!”);
grantEphemeralAccessLocked(userId, null /*intent*/,
cpi.applicationInfo.uid, UserHandle.getAppId(Binder.getCallingUid()));
}
// Wait for the provider to be published…
//14、因為我們需要獲取的ContentProvider是在新的程序中載入的,而getContentProviderImpl這個函式是在系統程序中執行的,
// 它必須要等到要獲取的ContentProvider在新的程序中載入完成後才能返回,這樣就涉及到程序同步的問題了。
// 這裡使用的同步方法是不斷地去檢查變數cpr的provider域是否被設定了。當要獲取的ContentProvider在新的程序載入完成之後,
// 它會通過Binder程序間通訊機制呼叫到系統程序中,把這個cpr變數的provider域設定為已經載入好的ContentProvider介面,
// 這時候函式getContentProviderImpl就可以返回了
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": launching app became null");
EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
UserHandle.getUserId(cpi.applicationInfo.uid),
cpi.applicationInfo.packageName,
cpi.applicationInfo.uid, name);
return null;
}
try {
if (DEBUG_MU) Slog.v(TAG_MU,
"Waiting to start provider " + cpr
+ " launchingApp=" + cpr.launchingApp);
if (conn != null) {
conn.waiting = true;
}
cpr.wait();
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
return cpr != null ? cpr.newHolder(conn) : null;
}
首先看註釋1處獲取了呼叫程序的程序記錄塊,方便後面使用
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);//1、獲取呼叫程序的程序記錄塊
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ “) when getting content provider “ + name);
}
}
在註釋2處檢查是否已經存在想要獲取的provider
cpr = mProviderMap.getProviderByName(name, userId);//2、檢查要獲取的provider是否已存在
在註釋3處根據上邊的查詢情況判斷provider是否已經在運行了,因為這是首次啟動所以providerRunning為false。
boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;//3、provider是否已經在執行
如果還未執行那麼會進入註釋5的分支,之後在註釋6處通過classname 查詢是否存在對應的provider,在註釋2處也查詢過否存在對應的provider。這裡解釋下因為有兩個成員變數用來儲存系統中的Content Provider資訊的,一個是mProvidersByName,一個是mProvidersByClass,前者是以Content Provider的authoriry值為鍵值來儲存的,後者是以Content Provider的類名為鍵值來儲存的。一個Content Provider可以有多個authority,而只有一個類來和它對應,因此,這裡要用兩個Map來儲存,為了方便根據不同條件來快速查詢而設計的。如果仍未找到那麼就說明這是首次載入,在註釋8處建立對應的ContentProviderRecord。
if (!providerRunning) {//5、provider還未執行
//. . .
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
checkTime(startTime, “getContentProviderImpl: before getProviderByClass”);
cpr = mProviderMap.getProviderByClass(comp, userId);//6、通過Classname 查詢是否存在對應的provider
checkTime(startTime, “getContentProviderImpl: after getProviderByClass”);
final boolean firstClass = cpr == null;//7、沒有找到對應的provider說明這是首次啟動
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
//. . .
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);//8、建立對應的ContentProviderRecord
}
註釋9系統中所有正在載入的Content Provider都儲存在mLaunchingProviders成員變數中。在載入相應的Content Provider之前,首先要判斷一下它是否正在被其它應用程式載入,如果是的話,就不用重複載入了。
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
//9、系統中所有正在載入的Content Provider都儲存在mLaunchingProviders成員變數中。
// 在載入相應的Content Provider之前,首先要判斷一下它是否正在被其它應用程式載入,如果是的話,就不用重複載入了
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
下面就是如果ContentProvider沒有被其它應用程式載入那麼會啟動一個新的程序去載入,完成後把其加到mLaunchingProviders中
//10、該ContentProvider沒有被其它應用程式載入
if (I >= N) {
//. . .
//11、啟動一個新的程序來載入這個ContentProvider對應的類
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, “content provider”,
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
//. . .
//12、把載入的ContentProvider加到mLaunchingProviders中
mLaunchingProviders.add(cpr);
}
接著把載入的ContentProvider的資訊分別儲存到mProvidersByName和mProviderByCalss兩個Map中去,以方便後續查詢
//13、把載入的ContentProvider的資訊分別儲存到mProvidersByName和mProviderByCalss兩個Map中去,以方便後續查詢
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
因為我們需要獲取的ContentProvider是在新的程序中載入的,而getContentProviderImpl這個函式是在系統程序中執行的,它必須要等到要獲取的ContentProvider在新的程序中載入完成後才能返回,這樣就涉及到程序同步的問題了。這裡使用的同步方法是不斷地去檢查變數cpr的provider域是否被設定了。當要獲取的ContentProvider在新的程序載入完成之後,
它會通過Binder程序間通訊機制呼叫到系統程序中,把這個cpr變數的provider域設定為已經載入好的ContentProvider介面,這時候函式getContentProviderImpl就可以返回了
// Wait for the provider to be published…
//14、因為我們需要獲取的ContentProvider是在新的程序中載入的,而getContentProviderImpl這個函式是在系統程序中執行的,
// 它必須要等到要獲取的ContentProvider在新的程序中載入完成後才能返回,這樣就涉及到程序同步的問題了。
// 這裡使用的同步方法是不斷地去檢查變數cpr的provider域是否被設定了。當要獲取的ContentProvider在新的程序載入完成之後,
// 它會通過Binder程序間通訊機制呼叫到系統程序中,把這個cpr變數的provider域設定為已經載入好的ContentProvider介面,
// 這時候函式getContentProviderImpl就可以返回了
synchronized (cpr) {
while (cpr.provider == null) {
try {
if (DEBUG_MU) Slog.v(TAG_MU,
“Waiting to start provider “ + cpr
+ “ launchingApp=“ + cpr.launchingApp);
if (conn != null) {
conn.waiting = true;
}
cpr.wait();
} catch (InterruptedException ex) {
}
}
}
以上就是整個getContentProviderImpl的操作。
7、上面我們知道要獲取的ContentProvider還未載入,需要啟動一個新的程序去載入。新程序的啟動時通過startProcessLocked來完成的,具體的啟動過程這裡就不具體展開了。當新程序啟動完畢之後會依次呼叫這幾個函式ActivityThread.main——> ActivityThread.attach——>ActivityManagerService.attachApplication。我們從attachApplication開始分析。
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
//此處我們只看跟ContentProvider有關的內容
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;//1、呼叫generateApplicationProvidersLocked獲得需要在這個過程中載入的Content Provider列表
//. .
//2、呼叫bindApplication執行一些應用程式初始化工作
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
}
8、我們先看generateApplicationProvidersLocked
private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
List<ProviderInfo> providers = null;
try {
providers = AppGlobals.getPackageManager()
.queryContentProviders(app.processName, app.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
| MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
.getList();
} catch (RemoteException ex) {
}
}
它是通過AppGlobals.getPackageManager().queryContentProviders().getList()來獲取要載入的ContentProvider。
9、然後我們接著看bindApplication,它的第三個引數就是我們在generateApplicationProvidersLocked中獲取的要載入的ContentProvider list。
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
setCoreSettings(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.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
sendMessage(H.BIND_APPLICATION, data);
}
它的操作就是把相關的資訊都封裝成一個AppBindData物件,然後以一個訊息的形式傳送到主執行緒的訊息佇列中等待處理。
10、ActivityThread中處理BIND_APPLICATION訊息的是handleBindApplication函式,該函式很長 我們只關注跟ContentProvider有關的操作,它取出了AppBindData中的provider然後呼叫installContentProviders。
private void handleBindApplication(AppBindData data)
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
11、installContentProviders
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
//1、installProvider來在本地安裝每一個ContentProivder的資訊
for (ProviderInfo cpi : providers) {
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
//2、呼叫ActivityManagerService服務的publishContentProviders函式來通知ActivityManagerService服務,這個程序中所要載入的Content Provider,都已經準備完畢
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
我們看到在註釋1處呼叫installProvider來在本地安裝每一個Content Proivder的資訊,並且為每一個Content Provider建立一個ContentProviderHolder物件來儲存相關的資訊。ContentProviderHolder物件是一個Binder物件,是用來把Content Provider的資訊傳遞給ActivityManagerService服務的。然後在註釋2處通過publishContentProviders函式來通知ActivityManagerService服務,這個程序中所要載入的Content Provider,都已經準備完畢。publishContentProviders函式的作用就是用來喚醒在前面步驟6中等待的執行緒
12、我們先來看installProvider
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
//. . .
try {
//1、載入ContentProvider
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
}
//. . .
//2、儲存ContentProvider
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 ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
//. . .
}
installProvider主要做了兩件事在註釋1處載入ContentProvider並呼叫getIContentProvider函式來獲得一個Binder物件,這個Binder物件返回給installContentProviders函式之後,就會傳到ActivityManagerService中去,後續其它應用程式就是通過獲得這個Binder物件來和相應的Content Provider進行通訊,然後又呼叫attachInfo
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
localProvider.attachInfo(c, info);
attachInfo最終會呼叫ContentProvider的onCreate函式,這樣ContentProvider就完成了建立載入。
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = 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);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
ContentProvider.this.onCreate();//回撥onCreate
}
}
ContentProvider載入完成之後,會在註釋2處把建立的ContentProvider儲存起來。
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 ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
可以看分別儲存到了mLocalProviders(以ContentProvider對應的Binder物件provider為鍵值來儲存,表明這是一個在本地載入的ContentProvider)、 mLocalProvidersByName(以ContentProvider類名為鍵值儲存)和mProviderMap(通過installProviderAuthoritiesLocked儲存到mProviderMap,以ContentProvider的author為鍵值儲存)
下面貼下installProviderAuthoritiesLocked方法
ContentProvider localProvider, ContentProviderHolder holder) {
final String auths[] = holder.info.authority.split(“;”);
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
if (provider != null) {
// If this provider is hosted by the core OS and cannot be upgraded,
// then I guess we're okay doing blocking calls to it.
for (String auth : auths) {
switch (auth) {
case ContactsContract.AUTHORITY:
case CallLog.AUTHORITY:
case CallLog.SHADOW_AUTHORITY:
case BlockedNumberContract.AUTHORITY:
case CalendarContract.AUTHORITY:
case Downloads.Impl.AUTHORITY:
case "telephony":
Binder.allowBlocking(provider.asBinder());
}
}
}
final ProviderClientRecord pcr = new ProviderClientRecord(
auths, provider, localProvider, holder);
for (String auth : auths) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord existing = mProviderMap.get(key);
if (existing != null) {
Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+ " already published as " + auth);
} else {
mProviderMap.put(key, pcr);//以ContentProvider的author為鍵值儲存該ContentProvider,因為可能存在多個author所以這裡採用for迴圈
}
}
return pcr;
}
至此 installProvider執行完畢,回到步驟11然後執行其註釋2處邏輯
13、步驟11註釋2執行了publishContentProviders來通知ActivityManagerService服務,程序中所要載入的ContentProvider,都已經準備完畢。
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
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) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);//1儲存ContentProvider
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);//2儲存ContentProvider
}
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {//3把剛剛載入的ContentProvider從mLaunchingProviders移除
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
synchronized (dst) {//4喚醒等待執行緒
dst.provider = src.provider;
dst.proc = r;
dst.notifyAll();
}
updateOomAdjLocked(r, true);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
}
}
首先在註釋1和2處把ContentProvider儲存,有人可能疑惑了上一步不是儲存了,這裡要注意的的上一步的儲存是在啟動ContentProvider的程序中進行的,這裡儲存是在AMS程序中。之後在註釋3處把剛剛載入的ContentProvider從mLaunchingProviders移除。最後在註釋4處喚醒等待的執行緒,這裡指的是步驟6中註釋14處等待的執行緒。喚醒之後,它檢查本地ContentProviderRecord變數cpr的provider域不為null,於是就返回了。最終返回到步驟4的註釋3處繼續執行。
14、步驟4的註釋3處呼叫了ActivityThread的installProvider,此次呼叫是在AMS請求呼叫ContentProvider的程序中。
//Step 4
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);//3
因為這次holder.provider不為空所以不需要再次載入ContentProvider
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
if (holder == null || holder.provider == null){
}else {
provider = holder.provider;
}
}
14、之後一路返回最終返回到步驟3的註釋1處,然後繼續執行在註釋2處利用返回的provider呼叫其insert完成資料插入操作。至此整個ContentProvider啟動過程分析完畢。
參考連結: