ContentProvider是Android系統的四大元件之一,主要用于向外部提供資料。不僅可以向自己應用程序提供資料,也可以向其他程序的提供資料。是以在分析ContentProvider的時候我們首先分析本程序的ContentProvider的啟動過程,然後再分析調用其他程序的ContentProvider的時候ContentProvider的安裝啟動過程。
本程序ContentProvider啟動過程分析
本程序内的ContentProvider一般是在程序啟動的時候就啟動并建立的。在Activity的啟動過程中分析過新程序的啟動過程。
1. 建立一個新的程序。調用ActivityThread的main方法
2. 調用ActivityThread的attach方法
3. 調用ActivityManagerService的attachApplication方法,通知新的程序建立完成,根據新建立的程序初始化ProcessRecord的資訊。然後查詢所有和本程序相關的ContentProvider資訊。
4. 調用建立程序的bindApplication方法,通知新程序安裝并啟動這些ContentProvider
以上就是本程序的ContentProvider的啟動和安裝過程。第一第二步我們不再分析,直接從AMS服務的attachApplication方法開始分析。
1. AMS.attachApplication
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
//找到之前建立的ProcessRecord對象,之前的ProcessRecord對象還沒有指向任何一個程序
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
} else {
app = null;
}
//根據建立的程序來初始化這個ProcessRecord對象,使它指向新建立的程序
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = -100;
app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.forcingToForeground = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
app.debugging = false;
app.cached = false;
app.killedByAm = false;
//使用generateApplicationProvidersLocked方法在PMS服務中查詢所有和該程序有關的ContentProvider資訊
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
……
//最終調用建立程序的bindApplication來建立并啟動該ContentProvider
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
……
}
在該方法中調用了generateApplicationProvidersLocked方法來查詢和建立程序相關的ContetProvider的資訊,儲存在Providers清單中。同時在該方法中,還将查詢到的每一個ContentProvider的資訊,封裝成了一個ContentProviderRecord對象,儲存了ProviderMap中。
随後調用建立程序的binderApplication方法的時候将providers資訊作為參數傳入了建立程序。
2. ApplicationThread.bindApplication
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
Bundle coreSettings) {
……
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableOpenGlTrace = enableOpenGlTrace;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
sendMessage(H.BIND_APPLICATION, data);
}
ApplicationThread接收到AMS服務發送的請求,然後通過Handler發送BIND_APPLICATION消息給Handler來處理。Handler的handlerMessage接收到BIND_APPLICATION消息後,調用ActivityThread的handBindApplication方法來處理。
3. ActivityThread.handleBindApplication
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, 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);
}
handleBindApplication方法中我們隻分析和初始化ContentProvider相關的代碼邏輯。首先擷取到參數傳遞的和本程序有關的provider的資訊,調用installContentProviders方法來安裝并啟動contentProvider元件。
4. ActivityThread.installContentProviders
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
for (ProviderInfo cpi : providers) {
……
//通過調用installProvider方法,生成了一個ContetProviderHolder對象
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
//通知AMS服務來釋出這些ContentProvider,這樣其他程序就可以通過AMS服務來通路這些ContentProvider了
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
}
}
這個方法主要做了兩件事。
第一.通過循環變量providerinfo資訊,調用installProvider方法将provider資訊安裝完成并封裝成了一個ContentProviderHolder類型的對象。
第二.調用AMS服務的publishContentProviders方法,将這些安裝完成的Provider資訊釋出到AMS服務,以便其他程序通路。
那就先來看installProvider的處理過程,在來看AMS釋出ContentProvider的過程。
5. ActivityThread.installProvider過程
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
……
Context c = null;
ApplicationInfo ai = info.applicationInfo;
if (context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
……
}
……
try {
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
//擷取ContetProvider的IContentProvider指派給provider變量
provider = localProvider.getIContentProvider();
localProvider.attachInfo(c, info);
}
} else {
……
}
我們首先來看這個方法的第一部分。在installContentProviders方法中調用這個方法的時候,holder參數傳遞的值為Null,也是因為這些ContentProvider是第一次安裝。是以holde肯定為Null。是以此時滿座if的條件。在If語句中,首先根據條件擷取相應的Context上下文資訊。
然後ClassLoader加載對應的ContentProvider類,并建立該類的對象,然後調用ContentProvider的attach方法。該方法作用是将新建立的ContentProvider和Context,ProviderInfo關聯起來,最後調用該Provider的onCreate方法啟動ContentProvider。這個一個ContentProvider就建立完成了,下一步就是将它儲存到應用程序的中,以友善查找和管理。并釋出到AMS服務中,友善其他程序調用。
擷取ContetProvider的IContentProvider指派給provider變量,IContentProvider是ContentProvider用戶端和服務端通信的接口,genIcontentProvider了解為得到一個Binder類型的對象,用于ContentProvider用戶端和服務端之間的通信。
IActivityManager.ContentProviderHolder retHolder;
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
//第一次建立ContentProvider,還沒有儲存,是以pr為null
if (pr != null) {
provider = pr.mProvider;
} else {
//根據Provider建立ContentProviderHolder對象
holder = new IActivityManager.ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
//通過該方法吧Provider的資訊儲存到ProviderMap中
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else {
……
}
return retHolder;
}
由于是第一次啟動ContentProvider,是以該資訊還沒有儲存,是以變量pr為空,此時根據ProviderInfo的資訊和Binder類型IContentProvider對象,建立一個ContentProviderHolder對象,它裡邊封裝了這個ContentProvider的ProviderInfo和IContentProvider資訊。
方法最後傳回建立的這個ContentProviderHolder的對象。
接着看看一下installProviderAuthoritiesLocked的實作
installProviderAuthoritiesLocked
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
final String auths[] = holder.info.authority.split(";");
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
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);
}
}
return pcr;
}
根據Provider的資訊建立了一個ProviderClientRecord對象,authority是一個多屬性值,變量這個Provider對應的所有authority,每個authority屬性為key,儲存這個ProviderClientReocrd到mProviderMap描述的HashMap中。
在一個應用程序中有三個清單來儲存本程序中的ContentProvider的資訊。
1. ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
主要以authority為key,儲存providerClientRecord資訊
2. ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
以通信的接口Binder對象為key儲存ProviderClientRecord對象。主要儲存了本程序的ContentProvider的資訊
3. ArrayMap<ComponentName,ProviderClientRecord> mLocalProvidersByName
以Provider的ComponentName資訊為key 儲存ProviderClientRecord對象。主要儲存了本程序的ContentProvider的資訊
通過installProvider方法将ContentProvider的類加載到記憶體中來,并建立了ContentProvider的對象,調用了ContentProvider的onCreate來啟動它。然後将它按照不同的存儲類型分别儲存不同的ContentProvider集合中。
6. AMS.publishContentProviders過程
ContentProvider本地建立完成并儲存後,将它封裝成立一個ContentProviderHolder對象傳回,然後我們調用AMS的publishContentProviders方法,将這些Holder對象發送給AMS服務将他們釋出到AMS服務中。
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
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) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
//将ContentProvider的資訊按照Authority為key儲存到AMS的ProviderMap集合中
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
//查詢是否有程序在等待這個ContentProvider啟動,如果等待清單中有,從等待清單删除
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
synchronized (dst) {
//将AMS服務中這個ConentProviderRecord指向應用程序的一個ContentProviderClientRecord.
dst.provider = src.provider;
//将AMS服務中這個ConentProviderRecord指向一個對應的應用程序
dst.proc = r;
//通知這個ContentProvider已經啟動完成,結束等待
dst.notifyAll();
}
}
}
}
}
在前面generateApplicationProvidersLocked方法查詢在該程序運作的所有ContentProvider的資訊的時候,為每一個ContentProvider資訊建立了一個ContentProviderRecord對象,儲存到了ProviderMap集合中。那時候這些ContnetProvider還沒有啟動建立,隻是一些ContentProvider的資訊。這個方法就是要将建立并啟動的ContentProvider和ContentProviderRecord關聯起來。
首先将啟動的ContentProvider按照authority為key儲存到ProviderMap中。然後将ContentProviderRecord的provider指向應用程序中啟動的ContentProviderClientRecord對象,proc指向新啟動應用程序。這樣ContentProvider就在AMS服務中釋出完成了。釋出的工作主要就是将ContentProvider的資訊儲存到AMS服務中去。
AMS服務儲存ContentProvider的資訊主要是在類ProviderMap中,它裡邊有兩種儲存的Provider資訊的集合,以兩種不同的方式儲存了provider的資訊。
1. ProviderByClass
以ComponentName為key儲存了ContentProviderRecord的資訊
2. ProviderByName
以authority為key儲存了ContentProviderRecord的資訊
本程序的ContentProvider的啟動過程就分析完成了。
接下來幾行代碼和和不同程序間調用ContentProvider有關。我們順便分析下。
如果是不同程序間調用ContentProvider的時候,首先會判斷該ContentProvider所在的程序是否已經啟動,如果有啟動需要首先啟動該程序,在該程序啟動完成後這個ContentProvider也就啟動起來了。
如果沒有啟動的時候,AMS就會首先啟動個程序及ContentProvider,并把這個ContentProviderRecord添加到等待隊列mLaunchingProviders中去,然後等他它啟動完成。
此處代碼就是新的程序及ContentProvider啟動完成後,首先判斷是否在等待程序中,如果有,就将該ContentProvider資訊從等待隊列中移除,并調用notifyAll來喚醒等待的工作。
調用ContentProvider的過程分析
ContentProvider的調用方法:
getContext().getContentResolver().insert(uri);
我們就分析insert方法來分析ContentProvider的調用過程。
getContext最終的實作是在ContextImpl中,那我們就從ContextImpl的getContentResolver開始分析
1. ContextImpl.getContentResolver
public ContentResolver getContentResolver() {
return mContentResolver;
}
private final ApplicationContentResolver mContentResolver;
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);
}
從代碼中可以看出,ContextImpl的getContentResolver最終傳回的是一個ApplicationContentResolver的對象。這個ApplicationContentResolver是ContextImpl的一個内部類,繼承自ContentResolver。在ContextImpl建立的時候就建立了這個對象。
Insert方法最終就是要看ApplicationContentResolver的Insert方法的實作了。但是ApplicationContentResolver裡面并沒有Insert方法,是以最終還是要看ContentResolver的insert方法。
2. ContentResolver.insert
public final @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues values) {
Preconditions.checkNotNull(url, "url");
//擷取調用ContentProvider的接口
IContentProvider provider = acquireProvider(url);
try {
long startTime = SystemClock.uptimeMillis();
//調用contentProvider的insert方法
Uri createdRow = provider.insert(mPackageName, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
return createdRow;
} catch (RemoteException e) {
……
} finally {
releaseProvider(provider);
}
}
這個方法主要做了兩個工作:
第一. 通過acquireProvider方法擷取ContentProvider的通信接口,用戶端通過該通信接口可以調用服務端的ContentProvider的方法。
第二. 通過擷取的通信接口,調用ContentProvider的insert方法,插入資料。
ContentProvider的用戶端和服務端通信主要通過Binder間程序通信實作和AMS服務關系不是太大,此處不再詳細分析。主要分析擷取ContentProvider通信接口的過程。
調用ContentResolver的acquireProvider方法,該方法是一個abstrict 方法。在ApplicationContentResolver中實作了該方法。
3. ApplicationContentResolver.acquireProvider
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
mMainThread就是ActivityThread,此處直接調用ActivityThread的acquireProvider方法。
4. ActivityThread. acquireProvider
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
//首先檢視是否已經儲存了該ContentProvider的通信接口,已經儲存的話直接傳回
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
//未儲存的話,去AMS服務查詢
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
//根據查詢的結果,儲存到本地程序中
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
1. 通過acquireExistingProvider方法檢查本地程序是否已經儲存了該ContentProvider的通信接口,如果已經儲存的話直接傳回,不再需要去AMS服務查詢并在本地儲存安裝。
2. 如果本地沒有儲存,請求AMS服務的getContentProvider方法,去擷取該ContentProvider的資訊。
3. 根據從AMS服務擷取的資訊,将該ContentPrivider儲存在本地
5. ActivityThread. acquireExistingProvider
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
//在本程序的ProviderMap中查詢是否包含請求的ContentProvider資訊
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
}
//如果有的話,直接傳回并對該ContentProvider引用加1
IContentProvider provider = pr.mProvider;
IBinder jBinder = provider.asBinder();
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
incProviderRefLocked(prc, stable);
}
return provider;
}
}
這個方法的實作邏輯比較簡單,直接根據要請求ContentProvider的Uri從ProviderMap的集合中查詢,如果已經存在直接傳回該Provider的通信接口IContentProvider,并對它的引用加1。
一般情況下調用本程序的ContentProvider都是存在的,因為在程序啟動的時候這些ContentProvider就已進啟動完成,并儲存到相關的集合中去了。
如果不存在,那就應該是第一次調用其他程序的ContentProvider,接着往下分析。
6. AMS.getContentProvider
getContentProvider直接調用getContentProviderImpl方法來實作
private final 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.elapsedRealtime();
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
}
//首先檢查該ContentProvider是否已經釋出
cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
//已經釋出
if (providerRunning) {
cpi = cpr.info;
//檢查是否允許該ContentProvider在多個程序中運作,允許的話直接傳回Holder為Null
if (r != null && cpr.canRunHere(r)) {
ContentProviderHolder holder = cpr.newHolder(null);
holder.provider = null;
return holder;
}
boolean singleton;
//還未釋出
if (!providerRunning) {
……
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
//還未釋出的情況下cpr還沒有儲存,查詢ContentProvider資訊,并封裝成一個ContentProviderReocrd對象
final boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
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);
}
}
if (r != null && cpr.canRunHere(r)) {
//檢查是否允許該ContentProvider在多個程序中運作,允許的話直接傳回Holder為Null
return cpr.newHolder(null);
}
不允許的話,啟動ContentProvider及其所在的程序,把這個ContentProviderRecord添加到等待隊列中去
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
try {
//判斷該程序是否已經啟動,如果已經啟動的話直接調用scheduleInstallProvider來啟動該ContentProvider
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null) {
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");
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;
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
//将該ContentProviderRecord儲存到mProviderMap中
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!");
}
//一直等到這個Provider啟動完成之後喚醒
synchronized (cpr) {
……
cpr.wait();
……
}
}
}
return cpr != null ? cpr.newHolder(conn) : null;
}
這個方法的邏輯有點複雜。首先從mProviderMap中查詢這個ContentProvider是否存在。
1. 如果存在,表示已經釋出
a. 首先檢查該Provider是否允許在多個程序中運作。如果運作多個程序中都有執行個體的話,直接傳回ContentProviderHolder為null
一般情況下,ContentProvider的服務端都是運作在程序應用所在的程序中,但是如果ContentProvider的屬性multiProcess為true的話表示,該ContentProvider可以在多個程序中都有執行個體。
b. 不允許在多個程序中都有執行個體,直接傳回已經釋出的ContentProvider的Holder資訊
2. 如果不存在,表示該ContentProvider還沒有釋出
如果還沒有釋出,則首先去查詢該ContentProvider的ProviderInfo資訊。然後根據ProviderInfo,再去查詢ContentProviderRecord是否在ProviderMap中存在。第一次調用的話ContentProviderRecord肯定為null.然後去PMS服務中查詢ContentProvider資訊,建立一個ContentProviderRecord對象。
a. 判斷是否允許多個程序中都有執行個體,允許的話直接傳回Holder為Null
b. 不允許的允許多個程序中都有執行個體,需要啟動該ContentProvider及其所在的程序。
a) 該程序已經啟動
直接通知該程序安裝并啟動ContentProvider,并将該ContentProviderRecord儲存到ProviderMap中。然後添加到mLaunchingProviders等待隊列,調用wait方法等待該 ContentProvider啟動完成。
b) 該程序及ContentProvider都還未建立啟動
啟動該ContentProvider所在的程序,并啟動ContentProvider.此處邏輯和前面ContentProvider啟動過程一緻。将該ContentProviderRecord儲存到ProviderMap中。然後添加到mLaunchingProviders等待隊列,調用wait方法等待該ContentProvider啟動完成。
當ContentProvider啟動後,喚醒此處等待,然後傳回該ContentProviderHolder資訊