目录引言1. 获取 provider1.1 等待 provider 发布1.2 安装 provider2. provider 实现多进程实例3. 两种 provider 区别结束引言
前面一篇文章分析了 AMS 端处理 provider 的逻辑,请读者务必仔细阅读前面一篇文章,否则看本文,你可能有很多疑惑。
以查询 provider 为例来分析客户端是如何处理 provider,它调用的是 ContentResolver#query()
// 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");
// ApplicationContentResolver 的 mWrapped 为 null
try {
if (mWrapped != null) {
return mWrapped.query(uri, projection, queryArgs, cancellationSignal);
}
} catch (RemoteException e) {
return null;
}
// 1. 获取 unstable provider
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 {
// 2. 执行操作
qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// 处理 unstable provider 进程挂掉的情况
// 通知 AMS,provider 进程挂掉了
unstableProviderDied(unstableProvider);
// 获取 stable provider,再次尝试获取数据
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);
// 注意,这里最终还是从 stable provider 获取 provider 接口
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
// 3. 返回数据
return wrapper;
} catch (RemoteException e) {
return null;
} finally {
// ...
}
}
纵观整个 provider 的查询过程,其实就是三步
我们注意到,代码中出现了两种 provider,unstable provider 和 stable provider。这两者的区别是,如果 provider 进程挂掉了,对于 stable provider,会杀死客户端进程,而 unstable 不会。这个我们会在后面分析。
现在我们要抓住重点,来分析如何获取 provider 。unstable provider 和 stable provider 的获取方式其实是一样的,本文只分析获取 unstbale provider。
对于 app 进程来说,ContentResolver 接口的实现类为 ApplicationContentResolver,获取 unstable provider 的操作最终会调用 ApplicationContentResolver#acquireUnstableProvider()
//ContextImpl.java
class ContextImpl {
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
}
}
原来最终是交给 ActivityThread 来获取 provider
// 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;
}
ContentProviderHolder holder = null;
// 合成一个 KEY
final ProviderKey key = getGetProviderKey(auth, userId);
try {
synchronized (key) {
// 1. 获取 ActivityManagerService 获取
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
// 2. 等待 provider 发布完成
// holder != null 表示 provider 存在
// holder.provider == null 表示 provider 正在发布中
// holder.mLocal 为 false,表示 provider 不是安装在客户端
if (holder != null && holder.provider == null && !holder.mLocal) {
synchronized (key.mLock) {
// 2.1 超时等等 provider 发布
// 超时时间一般为 20s
key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
// 这里可能因为超时被唤醒,获取的数据为空
// 也可以是因为provider发布完成,被AMS唤醒,holder 为AMS返回的数据
holder = key.mHolder;
}
// 2.2 确认是否是超时唤醒
if (holder != null && holder.provider == null) {
// probably timed out
holder = null;
}
}
}
}
// ...
// 这里记录了获取provider失败的日志
if (holder == null) {
if (UserManager.get(c).isUserUnlocked(userId)) {
Slog.e(TAG, "Failed to find provider info for " + auth);
} else {
Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)");
}
return null;
}
// 3. 成功从服务端获取 provider,本地安装它
holder = installProvider(c, holder, holder.info,
true , holder.noReleaseNeeded, stable);
return holder.provider;
}
客户端获取 provider 的过程大致分为如下几步
从前面的文章可知,当 provider 发布超时 或者 成功发布时,都会调用 ContentProviderRecord#onProviderPublishStatusLocked(boolean status) 来通知客户端 provider 的发布状态。参数 status 如果为 true,表示发布成功,如果为 false,表示发布超时。
// ContentProviderRecord.java
void onProviderPublishStatusLocked(boolean status) {
final int numOfConns = connections.size();
for (int i = 0; i < numOfConns; i++) {
// 遍历所有等待 provider 发布的客户端连接
final ContentProviderConnection conn = connections.get(i);
if (conn.waiting && conn.client != null) {
final ProcessRecord client = conn.client;
// 记录发布超时的日志
if (!status) {
// 从这里可以看出status为false时,不一定表示发布超时,还可能因为进程挂掉了
if (launchingApp == null) {
Slog.w(TAG_AM, "Unable to launch app "
+ appInfo.packageName + "/"
+ appInfo.uid + " for provider "
+ info.authority + ": launching app became null");
EventLogTags.writeAmProviderLostProcess(
UserHandle.getUserId(appInfo.uid),
appInfo.packageName,
appInfo.uid, info.authority);
} else {
Slog.wtf(TAG_AM, "Timeout waiting for provider "
+ appInfo.packageName + "/"
+ appInfo.uid + " for provider "
+ info.authority
+ " caller=" + client);
}
}
// 通知客户端
final IApplicationThread thread = client.getThread();
if (thread != null) {
try {
thread.notifyContentProviderPublishStatus(
newHolder(status ? conn : null, false),
info.authority, conn.mExpectedUserId, status);
} catch (RemoteException e) {
}
}
}
conn.waiting = false;
}
}
很简单,通过遍历所有等待 provider 发布的客户端连接,然后通过客户端 attach 的 thread 来通知它们。
// ActivityThread.java
public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder,
@NonNull String authorities, int userId, boolean published) {
final String auths[] = authorities.split(";");
for (String auth: auths) {
final ProviderKey key = getGetProviderKey(auth, userId);
synchronized (key.mLock) {
// 保存服务端传过来的数据
key.mHolder = holder;
// 唤醒等待provider的线程
key.mLock.notifyAll();
}
}
}
客户端收到信息后,唤醒了等待的线程,谁在等待呢?这里是不是有点熟悉,其实就是前面分析获取 provider 时,超时等待,部分代码如下
// ActivityThread.java
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// ...
try {
synchronized (key) {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
// 等待 provider 发布完成
if (holder != null && holder.provider == null && !holder.mLocal) {
synchronized (key.mLock) {
// 超时等待
key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
// 这里可能因为超时被唤醒,获取的数据为空
// 也可以是因为provider发布完成,被AMS唤醒,holder 为AMS返回的数据
holder = key.mHolder;
}
// 确认是否是超时唤醒
if (holder != null && holder.provider == null) {
// probably timed out
holder = null;
}
}
}
}
// ...
}
超时等待 provider 发布时,如果一旦被唤醒,再次获取 key.mHolder,因为如果成功发布,holder.provider 是不为空的,因为它就是 provider binder,否则就是超时唤醒。
客户端如果成功从 AMS 获取到 provider,那么就会安装它,其实这里的操作是保存数据,其实最主要的就是保存 provider 接口,同时也是保存 provider binder.
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
// 成功从 AMS 获取 provider,下面两个条件都是不成立
if (holder == null || holder.provider == null) {
// ...
} else {
// 获取 provider 接口,其实就是获取 provider binder
provider = holder.provider;
}
ContentProviderHolder retHolder;
synchronized (mProviderMap) {
// 从 provider 接口中获取 binder 对象
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
// ...
} else {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
// ...
} else {
// 1. 创建 provider 记录,并保存
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
// persistent app 的 provider 是不需要释放的
if (noReleaseNeeded) {
prc = new ProviderRefCount(holder, client, 1000, 1000);
} else {
prc = stable
? new ProviderRefCount(holder, client, 1, 0)
: new ProviderRefCount(holder, client, 0, 1);
}
// 2. 保存 provider 计数
mProviderRefCountMap.put(jBinder, prc);
}
retHolder = prc.holder;
}
}
return retHolder;
}
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider, ContentProviderHolder holder) {
final String auths[] = holder.info.authority.split(";");
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
// ...
// 创建一条 provider 记录
final ProviderClientRecord pcr = new ProviderClientRecord(
auths, provider, localProvider, holder);
// 一个 ContentProvider 可以声明多个 authority
for (String auth : auths) {
final ProviderKey key = new ProviderKey(auth, userId);
// mProviderMap 保存
final ProviderClientRecord existing = mProviderMap.get(key);
if (existing != null) {
Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+ " already published as " + auth);
} else {
mProviderMap.put(key, pcr);
}
}
return pcr;
}
很简单,就是用两个数据结构保存数据。
前面我们总是隐隐约约地提到,provider 可以安装在客户端进程,那么什么样的条件下,provider 可以安装在客户端进程中? 前面一篇文章的分析中有提到过,现在展示出部分代码
// ContentProviderHelper.java
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
// ...
synchronized (mService) {
// 获取客户端的进程实例
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);
}
}
// ...
// provider 正在运行
if (providerRunning) {
cpi = cpr.info;
if (r != null && cpr.canRunHere(r)) {
// This provider has been published or is in the process
// of being published... but it is also allowed to run
// in the caller's process, so don't make a connection
// and just let the caller instantiate its own instance.
ContentProviderHolder holder = cpr.newHolder(null, true);
// don't give caller the provider object, it needs to make its own.
holder.provider = null;
return holder;
}
// ...
}
// provider 没有运行
if (!providerRunning) {
// ...
if (r != null && cpr.canRunHere(r)) {
// If this is a multiprocess provider, then just return its
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
return cpr.newHolder(null, true);
}
// ...
}
// ...
}
// ...
}
可以看到,无论 provider 是否已经运行,都有机会在客户端进程中创建 provider 实例,而这个机会就在 ContentProviderRecord#canRunHere()
provider 已经运行,居然还可以运行在客户端进程中,也就是在客户端进程中创建 ContentProvider 实例,这样的设计又是为了什么呢?
public boolean canRunHere(ProcessRecord app) {
// info 为 provider 信息,也就是在 AndroidManifest 中声明的 provider 信息
// provider 可以 运行在客户端进程中的条件
// 1. provider 所在的 app 的 uid 与客户端 app 的 uid 相同
// 2. provider 支持多进程 或者 provider 的进程名与客户端 app 的进程名相同
return (info.multiprocess || info.processName.equals(app.processName))
&& uid == app.info.uid;
}
这里的条件可要看清楚了,首先 provider 所在 app 和 客户端 app 的 uid 相同,其实就是下面这个玩意要一样
<manifest
android:sharedUserId="">
然后,还需要 provider 支持多进程,其实就是下面这个玩意
<provider
android:multiprocess="true"/>
如果 provider 不支持多进程,只要 provider 的进程名与客户端 app 的进程名一样,provider 也是可以运行在客户端进程中。那么 provider 进程名是什么呢? provider 可以声明自己的进程名,如下
<provider
android:process=""
/>
而如果 provider 没有声明自己的进程名,那么 provider 进程名取自 app 的进程名。
现在 provider 怎样运行在客户端进程中,大家会玩了吗?如果会玩了,那么继续看下客户端如何安装 provider,这一次可就是真的安装 provider 了
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
// 此时 holder.provider == null 是成立的
if (holder == null || holder.provider == null) {
// ...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
// 1. 通过反射创建 ContentProvider 对象
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
// 获取 provider 接口,其实就是获取 provider binder
provider = localProvider.getIContentProvider();
if (provider == null) {
return null;
}
// 2. 为 ContentProvider 对象保存 provider 信息,并且调用 ContentProvider#onCreate()
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
// ...
}
} else {
// ...
}
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) {
// ...
} else {
// 本地创建 ContentProviderHolder
holder = new ContentProviderHolder(info);
// 保存 provider binder
holder.provider = provider;
// 本地安装的 provider,不需要释放
holder.noReleaseNeeded = true;
// 3. 创建 provider 记录,并保存
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else {
// ...
}
}
return retHolder;
}
其实这一部分代码在前面文章中已经分析过,这里简单介绍下过程
前面我们提到过 unstable provider 和 stable provider 的区别,现在我们用代码来解释下这两者的区别
假设我们通过 ActivityManager#forceStopPackage() 来杀掉 provider 进程,在 AMS 的调用如下
public void forceStopPackage(final String packageName, int userId) {
// ...
try {
IPackageManager pm = AppGlobals.getPackageManager();
synchronized(this) {
int[] users = userId == UserHandle.USER_ALL
? mUserController.getUsers() : new int[] { userId };
for (int user : users) {
// ...
if (mUserController.isUserRunning(user, 0)) {
// 杀掉进程
forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
// 发送广播
finishForceStopPackageLocked(packageName, pkgUid);
}
}
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
最终调用如下代码
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, int userId, String reason) {
// ...
// 获取 app 的所有 provider
ArrayList<ContentProviderRecord> providers = new ArrayList<>();
if (mCpHelper.getProviderMap().collectPackageProvidersLocked(packageName, null, doit,
evenPersistent, userId, providers)) {
if (!doit) {
return true;
}
didSomething = true;
}
// 移除 provider
for (i = providers.size() - 1; i >= 0; i--) {
mCpHelper.removeDyingProviderLocked(null, providers.get(i), true);
}
// ...
}
不出意外,最终由 ContentProviderHelper 来移除 provider
boolean removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr,
boolean always) {
// ...
for (int i = cpr.connections.size() - 1; i >= 0; i--) {
ContentProviderConnection conn = cpr.connections.get(i);
// ...
ProcessRecord capp = conn.client;
final IApplicationThread thread = capp.getThread();
conn.dead = true;
// 1. 如有 stable provider 的客户端
if (conn.stableCount() > 0) {
final int pid = capp.getPid();
// 注意,要排除 persistent app 进程,以及 system_server 进程
if (!capp.isPersistent() && thread != null
&& pid != 0 && pid != ActivityManagerService.MY_PID) {
// 杀掉客户端进程
capp.killLocked(
"depends on provider " + cpr.name.flattenToShortString()
+ " in dying proc " + (proc != null ? proc.processName : "??")
+ " (adj " + (proc != null ? proc.mState.getSetAdj() : "??") + ")",
ApplicationExitInfo.REASON_DEPENDENCY_DIED,
ApplicationExitInfo.SUBREASON_UNKNOWN,
true);
}
}
// 2. 如果只有 unstable provider 客户端
else if (thread != null && conn.provider.provider != null) {
try {
// 通知客户端移除数据
thread.unstableProviderDied(conn.provider.provider.asBinder());
} catch (RemoteException e) {
}
// In the protocol here, we don't expect the client to correctly
// clean up this connection, we'll just remove it.
cpr.connections.remove(i);
if (conn.client.mProviders.removeProviderConnection(conn)) {
mService.stopAssociationLocked(capp.uid, capp.processName,
cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
}
}
}
// ...
}
看到了吧,对于 stable provider,如果 provider 进程挂掉了,那么客户端也会受牵连被杀掉。
而对于 unstable provider,如果 provier 进程挂掉了,客户端只是移除了保存了的数据而已,并不会被杀掉。
最后,我们再来看看文章开头获取 provider 时关于两种 provider 代码
// ContentResolver.java
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
// ...
// 获取 unstable provider
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
// ...
// 注意,这里获取的 stable provider 并返回
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
// 返回数据
return wrapper;
} catch (RemoteException e) {
return null;
} finally {
// ...
}
}
我们可以看到,查询的时候使用的是 unstable provier,但是返回的结果 Curosr 使用的是 stable provider。这说明了什么? 它说明了,在 Cursor 没有被 close 之前,只要 provider 进程挂掉了,那么客户端也会受牵连,会被杀掉。
以上就是ContentProvider客户端处理provider逻辑分析的详细内容,更多关于ContentProvider客户端provider的资料请关注编程网其它相关文章!
--结束END--
本文标题: ContentProvider客户端处理provider逻辑分析
本文链接: https://www.lsjlt.com/news/169831.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-01-21
2023-10-28
2023-10-28
2023-10-27
2023-10-27
2023-10-27
2023-10-27
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0