ContentProvider Timeout
Base on: Android 12
ContentProvider 超时指的是ContentProvider发布时的超时,超时时间为CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s。ContentProvider 超时与Service、BroadcastQueue完全不同, 因为ContentProvider 的发布是在所在进程启动时进行的。
ContentProvider 超时比较少见。
发送延时消息
进程创建后,从主线程开始调用:
ActivityThread.main() ->
ActivityThread.attach() ->
AMS.attachApplication() ->
AMS.attachApplicationLocked()
ContentProvider 的发布在AMS.attachApplicationLocked()中进行的。
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
ProcessRecord app;
long startTime = SystemClock.uptimeMillis();
long bindApplicationTimeMillis;
//......
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
//通过PMS查找应用在Manifest中注册的ContentProvider
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
//如果该进程有ContentProvider,并且也存在待发布的ContentProviderRecord
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);
}
//......
}
移除延时消息
在进程启动后,进入到进程的main方法,通过调用:
ActivityThread.main() ->
ActivityThread.attach() ->
AMS.attachApplication() ->
AMS.attachApplicationLocked()
ApplicationThread.bindApplication(){
sendMessage(H.BIND_APPLICATION, data)
} ->
ActivityThread.handleBindApplication() ->
ActivityThread.installContentProviders() ->
AMS.publishContentProviders(){
removeMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG)
}
移除延时消息是在AMS.publishContentProviders()中进行的,首先查询对应的ContentProviderRecord在所在进程中是否已存在,若已存在,则表示启动成功,然后从mLaunchingProviders列表中移除,再移除之前的延时消息,removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r)。
代码如下:
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
//......
final int N = providers.size();
for (int i = 0; i < N; i++) {
//......
//
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
//查询ContentProvider所在进程中是否有对应的ContentProviderRecord
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) { //查询到了进程提供的ContentProvider
//......
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
//从mLaunchingProviders中移除已启动的ContentProvider
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
// ......
//通知ContentProvider已经启动完成,结束等待
synchronized (dst) {
dst.provider = src.provider;
dst.setProcess(r);
dst.notifyAll();
}
//......
}
}
}
}
执行ANR流程
通过handleMessage执行CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息,AMS会kill掉已经启动的App, 然后根据需要决定是否重启App。执行栈为:
AMS.processContentProviderPublishTimedOutLocked() ->
ProcessList.removeProcessLocked() ->
ProcessRecord.kill()
void kill(String reason, @Reason int reasonCode, @SubReason int subReason, boolean noisy) {
if (!killedByAm) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
if (mService != null && (noisy || info.uid == mService.mCurOomAdjUid)) {
mService.reportUidInfoMessageLocked(TAG,
"Killing " + toShortString() + " (adj " + setAdj + "): " + reason,
info.uid);
}
if (pid > 0) {
mService.mProcessList.noteAppKill(this, reasonCode, subReason, reason);
EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
Process.killProcessQuiet(pid);
ProcessList.killProcessGroup(uid, pid);
} else {
pendingStart = false;
}
if (!mPersistent) {
killed = true;
killedByAm = true;
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
注意:ContentProvider Timeout不会像input、Service 和Broadcast那样调用appNotResponding(),所以不会有ANR弹窗以及am_anr日志。