一、数据访问者
ContentProvider 提供了一种向外部进程暴露数据的通用方案,在进程中,数据任意存储方式,数据库、Sharedpreferences、xml、文件、网络。
外部进程通过统一的接口实现对进程的数据访问,数据提供者和访问者解耦,不用关心数据的存储方式。
我们在使用它时,比如向某个进程ContentProvider组件插入数据,在Activity组件中定义如下代码。
ContentResolver contentResolver = getContentResolver();
Uri uri = Uri.parse("content://xxx/xxx");
ContentValues values = new ContentValues();
values.put("name", "Demo");
Uri returnuir = contentResolver.insert(uri, values);
通过 Activity 组件调用 getContentResolver() 方法,获取一个 ContentResolver 访问者,ContentResolver 是抽象类。
统一资源标识符 Uri,匹配 ContentProvider,调用 ContentResolver 访问者的增删改查方法,访问者的 #insert() 方法实现插入。
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
Activity 组件 Wrapper 类中,调用 ContextImpl 类的 getContentResolver() 方法。
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
ContextImpl 构造方法,创建 ContentResolver。
private ContextImpl(ContextImpl container, ActivityThread mainThread,
LoadedApk packageInfo, ...) {
mOuterContext = this;
mMainThread = mainThread;
if (user == null) {
user = Process.myUserHandle();
}
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
}
ApplicationContentResolver 对象,继承 ContentResolver 抽象类,实现 acquireProvider() 方法。
public final Uri insert(Uri url, ContentValues values) {
//先ContentResolver的final方法
IContentProvider provider = acquireProvider(url);
//provider为空说明Uri无法识别,抛出异常
try {
long startTime = SystemClock.uptimeMillis();
Uri createdRow = provider.insert(mPackageName, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
return createdRow;
} catch (RemoteException e) {
return null;
} finally {
releaseProvider(provider);
}
}
acquireProvider() 方法,返回一个 IContentProvider,调用它的 insert() 方法。
二、IContentProvider 代理获取
查找 IContentProvider,子类 ApplicationContentResolver 实现方法。
public final IContentProvider acquireProvider(Uri uri) {
//SCHEME_CONTENT是"content"
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
final String auth = uri.getAuthority();
if (auth != null) {
//触发子类实现的acquireProvider方法
return acquireProvider(mContext, auth);
}
return null;
}
统一资源标识符,协议 content://,授权信息域名,是 ContentProvider 唯一的标识符,目录即是表名,文件,具体的某项记录ID
协议://域名/目录/文件#片段标示符。
ContentProvider 的 协议是 content,getAuthority() 方法,Uri 的 authorities 字符串,通过 UriMatcher 的 addURI 添加需匹配的 Uri,包括 authority,存储路径或者表名,以及匹配码。在 UriMatcher #match() 方法,若传入 Uri 匹配,返回匹配码。
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
调用 ActivityThread 类 acquireProvider() 方法,入参是解析 Uri 得到的 authority 字符。
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth,
userId, stable);
if (provider != null) {
return provider;
}
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
//无法找到auth对应的provider
return null;
}
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
acquireExistingProvider() 方法,根据 auth 和 userId 创建一个 ProviderKey,从本进程 ArrayMap 中查找 ProviderClientRecord 类,内部 IContentProvider。
// ActivityThread 类内部 ProviderMap,
// 保存本地 ProviderClientRecord。
ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap。
当本地 IContentProvider 不存在时,访问 Ams 服务 getContentProvider() 方法,请求 Ams 启动 ContentProvider,返回IActivityManager.ContentProviderHolder,IActivityManager 接口中定义的 Parcelable 类型,封装 IContentProvider。
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
//IApplicationThread是空抛出异常
return getContentProviderImpl(caller, name, null, stable, userId);
}
检查 ContentProvider 组件的进程,如果 ProcessRecord 是空,进程不存在,startProcessLocked() 方法,创建进程。
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null) {
//ContentProvider所在进程存在,但是还发布到 Ams。
if (!proc.pubProviders.containsKey(cpi.name)) {
//加入ContentProviderRecord记录,同时让组件进程启动
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
//ContentProvider所在进程还不存在,创建
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
if (proc == null) {
return null;
}
}
进程存在,但 ProcessRecord 内部 Map 没有保存 ContentProviderRecord,回调 App scheduleInstallProvider() 方法,在 ContentProviders 组件进程,启动 ContentProvider。
public void handleInstallProvider(ProviderInfo info) {
try {
installContentProviders(mInitialApplication, Lists.newArrayList(info));
} finally {
}
}
遍历 ProviderInfo, installProvider() 方法,生成 ContentProviderHolder。
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
for (ProviderInfo cpi : providers) {
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
}
}
将 ContentProviderHolder list,通知 Ams,publishContentProviders() 方法,将 远程 ContentProviderRecord 记录更新 ContentProviderHolder 中的 IContentProvider。
本地 installProvider() 方法,初始化 ContentProvider,将数据访问到业务接口 IContentProvider 注册到 Ams。
ContentProvider localProvider = null;
IContentProvider provider;
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
//通过 info 的 class name, newInstance() 创建,
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
provider = localProvider.getIContentProvider();
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
}
创建 ContentProvider 对象 localProvider,内部类 Transport ,即 IContentProvider。
attachInfo() 方法,初始化内部 Context,触发组件 onCreate() 生命周期方法。
将 IContentProvider 赋值给 ContentProviderHolder。
ProviderClientRecord 记录,封装 ContentProviderHolder,IContentProvider 和 ContentProvider。
最终,Ams 服务 getContentProviderImpl() 方法,返回 ContentProviderHolder 对象。
通过 ActivityThread 的 installProvider() 方法,(这一次是访问者进程调用,不是 ContentProvider 组件进程),不会执行 onCreate()方法。
本地进程创建 ProviderClientRecord 记录保存 ArrayMap,下次可以直接从本地获取 IContentProvider,即 acquireExistingProvider() 方法 ,更新 Ref 计数。
三、通信原理
数据提供者进程,当 Ams 服务发起 scheduleInstallProvider() 方法时,在 ActivityThread 类 installProvider() 方法,创建 ContentProvider 对象,执行 onCreate() 方法,ContentProvider 组件即完成启动,且 将 IContentProvider 发布到 Ams 服务。
访问者进程,通过 IContentProvider 的数据操作方法实现跨进程组件请求,底层 binder 通信,IContentProvider 是业务接口。
ContentProvider 是抽象类,子类实现增删查改数据操作。
//增
public abstract Uri insert(Uri uri, ContentValues values);
//删
public abstract int delete(Uri uri, String selection,
String[] selectionArgs);
//查
public abstract Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs,
String sortOrder);
//改
public abstract int update(Uri uri, ContentValues values,
String selection, String[] selectionArgs);
从 Ams 获取的 IContentProvider 是一个 binder 代理,ContentProvider 组件内部引用一个 Transport 对象。
private Transport mTransport = new Transport();
ContentProvider 内部类,继承 ContentProviderNative,是通信的服务端。IContentProvider 具体数据操作业务。
class Transport extends ContentProviderNative {
}
abstract public class ContentProviderNative extends Binder implements IContentProvider {
}
访问者进程触发从 Ams 服务返回的 IContentProvider 的 insert() 方法时,在数据提供者进程,触发 Transport 的 insert() 方法。
@Override
public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return rejectInsert(uri, initialValues);
}
final String original = setCallingPackage(callingPkg);
try {
//重写的ContentProvider的insert方法
return maybeAddUserId(ContentProvider.this.insert(uri, initialValues), userId);
} finally {
setCallingPackage(original);
}
}
进入我们重写 ContentProvider 子类的 insert() 方法,实现数据写入,自由选择数据持久化方案,其他的数据操作流程和 insert 相同。
四、总结
访问者,获取 ContentResolver 抽象类的实现对象,调用它的数据操作方法。
根据 Url,查找 IContentProvider,先本地 Map,找到 ProviderClientRecord,取内部 IContentProvider。
未找到,Ams 服务查询,返回 ContentProviderHolder,内部 IContentProvider,且创建一个 ProviderClientRecord 封装 IContentProvider 本地。
Ams 查询时,若未发布或进程未创建,提供者进程启动,组件创建,生命周期 执行,发布到 Ams。
数据通信原理是利用 binder 机制,访问者从 Ams 获取的 IContentProvider 即是业务接口代理对象。
提供者进程 ContentProvider 组件实现增删查改方法,内部 Transport 是业务接口服务端,接收到请求时, ContentProvider 组件对应实现方法。
任重而道远