一 概述
ContentProvider 作为 Android 的四大组件之一,是出场最少的一个了,所以今天在我们介绍 ContentProvider 之前,先来回顾一下 ContentProvider 的使用方式。
二 使用方式
ContentProvider 作为内容提供者,一般涉及到两个进程,ContentProvider 的提供者所在的进程和使用者所在的进程(当然它也可以在一个进程内使用)。
2.1 提供者
作为提供者的一方,我们需要继承 ContentProvider,并实现它的抽象方法。下面就是 Google 提供的一个简单的 Demo
2.2 FileProvider
[development/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java]
public class FileProvider extends ContentProvider
implements PipeDataWriter<InputStream> {
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// content providers that support open and openAssetFile should support queries for all
// android.provider.OpenableColumns.
int displayNameIndex = -1;
int sizeIndex = -1;
// If projection is null, return all columns.
if (projection == null) {
projection = new String[] {
OpenableColumns.DISPLAY_NAME,
OpenableColumns.SIZE};
}
for (int i = 0; i < projection.length; i++) {
if (OpenableColumns.DISPLAY_NAME.equals(projection[i])) {
displayNameIndex = i;
}
if (OpenableColumns.SIZE.equals(projection[i])) {
sizeIndex = i;
}
}
MatrixCursor cursor = new MatrixCursor(projection);
Object[] result = new Object[projection.length];
for (int i = 0; i < result.length; i++) {
if (i == displayNameIndex) {
result[i] = uri.getPath();
}
if (i == sizeIndex) {
result[i] = null; // Size is unknown, so null, if it was known, it would go here.
}
}
cursor.addRow(result);
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// Don't support inserts.
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Don't support deletes.
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// Don't support updates.
return 0;
}
@Override
public String getType(Uri uri) {
// For this sample, assume all files are .apks.
return "application/vnd.android.package-archive";
}
...
}
作为内容提供者,需要实现以下五个函数
- onCreate :返回值表示当前内容提供者所在的进程是否加载完成
- query : 返回一个 Cursor 用来查询数据,使用完需要关闭
- insert :增
- delete : 删
- update :改
2.3 AndroidManifest.xml
<provider android:name=".content.FileProvider"
android:authorities="com.example.android.apis.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false"
android:enabled="@bool/atLeastHoneycombMR2" />
在 AndroidManifest 中配置对应的 provider 节点,并配置以下属性
- android:name : 文件路径
- android:authorities :URI ,授权方用于标识 Content Provider 提供的数据
- android:enabled :系统是否可以实例化 Content Provider。如果可以,则设为“true”;如果不能,则设为“false”。默认值为“true”。
接下来,就是使用方的示例。
2.4 使用者
ContentResolver cr = getContentResolver();
Uri uri = Uri.parse("uri ...");
//执行查询
Cursor c = cr.query(uri, null, null, null, null);
使用者的逻辑就相对比较简单了
- 首先是获取 ContentResolver
- 然后通过 Uri.parse 解析对应的 uri
- 最后通过 ContentResolver 和 Uri 进行增删改查
ContentResolver 它是一个抽象类,它实现了 ContentInterface 接口。而 ContentInterface 中,定义了所有的增删改查,即我们需要使用的相关 api。
接下来,我们就从使用者开始,看看 ContentResolver 具体的调用流程和原理。
三 Context
首先还是从 Context 出发,通过 getContentResolver 获取 ContentResolver。调用流程如下
- Context.getContentResolver
- ContextWrapper.getContentResolver
- ContextImpl.getContentResolver
3.1 getContentResolver
[frameworks/base/core/java/android/app/ContextImpl.java]
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
和其他的四大组件不同,ContentResolver 在 ContextImpl 中直接就有一个成员对象 mContentResolver。
3.2 ContextImpl
[frameworks/base/core/java/android/app/ContextImpl.java]
@UnsupportedAppUsage
private final ApplicationContentResolver mContentResolver;
这个成员对象的类型是 ApplicationContentResolver。
[frameworks/base/core/java/android/app/ContextImpl.java]
private ContextImpl (...)
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
并且这个对象还是在构造函数中初始化的。
3.3 ApplicationContentResolver
[frameworks/base/core/java/android/app/ContextImpl.java]
private static final class ApplicationContentResolver extends ContentResolver {
@UnsupportedAppUsage
private final ActivityThread mMainThread;
...
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
......
}
ApplicationContentResolver 是 ContextImpl 的静态内部类,它继承自 ContentResolver,在 ApplicationContentResolver 中保存了一个 ActivityThread 的对象,外部调用它时,其实都会调用到 ActivityThread。
接下来,我们看看 ApplicationContentResolver 的增删改查都是如何实现的。
四 ContentResolver
4.1 query
接下来我们通过跟踪 query 的调用,来看一下 ContentProvider 的使用者和提供者是如何交互的。
[frameworks/base/core/java/android/content/ContentResolver.java]
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
Objects.requireNonNull(uri, "uri");
... // 省略代码
// 通过 uri 获取 ContentProvider 的 BInder 对象
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
long startTime = SystemClock.uptimeMillis();
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
// Force query execution. Might fail and throw a runtime exception here.
qCursor.getCount();
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
// Wrap the cursor object into CursorWrapperInner object.
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
return wrapper;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
if (qCursor != null) {
qCursor.close();
}
if (cancellationSignal != null) {
cancellationSignal.setRemote(null);
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
}
}
首先,在 query 函数中,首先会 通过 acquireUnstableProvider 拿到一个 IContentProvider 对象。
4.2 IContentProvider
public interface IContentProvider extends IInterface {
这个 IContentProvider 对象是一个抽象类,它实现了 IInterface 接口,这个接口就是用于跨进程通信的。
public interface IInterface
{
public IBinder asBinder();
}
所以 IContentProvider 这个对象,就是用于跨进程通信的。
4.3 acquireUnstableProvider
在 query 中,首先就会通过 acquireUnstableProvider 获取到一个 IContentProvider,而这个 acquireUnstableProvider 在之前我们已经说了,它定义在 ApplicationContentResolver 中,但是实际上调用的却是 ActivityThread 中的函数。
[frameworks/base/core/java/android/app/ActivityThread.java]
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
4.3 ApplicationContentResolver.acquireUnstableProvider
[frameworks/base/core/java/android/app/ContextImpl.java]
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
最后会调用 mMainThread 中的 acquireProvider 函数。
五 ActivityThread
5.1 acquireProvider
[frameworks/base/core/java/android/app/ActivityThread.java]
@UnsupportedAppUsage
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;
}
// 如果不存在,就准备创建一个,但是为了避免多个线程同时获取
// 这里会有一个锁
ContentProviderHolder holder = null;
final ProviderKey key = getGetProviderKey(auth, userId);
try {
synchronized (key) {
// 通过 AMS 获取 IContentProvider
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
// 如果 holder 不为空,但是 provider 为空,那么久要等待
// 提供者的进程发布 provider,这里的超时时间是 10 秒(不是ANR)
if (holder != null && holder.provider == null && !holder.mLocal) {
synchronized (key.mLock) {
key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
holder = key.mHolder;
}
if (holder != null && holder.provider == null) {
// probably timed out
holder = null;
}
}
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
} catch (InterruptedException e) {
holder = null;
} finally {
synchronized (key.mLock) {
key.mHolder = null;
}
}
// 如果 holder 也没有,那么就会安装一个 IContentProvider
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
在 acquireProvider 中,主要就做了三件事
- 如果有可以复用的缓存,就直接使用 -> 调用 acquireExistingProvider [5.2]
- 如果没有可以复用的缓存,就新建一个 -> 调用 AMS.getContentProvider [6.1]
- 如果是新建的,就调用执行安装逻辑 -> 调用 installProvider [6.8]
5.2 acquireExistingProvider
acquireExistingProvider 就是从 ActivityThread 的成员变量 mProviderMap 中,取出对应的 IContentProvider。
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
}
IContentProvider provider = pr.mProvider;
IBinder jBinder = provider.asBinder();
if (!jBinder.isBinderAlive()) {
// Binder 所在的进程死亡,就不使用缓存
handleUnstableProviderDiedLocked(jBinder, true);
return null;
}
//
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
// 增加引用计数
incProviderRefLocked(prc, stable);
}
return provider;
}
}
缓存的逻辑很简单,就是从 mProviderRefCountMap 中取出。
5.3 小结
到此我们可以简单小结一下,目前有两个关键点,一个通过 AMS.getContentProvider 获取 IContentProvider,另一个是 ActivityThread 的 installProvider。
六 AMS
首先我们来看看 AMS 获取 IContentProvider 的逻辑。
6.1 getContentProvider
[frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java]
final ContentProviderHelper mCpHelper;
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String callingPackage, String name, int userId,
boolean stable) {
try {
return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable);
} finally {
}
}
在 AMS 中,存在一个 ContentProviderHelper 类型的对象 mCpHelper,通过调用 mCpHelper.getContentProvider 获得了 ContentProviderHolder。
6.2 ContentProviderHelper.getContentProvider
ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,
String name, int userId, boolean stable) {
... // 省略代码
return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
null, stable, userId);
}
6.3 getContentProviderImpl 中的数据结构
getContentProviderImpl 这个函数很长,所以这里我们分几个部分进行分析。
- 获取缓存的 ContentProviderRecord,并获取 ContentProvider 对应所在的进程
- ContentProvider 所在进程存活的情况
- ContentProvider 所在进程死亡的情况
为了方便理解,我们先看一下 ContentProviderRecord 这个对象的定义
6.3.1 ContentProviderRecord
final class ContentProviderRecord implements ComponentName.WithComponentName {
//
static final int MAX_RETRY_COUNT = 3;
// AMS
final ActivityManagerService service;
// 持有者的信息?
public final ProviderInfo info;
final int uid;
// 进程信息
final ApplicationInfo appInfo;
// 包名
final ComponentName name;
final boolean singleton;
// 它的 Binder 对象
public IContentProvider provider;
public boolean noReleaseNeeded;
// 所有连接的客户端
final ArrayList<ContentProviderConnection> connections
= new ArrayList<ContentProviderConnection>();
//final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
// Handles for non-framework processes supported by this provider
ArrayMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
// Count for external process for which we have no handles.
int externalProcessNoHandleCount;
int mRestartCount; // number of times we tried before bringing up it successfully.
ProcessRecord proc; // if non-null, hosting process.
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
String stringName;
String shortStringName;
ContentProviderRecord 是 AMS 用来记录 ContentProvider 的结构,它里面记录了有关 ContentProvider 的相关信息
- ProviderInfo:此 ContentProvider 在 AndroidManifest.xml 中的信息
- ComponentName:ContentProvider 的名称
- singleton:此 ContentProvider 是否是单例
- provider:此 ContentProvider 对应的 Binder 对象
- connections:当前 ContentProvider 所有的连接信息
- proc:此 proc 所在的进程信息
- launchingApp:等待 ContentProvider 启动的进程
6.3.2 ContentProviderConnection
public final class ContentProviderConnection extends Binder {
public final ContentProviderRecord provider;
// 请求使用 ContentProvider 的客户端
public final ProcessRecord client;
// 客户端包名
public final String clientPackage;
...
ContentProviderConnection 是 ContentProvider 和对应客户端的连接对象,它里面保存了请求 ContentProvider 的客户端信息,和对应的记录 ContentProviderRecord。
6.4 getContentProviderImpl
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;
final int expectedUserId = userId;
synchronized (mService) {
long startTime = SystemClock.uptimeMillis();
// 先检查调用者所在进程
ProcessRecord r = null;
if (caller != null) {
// 如果调用者所在的进程不存在(异常情况,直接抛出)
r = mService.getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid() + ") when getting content provider "
+ name);
}
}
boolean checkCrossUser = true;
// 检查 ContentProviderRecord 缓存是否存在(内容提供者是否已经发布)
cpr = mProviderMap.getProviderByName(name, userId);
// 如果没发布,并且当前用户不是系统用户
if (cpr == null && userId != UserHandle.USER_SYSTEM) {
// 那么就从系统用户中获取
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
if (mService.isSingleton(
cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)
&& mService.isValidSingletonCall(
r == null ? callingUid : r.uid, cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
cpr = null;
cpi = null;
}
}
}
ProcessRecord dyingProc = null;
if (cpr != null && cpr.proc != null) {
// ContentProvider 所在的进程是否存活,保存到 providerRunning 中
providerRunning = !cpr.proc.isKilled();
// 如果 ContentProviderRecord 所在的进程已经被杀了,
// 但是 appDiedLocked 还没用被调用,那么就保存到 dyingProc
// 后续需要对 dyingProc 做清理工作
if (cpr.proc.isKilled() && cpr.proc.isKilledByAm()) {
dyingProc = cpr.proc;
}
}
// 如果 ContentProviderRecord 的进程在运行
if (providerRunning) {
...
}
// 如果 ContentProvider 进程没用运行
if (!providerRunning) {
...
}
// 因为需要等待提供者发布,所以这里有一个死循环
synchronized (cpr) {
while (cpr.provider == null) {
...
}
}
}
第一部分的逻辑比较简单,就是先判断有没有缓存的 ContentProvider。
- 如果有,就看这个 ContentProvider 的进程是否存活,如果是则 providerRunning 为 true,否则为 false。
- 如果没有,providerRunning 就为 false
接下来,就会根据 providerRuning 的值为 true 或者 false 执行不同的逻辑。
6.5 进程存活
// 如果 ContentProviderRecord 的进程在运行
if (providerRunning) {
cpi = cpr.info;
// 如果调用者进程已启动
// 并且 ContentProviderRecord 可以在调用者进程中运行
// 如果 multiprocess 的设置为 true,那么这里 canRunHere 就为 true
// 那么就会【直接返回】ContentProviderHolder
if (r != null && cpr.canRunHere(r)) {
checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
cpr.name.flattenToShortString(), startTime);
ContentProviderHolder holder = cpr.newHolder(null, true);
// 【直接返回】不要给调用者提供对象
holder.provider = null;
return holder;
}
// 免安装应用不提供 ContentProvider
try {
if (AppGlobals.getPackageManager()
.resolveContentProvider(name, /*flags=*/ 0, userId) == null) {
return null;
}
} catch (RemoteException e) {
}
checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
cpr.name.flattenToShortString(), startTime);
final long origId = Binder.clearCallingIdentity();
try {
checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
// Return the provider instance right away since it already exists.
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage,
callingTag, stable, true, startTime, mService.mProcessList,
expectedUserId);
checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
final int verifiedAdj = cpr.proc.mState.getVerifiedAdj();
boolean success = mService.updateOomAdjLocked(cpr.proc,
OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
// 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.mState.getSetAdj()
&& !isProcessAliveLocked(cpr.proc)) {
success = false;
}
maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
Slog.i(TAG, "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.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable,
false, false);
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we will be killed during cleaning up, bail.
return null;
}
// We'll just start a new process to host the content provider
providerRunning = false;
conn = null;
dyingProc = cpr.proc;
} else {
cpr.proc.mState.setVerifiedAdj(cpr.proc.mState.getSetAdj());
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
如果 ContentProvider 所在的进程存活,那么执行 providerRunning 为 true 的逻辑。
- 注意,如果调用者的进程不为空,并且 ContentProvider 可以运行在调用者所在的进程中(即 AndroidManifest.xml 中的 multiprocess 为 true)。那么就会直接通过 ContentProviderRecord 创建出一个 ContentProviderHolder 对象,并且这个 holder 对象里的 provider 为空。直接返回此 holder。
- 这里 provider 为空是为了在调用者的进程中创建出对应的 ContentProvider,而不是提供者所在的进程。因为如果你在一个 App 中使用支付宝的付款功能,结果付款成功之后,进入了支付宝应用的界面,而不是你所在使用 App 的付款结果页,必然会导致你使用上的误解,所以这里需要在使用者进程中创建 ContentProvider,而不是提供者进程。
6.6 进程死亡
// 如果 ContentProvider 进程没用运行
if (!providerRunning) {
... // 省略代码
// 获取 ContentProvider 的进程,如果存在就直接使用
ProcessRecord proc = mService.getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid);
IApplicationThread thread;
if (proc != null && (thread = proc.getThread()) != null
&& !proc.isKilled()) {
if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
Slog.d(TAG, "Installing in existing process " + proc);
}
final ProcessProviderRecord pr = proc.mProviders;
if (!pr.hasProvider(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
pr.installProvider(cpi.name, cpr);
try {
thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
// 如果不存在,就走启动进程的逻辑
proc = mService.startProcessLocked(
cpi.processName, cpr.appInfo, false, 0,
new HostingRecord("content provider",
new ComponentName(
cpi.applicationInfo.packageName, cpi.name)),
Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false);
if (proc == null) {
// 如果启动失败,就返回空
return null;
}
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
... // 省略代码
}
如果 ContextProvider 的进程没有运行,那么就会走启动进程的逻辑。
6.7 等待发布
final long timeout =
SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
boolean timedOut = false;
synchronized (cpr) {
// 这里是个死循环
while (cpr.provider == null) {
// 处理异常情况,如果启动的进程失败就直接返回
if (cpr.launchingApp == null) {
return null;
}
try {
final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
if (conn != null) {
conn.waiting = true;
}
// 调用 ContentProviderRecord 的 wait 进入等待
cpr.wait(wait);
if (cpr.provider == null) {
timedOut = true;
break;
}
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
if (timedOut) {
// 如果超时,就返回为 null
String callerName = "unknown";
if (caller != null) {
synchronized (mService.mProcLock) {
final ProcessRecord record =
mService.mProcessList.getLRURecordForAppLOSP(caller);
if (record != null) {
callerName = record.processName;
}
}
}
return null;
}
// 正常返回,通过 ContentProviderConnection 创建一个 Holder 对象
return cpr.newHolder(conn, false);
最后,如果 ContentProviderRecord 里的 provider 为空,即没有发布,就会在一个死循环里等待发布。发布流程可以看 [7.3]。
我们再回到 [5.1],在经历了 AMS 的 getContentProvider 之后,会调用 installProvider 函数。
6.8 installProvider
[frameworks/base/core/java/android/app/ActivityThread.java]
@UnsupportedAppUsage
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
// localProvider 是一个 ContentProvider 对象,
// 而不是 IContentProvider 这个 Binder 对象
// 说明使用 localProvider 时是同一个进程间通信
ContentProvider localProvider = null;
IContentProvider provider;
... // 省略代码
if (holder == null || holder.provider == null) {
// 如果 holder 为空或者 holder.provider 为空,
// 就走同进程通信
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
provider = localProvider.getIContentProvider();
if (provider == null) {
return null;
}
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
if (!mInstrumentation.onException(null, e)) {
throw new RuntimeException(
"Unable to get provider " + info.name
+ ": " + e.toString(), e);
}
return null;
}
} else {
// 否则,就从 ContentProviderHolder 中取出 IContentProvider
provider = holder.provider;
}
ContentProviderHolder retHolder;
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
// localProvider 是否为空,即当前的 ContextProvider 和调用者是否是同一个进程
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;
} else {
// 跨进程通信,增加引用计数,
// 并且将 Binder 对象缓存到 mProviderRefCountMap 中
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
if (!noReleaseNeeded) {
incProviderRefLocked(prc, stable);
try {
ActivityManager.getService().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
if (noReleaseNeeded) {
prc = new ProviderRefCount(holder, client, 1000, 1000);
} else {
prc = stable
? new ProviderRefCount(holder, client, 1, 0)
: new ProviderRefCount(holder, client, 0, 1);
}
mProviderRefCountMap.put(jBinder, prc);
}
retHolder = prc.holder;
}
}
return retHolder;
}
installProvider 函数,看它名字久知道它的目的是安装 ContextProvider。在这个函数中,会根据调用者来源的不同来区分处理。
- 如果调用者和 ContextProvider 的提供者是同一个进程,那么就直接返回。
- 如果调用者和 ContextProvider 的提供者是不同进程,那么就需要增加计数器
七 ActivityThread
说完了 ContextProvider 的获取,再来说说 ContextProvider 的发布。之前在 [6.7] 我们看到了 ContextProvider 发布了就可以直接获取,而 ContextProvider 没有发布的时候,会有一个死循环。
7.1 handleBindApplication
接下来,我们就来看看 ContextProvider 是如何发布的。首先是进程的启动流程,在应用启动完毕后,会调用 handleBindApplication 创建 Application 并绑定上下午。
private void handleBindApplication(AppBindData data) {
if (!data.restrictedBackupMode) {
// 如果 App 的 providers 不为空,就先安装 providers
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
// 调用 Application 的 OnCreate 函数
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
在 handleBindApplication 中,我们居然发现 ContentProvider 的发布居然是在调用 Application 的 OnCreate 函数之前。所以之前有一些应用或者三方库会在 ContentProvider 进行初始化。
当然,现在建议不要这样做,因为 ContentProvider 从发布和创建也是需要消耗性能的,谷歌现在在 Jetpack 中已经提供了用于初始化的 App Startup。不要再用 ContentProvider 初始化了。
7.2 installContentProviders
@UnsupportedAppUsage
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
for (ProviderInfo cpi : providers) {
// 线调用 installProvider 安装 ContentProvider
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
// 然后再调用 AMS 的 publishContentProviders
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
在 ActivityThread 的 installContentProviders 中,会先调用 installProvider 安装 ContentProvider。然后再调用 AMS 的 来发布 publishContentProviders。
安装的逻辑在 [6.8] 中已经说了,我们接下来看看发布的逻辑。
7.3 ContentProviderHelper.publishContentProviders
AMS 的发布逻辑,其实也是调用它的 mCpHelper 对象的 publishContentProviders 函数,和之前的 [6.1] 一样。
void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
synchronized (mService) {
final ProcessRecord r = mService.getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when publishing content providers");
}
final long origId = Binder.clearCallingIdentity();
boolean providersPublished = false;
for (int i = 0, size = providers.size(); i < size; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
ContentProviderRecord dst = r.mProviders.getProvider(src.info.name);
if (dst == null) {
continue;
}
providersPublished = true;
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);
}
boolean wasInLaunchingProviders = false;
for (int j = 0, numLaunching = mLaunchingProviders.size(); j < numLaunching; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
numLaunching--;
}
}
// 移除超时消息
if (wasInLaunchingProviders) {
mService.mHandler.removeMessages(
ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst);
mService.mHandler.removeMessages(
ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
r.addPackage(dst.info.applicationInfo.packageName,
dst.info.applicationInfo.longVersionCode, mService.mProcessStats);
synchronized (dst) {
// 注意,这里给 ContentProviderRecord 的 provider 赋值
// 还记得 [6.7] 里的死循环吗
dst.provider = src.provider;
dst.setProcess(r);
// dst 是一个 ContentProviderRecord 对象,这里调用 notifyAll 通知之前 cpr.wait 的对象。
dst.notifyAll();
dst.onProviderPublishStatusLocked(true);
}
dst.mRestartCount = 0;
}
// update the app's oom adj value and each provider's usage stats
if (providersPublished) {
mService.updateOomAdjLocked(r, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
for (int i = 0, size = providers.size(); i < size; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
maybeUpdateProviderUsageStatsLocked(r,
src.info.packageName, src.info.authority);
}
}
Binder.restoreCallingIdentity(origId);
}
}
总结
最后还是来做一个简单的总结,先用一幅图来展示整个流程
- ContentProvider 的使用首先需要获取 ContentResolver,它实现了 ContentInterface 接口,这个接口里定义所有我们需要使用到的增删改查的 api。
- 在 ContentResolver 中,实际上调用的是 IContentProvider 对象,它继承自 IInterface,用于跨进程通信。
- getContentResolver.query 实际真正调用的是支持 binder 通信的代理对象 IContentProvider
- 在获取 IContentProvider 的过程中,会触发提供者进程的启动(如果需要的话)
- ContentProvider 启动支持在当前进程启动,也支持新建一个进程启动
- ContentProvider 启动过程的 OnCreate 方法调用会先于 Application 方法的 OnCreate 方法调用