成体系学习ANR系列,代码是基于 Android 15。如果你想成体系学习 ANR,可以关注并点击文章下面的合集
ContentProvider Timeout
是位于ActivityManager
线程中的AMS.MainHandler
收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG
消息时触发。ContentProvider
超时为CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s
.
ContentProvider
的 ANR 检测跟 Service 不太一样,他是在每次应用启动进程启动时进行的检测。
埋炸弹
埋炸弹的过程其实是在进程创建的过程,进程创建后会调用attachApplicationLocked()
进入system_server
进程的ActivityManagerService
中:
ini
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid); // 根据pid获取ProcessRecord
}
}
...
//系统处于ready状态或者该app为FLAG_PERSISTENT进程则为true
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
// APP 是否有Providers
List<ProviderInfo> providers = normalMode ?
generateApplicationProvidersLocked(app) : null;
//app进程是否是新创建,则发送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG=10
//的超时检测消息
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
// 通知 ActivityThread 的 bindApplication
thread.bindApplication(...);
...
}
arduino
// 10s
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
阶段总结下上面的代码逻辑:
- 应用进程启动时,
ActivityManagerService
会检测当前的应用是否有声明ContentProvider
- 检测当前的应用进程是否是新建,是否有需要被启动的
ContentProvider
- 如果有
ContentProvider
类型的任务,则开始抛出一个CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000
10s 的延时消息,这个消息会出发 ANR 检测,相当于是埋雷阶段
拆炸弹
我们知道应用进程在启动时候,调用installContentProviders
安装对应的 Providers
,如下
java
@UnsupportedAppUsage
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
for (ProviderInfo cpi : providers) {
......
// 安装 provider
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
// 通知 AMS 安装成功
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
当 ContentProvider
安装 install 成功后,就会通过 publishContentProviders
通知 AMS 拆除ANR 检测炸弹,如下是 AMS 中的publishContentProviders
方法:
java
@Override
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
......
......
try {
mCpHelper.publishContentProviders(caller, providers);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
在 Android 15 上把 publish 的逻辑都封装到了ContentProviderHelper
类中
ini
void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
synchronized (mService) {
final ProcessRecord r = mService.getRecordForAppLOSP(caller);
.......
boolean providersPublished = false;
for (int i = 0, size = providers.size(); i < size; i++) {
ContentProviderHolder src = providers.get(i);
......
// 是否publish 成功
boolean wasInLaunchingProviders = false;
for (int j = 0, numLaunching = mLaunchingProviders.size(); j < numLaunching; j++) {
if (mLaunchingProviders.get(j) == dst) {
// 将该provider移除mLaunchingProviders队列
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
numLaunching--;
}
}
// 成功pubish则移除该消息
if (wasInLaunchingProviders) {
mService.mHandler.removeMessages(
ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst);
// 移除ANR 检测任务
mService.mHandler.removeMessages(
ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
......
}
......
}
}
上面代码阶段总结下:
- 应用进程在 bindApplication 阶段会现调用
installContentProviders
安装对应的Providers
- 安装完成后,应用进程通过调用 AMS的
publishContentProviders
通知 AMS 安装完成,并移除 ANR 检测任务,也就是拆除炸弹
引爆炸弹
上面说了正常的流程是那样的,但是如果不正常呢?也就是炸弹没有被拆除,那怎么搞呢?我们看下 ASM 中如果接收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG.
时,会干啥。其中MainHandler
是 AMS
的内部类
java
final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
...
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
// 处理ContentProvider ANR 的情况
mCpHelper.processContentProviderPublishTimedOutLocked(app);
}
} break;
...
}
...
}
}
继续看ContentProviderHelper
类中的processContentProviderPublishTimedOutLocked
方法:
typescript
@GuardedBy("mService")
void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
// 移除一些Provider 相关的东西
cleanupAppInLaunchingProvidersLocked(app, true);
// 处理 ANR
mService.mProcessList.removeProcessLocked(app, false, true,
ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
ApplicationExitInfo.SUBREASON_UNKNOWN,
"timeout publishing content providers");
}
阶段总结下:
- 如果没有及时移除
CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG
表示发生了 ANR,AMS 会执行processContentProviderPublishTimedOutLocked
方法 - 清除处理一些和
ContentProvider
相关的逻辑 - 杀死应用进程
- 注意:这里不像其他的类型 ANR,发生 ANR 后,并不会调用
appNotResponding()
。
关注 "代码和生活手记" 系列学习 Android 进阶知识