一. 引言
作为四大组件之一的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
------------------------------------------------------分割线--------------------------------------------------
以下是本人的一些备注:
ActivityThread中的相关成员:
mProviderMap:ProviderKey由ContentProvider的authorith和userId组成;mProviderMap里面包含了acquireProvider得来的Transport,也包含和从本进程publish的ContentProvider。就是说ProviderClientRecord是本地Provider和从远端进程获得的Provider共用的一个结构体,即ProviderClientRecord可以表示一个本地Provider,也可以表示从远端进程获得的Provider(后面会称为远端Provider)。如果表示从远端进程获得的Provider,那么ContentProvider mLocalProvider成员为null。ContentProviderHolder是两者都有的一个成员。但是ContentProviderHolder#ContentProviderConnection只有表示从远端进程获得的Provider才不为null。
mProviderRefCountMap:是专门用于对远端Provider计数的。
mLocalProviders和mLocalProvidersByName:两者都只是包含本地Provider,两者的区别只在于key不一样。一个是IContentProvider作为key,一个是ComponentName作为key。
ContentProviderRecord创建于ActivityThread调用AMS#attachApplication时(如果Client acquireProvider()时,ContentProviderRecord没创建,则会创建,那么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;
}
文章一开始说被client在AMS调起的binder线程在等待另一binder线程。那么是怎么等待和唤醒的呢,看流程可以知道。在getContentProviderImpl中wait,而在publishContentProviders中notify,具体代码如下:
AMS#getContentProviderImpl()
// Wait for the provider to be published...
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();//Object.wait(),在这里等待provider发布
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
AMS#publishContentProviders()
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
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) {
dst.provider = src.provider;
dst.proc = r;
dst.notifyAll();//Object.notifyAll(),唤醒在等待队列中的binder线程
}