一. 引言
作爲四大組件之一的ContentProvider,相比來說是設計得稍遜色,有些地方不太合理,比如provider級聯被殺, 請求provider時佔用system_server的binder線程來wait()等。
即便很少自定義ContentProvider,但你也可以會需要使用到ContentProvider,比如通信錄,Settings等; 使用Provider往往跟數據庫結合起來使用,所以這裏需要注意不要再主線程用provider做過多的io操作。
二. ContentProvider數據結構
先以一幅圖來展示AMS管理ContentProvider所涉及的相關數據結構: 點擊查看大圖
2.1 ContentProviderRecord
- provider:在ActivityThread的installProvider()過程,會創建ContentProvider對象, 該對象有一個成員變量Transport,繼承於ContentProviderNative對象,作爲binder服務端。經過binder傳遞到system_server 進程的便是ContentProvider.Transport的binder代理對象, 由publishContentProviders()過程完成賦值;
- proc:記錄provider所在的進程,是在publishContentProviders()過程完成賦值;
- launchingApp:記錄等待provider所在進程啓動,getContentProviderImpl()過程執行創建進程之後賦值;
- connections:記錄該ContentProvider的所有連接信息,
- 添加連接過程:incProviderCountLocked
- 減少連接過程:decProviderCountLocked,removeDyingProviderLocked,cleanUpApplicationRecordLocked;
- externalProcessTokenToHandle: 數據類型爲HashMap<IBinder, ExternalProcessHandle>.
- AMS.getContentProviderExternalUnchecked()過程會添加externalProcessTokenToHandle值;
- CPR.hasConnectionOrHandle()或hasExternalProcessHandles()都會判斷該變量是否爲空.
2.2 ContentProviderConnection
功能:連接contentProvider與請求該provider所對應的進程
- provider:目標provider所對應的ContentProviderRecord結構體;
- client:請求該provider的客戶端進程;
- waiting:該連接的client進程正在等待該provider發佈
2.3 ProcessRecord
- pubProviders: ArrayMap<String, ContentProviderRecord>
- 記錄當前進程所有已發佈的provider;
- conProviders: ArrayList
- 記錄當前進程跟其他進程provider所建立的連接
2.4 AMS
- mProviderMap記錄系統所有的provider信息;
- mLaunchingProviders記錄當前正在啓動的provider;
2.5 ActivityThread
- mProviderMap: 記錄App端的所有provider信息;
- mProviderRefCountMap:記錄App端的所有provider引用信息;
三. Provider使用過程
更多源碼詳細過程,見理解ContentProvider原理
以上博文轉自gityuan的四大組件之ContentProviderRecord
------------------------------------------------------分割線--------------------------------------------------
以下是本人的一些備註:
ContentProviderRecord創建於ActivityThread調用AMS#attachApplication時,而installContentPrivders是在AMS調用IApplicationThread#bindApplication時,在AT中執行的。
AMS#attachApplication
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
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) {
}
if (DEBUG_MU) Slog.v(TAG_MU,
"generateApplicationProvidersLocked, app.info.uid = " + app.uid);
int userId = app.userId;
if (providers != null) {
int N = providers.size();
app.pubProviders.ensureCapacity(N + app.pubProviders.size());
for (int i=0; i<N; i++) {
// TODO: keep logic in sync with installEncryptionUnawareProviders
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags);
if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
// This is a singleton provider, but a user besides the
// default user is asking to initialize a process it runs
// in... well, no, it doesn't actually run in this process,
// it runs in the process of the default user. Get rid of it.
providers.remove(i);
N--;
i--;
continue;
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
mProviderMap.putProviderByClass(comp, cpr);
}
if (DEBUG_MU) Slog.v(TAG_MU,
"generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
app.pubProviders.put(cpi.name, cpr);
if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
mProcessStats);
}
notifyPackageUse(cpi.applicationInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
}
}
return providers;
}
ActivityThread#handleBindApplication(AppBindData data)
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
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);
}
}
ContentProviderConnection在應用進程向AMS獲取IContentProvider時創建
getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId)
conn = incProviderCountLocked(r, cpr, token, stable);
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable)
if (r != null) {
for (int i=0; i<r.conProviders.size(); i++) {
ContentProviderConnection conn = r.conProviders.get(i);
if (conn.provider == cpr) {
if (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER,
"Adding provider requested by "
+ r.processName + " from process "
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
if (stable) {
conn.stableCount++;
conn.numStableIncs++;
} else {
conn.unstableCount++;
conn.numUnstableIncs++;
}
return conn;//若Connection已存在,則count++後返回,否則創建一個
}
}
//若Connection不存在則創建一個ProcessRecord與該ContentProvider的連接
ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
if (stable) {
conn.stableCount = 1;
conn.numStableIncs = 1;
} else {
conn.unstableCount = 1;
conn.numUnstableIncs = 1;
}
cpr.connections.add(conn);
r.conProviders.add(conn);
startAssociationLocked(r.uid, r.processName, r.curProcState,
cpr.uid, cpr.name, cpr.info.processName);
return conn;
}
cpr.addExternalProcessHandleLocked(externalProcessToken);
return null;
}