基于版本:Android R
0. 前言
很早以前分析过service(Android 中service 详解) 和broadcast receiver(android 中的 Broadcast 机制详解) 的机制,最近在回顾的时候发现少了ContentProvider 和 Activity 的流程,抽些时间补充。这一篇主要分析ContentProvider 的原理。
《Android基础总结之八:ContentProvider》中举例说明ContentProvider 的调用流程,涉及到的接口比较多,我们这里以query 为入口进行剖析。
1. 入口--ContentResolver
调用接口类似:
ContentResolver cr = mContext.getContentResolver();
cr.query(...);
使用ContentResolver的原因:
- ContentResolver 是不同的应用与不同或相同的ContentProvider 交互的枢纽;
- ContentResolver 不但管理ContentProvider 状态的notify,也管理应用端ContentObserver的转接;
- 让架构更清晰,应用端只需要与ContentResolver 交互,ContentProvider只需要负责数据维护、数据状态更新;
- ContentProvider 为单独的进程,作为通信的server 端,ContentResolver 是应用为了交互使用,作为通信的Client 端;
1.1 getContentResolver
frameworks/base/core/java/android/app/ContextImpl.java
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
private ContextImpl(...) {
...
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
private static final class ApplicationContentResolver extends ContentResolver {
@UnsupportedAppUsage
private final ActivityThread mMainThread;
public ApplicationContentResolver(Context context, ActivityThread mainThread) {
super(context);
mMainThread = Objects.requireNonNull(mainThread);
}
...
}
ContentResolver的源头是ApplicationContentResolver,继承自ContentResolver。
ApplicationContentResolver中记录这App 的context 和mainThread。
1.2 ContentResolver 中重要的接口
frameworks/base/core/java/android/content/ContentResolver.java
public abstract class ContentResolver implements ContentInterface {
...
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
...
}
public final @Nullable String getType(@NonNull Uri url) {
...
}
public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
@Nullable ContentValues values) {
...
}
public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
@Nullable String[] selectionArgs) {
...
}
public final int update(@RequiresPermission.Write @NonNull Uri uri,
@Nullable ContentValues values, @Nullable String where,
@Nullable String[] selectionArgs) {
...
}
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
ContentObserver observer, @UserIdInt int userHandle) {
...
}
public final void unregisterContentObserver(@NonNull ContentObserver observer) {
...
}
}
这里没有细列举,应为ContentResolver和ContentProvider 都是继承自interface ContentInterface,所以,query/insert/delete/update/getType 等接口都是需要实现的。在此基础上ContentResolver还负责ContentObserver的register 和unRegister 工作。
2. query
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
Objects.requireNonNull(uri, "uri");
...
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
long startTime = SystemClock.uptimeMillis();
...
try {
qCursor = unstableProvider.query(mPackageName, mAttributionTag, 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(mPackageName, mAttributionTag, 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) {
...
} finally {
...
}
}
代码比较长,大概可以分以下几个部分:
- acquireUnstableProvider 获取ContentProvider 的proxy;
- 通过ContentProvider 的proxy 调用query 接口进行查询;
- 如果查询中unstable provider 出现dead,应用会尝试再拉一次;
- 实例CursorWrapperInner 并返回;
2.1 acquireUnstableProvider
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;
}
acquireUnstableProvider 是个抽象函数,实现在ApplicationContentResolver中:
frameworks/base/core/java/android/app/ContextImpl.java#ApplicationContentResolver
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
最终调用的是ActivityThread.acquireProvider。
2.1.1 acquireProvider
frameworks/base/core/java/android/app/ActivityThread.java
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;
}
try {
synchronized (getGetProviderLock(auth, userId)) {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
...
return null;
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
大概分三部分:
- acquireExistingProvider 函数用于确认是否ActivithThread 中已经存有provider,如果存有则直接返回,防止反复去AMS 中acquire;
- 通过AMS.getContentProvider 函数获取符合auth 和userId的 provider proxy;
- 通过installProvider 让ActivityThread 持有provider;
这里的AMS.getContentProvider 以单独的一节讲解,详细请看第 3 节。
这里的installProvider 关注3.4.4节;
另外,最终获取到的provider proxy 是通过getIContentProvider 接口而来,或者的流程详细见3.4.4节,getIContentProvider 接口讲解看第 4 节。
3. AMS.getContentProvider
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String callingPackage, String name, int userId,
boolean stable) {
...
return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
null, stable, userId);
}
getContentProviderImpl 代码比较长,这里分开剖析。
3.1 getRecordForAppLocked
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
...
synchronized(this) {
...
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
}
根据传入的caller,类型为IApplicationThread,获得调用者App 的ProcessRecord,要求App 必须是存在的。
3.2 确认provider 是否已经存在
// First check if this content provider has been published...
cpr = mProviderMap.getProviderByName(name, userId);
下面的代码主要是根据provider 是否已经发布进行分段,主要分下面几个部分:
- 如果provider 已经running;
- 如果 provider 尚未running;
- 如果provider 已经启动,当未发布;
- 如果provider 没有启动;
- 等待provider 发布;
3.3 如果provider已经running
if (providerRunning) {
cpi = cpr.info;
String msg;
//当允许运行在调用者进程且已发布,则直接返回
if (r != null && cpr.canRunHere(r)) {
// 检查权限
...
ContentProviderHolder holder = cpr.newHolder(null);
holder.provider = null; //注意这里的provider 为null,详细见3.4.4
return holder;
}
//如果provider不是在caller进程中,需要继续检查caller 的权限
...
//权限检查ok,则返回已经存在的provider 的connection,并增加引用计数
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null
&& r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
//更新进程LRU队列updateLruProcess");
mProcessList.updateLruProcessLocked(cpr.proc, false, null);
}
}
//更新进程adj
final int verifiedAdj = cpr.proc.verifiedAdj;
boolean success = updateOomAdjLocked(cpr.proc, true,
OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
success = false;
}
...
if (!success) {//如果失败了,provider进程已经被kill,减少引用计数
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
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.verifiedAdj = cpr.proc.setAdj;
}
...
}
如果ContentProvider 所在进程已经存在时:
- 权限检查;
- 当允许运行在调用者进程,直接返回;
- 增加引用计数;
- 更新进程LRU队列;
- 更新进程adj;
- 当provider被kill时,减少引用计数并调用appDiedLocked,且设置ContentProvider为未running状态,并记录dyingProc;
注意:当允许运行在发布者的进程中时,holder.provider 为null,installProvider 函数中需要重新创建provider,详细见3.4.4节。
3.4 如果provider没有running
if (!providerRunning) {
try {
//根据authority,获取ProviderInfo对象
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
} catch (RemoteException ex) {
}
if (cpi == null) {//不存在这个provider,则返回null
return null;
}
//provider是否为system,或者是PHONE_UID或者是persistent app
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r == null ? callingUid : r.uid,
cpi.applicationInfo.uid);
if (singleton) {//如果是标记UID 为system
userId = UserHandle.USER_SYSTEM;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
//check caller的权限
...
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
...
try {
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
if (ai == null) {
Slog.w(TAG, "No package info for content provider "
+ cpi.name);
return null;
}
ai = getAppInfoForUser(ai, userId);
//创建对象ContentProviderRecord
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} finally {
Binder.restoreCallingIdentity(ident);
}
} else if (dyingProc == cpr.proc && dyingProc != null) {
cpr = new ContentProviderRecord(cpr);
// This is sort of "firstClass"
firstClass = true;
}
if (r != null && cpr.canRunHere(r)) {
//可以运行在caller 进程中,详细见3.4.4节
return cpr.newHolder(null);
}
//从mLaunchingProviders中查询是否存在该cpr
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
//当provider并没有处于mLaunchingProviders队列,则启动它
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
try {
// Content provider is now in use, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
cpr.appInfo.packageName, false, userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ cpr.appInfo.packageName + ": " + e);
}
//查询进程记录ProcessRecord
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {
if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
"Installing in existing process " + proc);
if (!proc.pubProviders.containsKey(cpi.name)) {
proc.pubProviders.put(cpi.name, cpr);
try {
//provider进程启动,发布provider
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0,
new HostingRecord("content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name)),
ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
...
}
cpr.launchingApp = proc;
//将cpr添加到mLaunchingProviders
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
stable);
if (conn != null) {
conn.waiting = true;
}
}
如果ContentProvider 所在进程没有存在或被kill时:
- 根据auth 获取ProviderInfo;
- 权限检查;
- 当provider 不是运行在system 进程,且系统未准备好,则抛出异常;
- 当拥有该provider 的用户并没有运行,则直接返回;
- 根据ComponentName,从AMS.mProviderMap中查询相应的ContentProviderRecord;
- 当没有添加ContentProviderRecord 时,表示为首次调用,则创建ContentProviderRecord;
- 当允许运行在调用者的进程中,且ProcessRecord 不为空,则直接返回
- 当provider 并没有处于LaunchingProviders队列,则启动provider process;
- 增加引用计数
发布ContentProvider 分两种情况:
- provider 进程尚未启动;
对于此场景,system_server 进程调用startProcessLocked 创建provider进程,且attach 到system_server 后,通过binder call 到provider 进程执行ActivityThread.bindApplication 进行发布;
- provider 进程已启动,但未发布;
对于此场景,provider 进程已存在且attach 到system_server,但所对应的provider 还没有发布,通过binder call 到provider进程执行ActivityThread.scheduleInstallProvider 进行发布;
3.4.1 scheduleInstallProvider
frameworks/base/core/java/android/app/ActivityThread.java
注意,这里是Provider 进程
public void scheduleInstallProvider(ProviderInfo provider) {
sendMessage(H.INSTALL_PROVIDER, provider);
}
消息INSTALL_PROVIDER最终的处理函数为handleInstallProvider:
public void handleInstallProvider(ProviderInfo info) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
installContentProviders(mInitialApplication, Arrays.asList(info));
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
3.4.2 bindApplication
frameworks/base/core/java/android/app/ActivityThread.java
注意,这里是provider进程
private void handleBindApplication(AppBindData data) {
...
try {
...
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
...
注意bindApplication 是从attachApplicationLocked 中触发,而对于provider 在attach函数中埋下了ANR 的炸弹:
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg,
ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
}
消息为 CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG
timeout 为ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS(10s)
这个ANR 的炸弹需要在3.4.3 和之下的函数中拆除,详细看3.4.5节。
3.4.3 installContentProviders
3.4.1和3.4.2 两小节就是在provider没有运行下的两种场景,但最终调用的都是installContentProvider:
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
for (ProviderInfo cpi : providers) {
...
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) {
throw ex.rethrowFromSystemServer();
}
}
主要两件事:
- installProvider
- AMS.publishContentProviders
3.4.4 installProvider
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
if (DEBUG_PROVIDER || noisy) {
Slog.d(TAG, "Loading provider " + info.authority + ": "
+ info.name);
}
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;
} else {
try {
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
if (c == null) {
Slog.w(TAG, "Unable to get context for package " +
ai.packageName +
" while loading content provider " +
info.name);
return null;
}
if (info.splitName != null) {
try {
c = c.createContextForSplit(info.splitName);
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
}
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) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider.
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 {
provider = holder.provider;
if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
+ info.name);
}
ContentProviderHolder retHolder;
synchronized (mProviderMap) {
if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
+ " / " + info.name);
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, "
+ "using existing local provider");
}
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 {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, updating ref count");
}
// We need to transfer our new reference to the existing
// ref count, releasing the old one... but only if
// release is needed (that is, it is not running in the
// system process).
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;
}
代码比较长,逻辑大概分下面几部分:
- 当holder 为null 或者holder.provider为null 时:
- 当holder 为null时,是由于AMS.getContentProviderImpl 中一些条件限制;
- 当holder.provider 为null时,是由于AMS.getContentProviderImpl 中确定provider 可以在调用者的进程中发布,此时holder 不为null;
- 根据参数ProviderInfo 重新创建、init 一个localProvider(通过函数instantiateProvider)
- 通过localProvider.getIContentProvider 获取provider 代理,详细见第4节;
- 如果获取provider 成功,则调用localProvider.attachInfo,进入onCreate 流程
- 当holder 或holder.provider 都不为null时,表示provider已经创建成功
- 维护mLocalProvidersByName 和mProviderRefCountMap
attachInfo 这里暂时不分析了,代码比较简单,设置一些Provider 的权限和authority 并最终调用onCreate;
最终通过getIContentProvider 获取的provider 会存放在ContentProviderHolder 里面,这里很重要。可以接着看第4节。
3.4.5 AMS.publishContentProviders
代码比较多,这里不列举,主要做如下:
- 拆除ANR 的炸弹:
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
- 发布成功,notify 到3.5 节中的等待
synchronized (dst) {
dst.provider = src.provider;
dst.setProcess(r);
dst.notifyAll();
}
3.5 等待provider 发布
synchronized (cpr) {
while (cpr.provider == null) {
...
try {
final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
if (conn != null) {
conn.waiting = true;
}
cpr.wait(wait);
if (cpr.provider == null) {
timedOut = true;
break;
}
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
如3.4
至此,AMS.getContentProviderImpl 分析基本完成。
4. getIContentProvider
这个函数是在3.4.4 节中触发,在调用query / insert 等接口前首先要通过acquire 获取到provider proxy,最终就是通过getIContentProvider 而来:
frameworks/base/core/java/android/content/ContentProvider.java
public IContentProvider getIContentProvider() {
return mTransport;
}
private Transport mTransport = new Transport();
AMS 中在启动provider之后都将provider proxy 存放到ContentProviderRecord 中,并保存在AMS 的providerMap 中。
4.1 Tansport
class Transport extends ContentProviderNative {
abstract public class ContentProviderNative extends Binder implements IContentProvider {
...
}
Transport 是继承自ContentProviderNative,看完整个ContentProviderNative,会发现第 3.4.4 节中通过getIContentProvider 接口获取的provider 是Bn provider。即,在provider 的ActivityThread 中获取的provider 为Bn provider。
但是,当经过3.4.4节provider 获取后,紧接着会调用3.4.5节进行publish,而在publish 中会将ContentProviderHolder 作为参数传递给AMS。
---->来看下ContentProviderHolder
frameworks/base/core/java/andriod/app/ContentProviderHolder.java
public class ContentProviderHolder implements Parcelable {
...
@Override
public void writeToParcel(Parcel dest, int flags) {
info.writeToParcel(dest, 0);
if (provider != null) {
dest.writeStrongBinder(provider.asBinder());
} else {
dest.writeStrongBinder(null);
}
dest.writeStrongBinder(connection);
dest.writeInt(noReleaseNeeded ? 1 : 0);
}
public static final @android.annotation.NonNull Parcelable.Creator<ContentProviderHolder> CREATOR
= new Parcelable.Creator<ContentProviderHolder>() {
@Override
public ContentProviderHolder createFromParcel(Parcel source) {
return new ContentProviderHolder(source);
}
@Override
public ContentProviderHolder[] newArray(int size) {
return new ContentProviderHolder[size];
}
};
@UnsupportedAppUsage
private ContentProviderHolder(Parcel source) {
info = ProviderInfo.CREATOR.createFromParcel(source);
provider = ContentProviderNative.asInterface(
source.readStrongBinder());
connection = source.readStrongBinder();
noReleaseNeeded = source.readInt() != 0;
}
}
(1)ContentProviderHolder 为一个Parcelable,用以binder间数据传递;
(2)ContentProviderHolder.writeToParcel 是将provider.asBinder 写入data;
dest.writeStrongBinder(provider.asBinder());
若,provider 为Bn provier,即为ContentProviderNative,asBinder为自身,即remote 端。
public IBinder asBinder()
{
return this;
}
若,provider 为Bp provider,即为ContentProviderProxy(为什么是这个,后面讲),asBinder 为:
public IBinder asBinder()
{
return mRemote;
}
即,无论provider是Bn provider,还是Bp provider,只要以ContentProviderHolder 传递,就会将Bn provider 作为数据写入。
(3)通过CREATOR读取provider
provider = ContentProviderNative.asInterface(
source.readStrongBinder());
static public IContentProvider asInterface(IBinder obj)
{
if (obj == null) {
return null;
}
IContentProvider in =
(IContentProvider)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ContentProviderProxy(obj);
}
总结,如果ContentProviderHolder 作为参数传递,对端永远是ContentProviderProxy。即,provider 进程在创建后,无论是AMS 还是APP 拿到的provider 都是ContentProviderProxy,即都是用proxy 进行通信。
ok,终于解惑了provider 的产生和传递,回到第 2节query 接口,通过acquireUnstableProvider 获取到的provider 就是ContentProviderProxy。剩下来的就都是binder通信的问题啦,最终调用的肯定是Bn provider 中的query ,也就是Transport 中的query 接口,最终调用的当然是ContentProvider 中的query 接口。。。。。
5. 总结
至此,ContentProvider 机制就完整的剖析完了,中间可能过程还有些省略,如果有什么问题,可以留言!
下面做一些总结,大体分:
- 针对3.3 节当provider 已经运行的场景;
- 针对3.4节当provider 尚未运行的场景;
- 整体的调用流程图;
- ANR 炸弹埋下、拆除说明;
5.1 针对provider 已经运行的场景
- Client进程在获取provider的过程,发现cpr为空,则调用scheduleInstallProvider来向provider所在进程发出一个oneway的binder请求,并进入wait()状态.
- provider进程安装完provider信息,则notifyAll()处于等待状态的进程/线程;
如果provider在publish完成之后, 这时再次请求该provider,那就便没有的最右侧的这个过程,直接在AMS.getContentProviderImpl之后便进入AT.installProvider的过程,而不会再次进入wait()过程.
5.2 针对provider 尚未运行的场景
- client进程:通过binder(调用AMS.getContentProviderImpl)向system_server进程请求相应的provider;
- system进程:如果目标provider所对应的进程尚未启动,system_server会调用startProcessLocked来启动provider进程; 当进程启动完成,此时cpr.provider ==null,则system_server便会进入wait()状态,等待目标provider发布;
- provider进程:进程启动后执行完attch到system_server,紧接着执行bindApplication;在这个过程会installProvider以及 publishContentProviders;再binder call到system_server进程;
- system进程:再回到system_server,发布provider信息,并且通过notify机制,唤醒前面处于wait状态的binder线程;并将 getContentProvider的结果返回给client进程;
- client进程:接着执行installProvider操作,安装provider的(包含对象记录,引用计数维护等工作);
5.3 整体调用流程
5.4 ANR
在3.4.2 节已经说明在启动provider 进程时最终会调用attachApplicationLocked,并埋下ANR 炸弹。timeout 为:
ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS(10s)
在3.4.5 节中通过publish 接口拆除炸弹。其实这个时间都在AMS 和provider ActivityThread 的流程中,我们能做的就是onCreate()时尽可能的少做耗时操作。
OVER~~
参考: