什么是ANR
ANR(Application Not Responding)就是應(yīng)用在規(guī)定的時(shí)間內(nèi)沒有響應(yīng)用戶輸入或者其他應(yīng)用或者系統(tǒng)服務(wù)。
發(fā)生ANR的場(chǎng)景
- Service超時(shí)
Service ANR一般是指AMS通過binder IPC調(diào)用Service CMD,如果在規(guī)定的時(shí)間內(nèi)沒有收到Service宿主進(jìn)程反饋的Service CMD執(zhí)行完成的消息焕盟,就會(huì)觸發(fā)Service ANR桃焕。這里Service CMD包括CREATE_SERVICE寸宏、BIND_SERVICE幕袱、UNBIND_SERVICE仪际、STOP_SERVICE等。
接下來我們以startService為例分析Service Timeout ANR的流程宪迟,如圖所示:
我們從startService開始分析Service超時(shí)ANR的流程酣衷。
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId)
throws TransactionTooLargeException {
...
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
startService進(jìn)行一些簡(jiǎn)單的檢查工作后,直接調(diào)用ActiveServices對(duì)象的startServiceLocked踩验,進(jìn)一步調(diào)用bringUpServiceLocked來啟動(dòng)Service鸥诽。
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
//Slog.i(TAG, "Bring up service:");
//r.dump(" ");
// 檢查服務(wù)是否已經(jīng)運(yùn)行
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
...
if (!isolated) {
// 服務(wù)運(yùn)行在宿主應(yīng)用的進(jìn)程
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
// 運(yùn)行服務(wù)
realStartServiceLocked(r, app, execInFg);
return null;
} catch (TransactionTooLargeException e) {
throw e;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
} else {
// 服務(wù)運(yùn)行在單獨(dú)的進(jìn)程
// If this service runs in an isolated process, then each time
// we call startProcessLocked() we will get a new isolated
// process, starting another process if we are currently waiting
// for a previous process to come up. To deal with this, we store
// in the service any current isolated process it is running in or
// waiting to have come up.
app = r.isolatedProc;
}
...
}
bringUpServiceLocked首先進(jìn)行一些檢查工作兽泣,如果Service運(yùn)行在宿主應(yīng)用负蚊,調(diào)用realStartServiceLocked運(yùn)行服務(wù);如果Service運(yùn)行在單獨(dú)的進(jìn)程雨饺,首先要?jiǎng)?chuàng)建一個(gè)進(jìn)程袭异,然后運(yùn)行Service钠龙。這里我們只考慮前者。
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
if (app.thread == null) {
throw new RemoteException();
}
...
// 根據(jù)服務(wù)類型設(shè)置超時(shí)Message
bumpServiceExecutingLocked(r, execInFg, "create");
// 更新LRU鏈表
mAm.updateLruProcessLocked(app, false, null);
// 更新相關(guān)進(jìn)程的OomAdj
mAm.updateOomAdjLocked();
...
try {
if (LOG_SERVICE_START_STOP) {
String nameTerm;
int lastPeriod = r.shortName.lastIndexOf('.');
nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
EventLogTags.writeAmCreateService(
r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
}
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
// 通過binder IPC運(yùn)行服務(wù)
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
r.postNotification();
created = true;
}
...
}
realStartServiceLocked的參數(shù)execInFg表示服務(wù)是前臺(tái)服務(wù)還是后臺(tái)服務(wù)御铃,bumpServiceExecutingLocked會(huì)根據(jù)服務(wù)的類型設(shè)定不同的超時(shí)時(shí)間碴里,其中前臺(tái)服務(wù)的超時(shí)時(shí)間為SERVICE_TIMEOUT(20s),后臺(tái)服務(wù)的超時(shí)時(shí)間為10 * SERVICE_TIMEOUT上真。如果超時(shí)時(shí)間結(jié)束時(shí)咬腋,超時(shí)消息仍然沒有被移除,就表明有Service執(zhí)行超時(shí)睡互,下面分析ServiceTimeout根竿。
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
final long now = SystemClock.uptimeMillis();
final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
for (int i=proc.executingServices.size()-1; i>=0; i--) {
// 遍歷正在執(zhí)行的服務(wù),找到執(zhí)行超時(shí)的服務(wù)
ServiceRecord sr = proc.executingServices.valueAt(i);
if (sr.executingStart < maxTime) {
timeout = sr;
break;
}
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
// 找到執(zhí)行超時(shí)的服務(wù)
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(timeout);
timeout.dump(pw, " ");
pw.close();
mLastAnrDump = sw.toString();
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
anrMessage = "executing service " + timeout.shortName;
} else {
// 沒有單個(gè)服務(wù)執(zhí)行超時(shí)就珠,繼續(xù)設(shè)定超時(shí)Message
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
}
}
if (anrMessage != null) {
// 處理Service ANR
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
serviceTimeout負(fù)責(zé)找到執(zhí)行超時(shí)的服務(wù)寇壳,然后調(diào)用AppErrors的appNotResponding處理。
上面的整個(gè)過程就是appNotResponding的處理流程妻怎,對(duì)于細(xì)節(jié)我們?cè)诜治鯝NR一節(jié)中再討論壳炎,需要注意的
是紅色區(qū)域的流程在Android N上已經(jīng)改變(改善系統(tǒng)性能),詳情參考下面鏈接逼侦。
- Broadcast超時(shí)
在分析Broadcast ANR之前我們先簡(jiǎn)單了解下Broadcast匿辩。
Broadcast一般分為兩類:
- Normal broadcasts (sent with Context.sendBroadcast) are completely asynchronous. All receivers of the broadcast are run in an undefined order,
often at the same time. This is more efficient, but means that receivers cannot use the result or abort APIs included here.- Ordered broadcasts (sent with Context.sendOrderedBroadcast) are delivered to one receiver at a time. As each receiver executes in turn, it can propagate a result to the next receiver, or it can completely abort the broadcast so that it won't be passed to other receivers. The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order.
Even in the case of normal broadcasts, the system may in some situations revert to delivering the broadcast one receiver at a time. In particular, for receivers that may require the creation of a process, only one will be run at a time to avoid overloading the system with new processes. In this situation, however, the non-ordered semantics hold: these receivers still cannot return results or abort their broadcast.
BroadcastReceiver有兩種注冊(cè)方式:
You can either dynamically register an instance of this class with Context.registerReceiver() or statically publish an implementation through the receiver tag in your AndroidManifest.xml.
下面分析Broadcast ANR的流程,如圖所示:
如果AMS將Broadcast發(fā)送給廣播接收機(jī)后榛丢,在規(guī)定的時(shí)間內(nèi)沒有收到廣播接收機(jī)
發(fā)送的finishReceiver的消息铲球,就會(huì)觸發(fā)BroadcastTimeout ANR。下面從broadcastIntentLocked開始分析涕滋。
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
...
// Figure out who all will receive this broadcast.
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
// 收集靜態(tài)注冊(cè)的廣播接收機(jī)
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
for (int i = 0; i < users.length; i++) {
if (mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(intent,
resolvedType, false, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
registeredReceivers.addAll(registeredReceiversForUser);
}
}
} else {
// 查找動(dòng)態(tài)注冊(cè)的廣播接收機(jī)
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
}
}
...
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// 發(fā)送普通廣播到動(dòng)態(tài)注冊(cè)的廣播接收機(jī)
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
// 根據(jù)廣播類型決定發(fā)送廣播的隊(duì)列,前臺(tái)廣播由前臺(tái)廣播對(duì)表處理挠阁;
// 后臺(tái)廣播由后臺(tái)廣播隊(duì)列處理
final BroadcastQueue queue = broadcastQueueForIntent(intent);
// 創(chuàng)建廣播記錄
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
resultExtras, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
// 將(前臺(tái)\后臺(tái))普通廣播放入(前臺(tái)\后臺(tái))并行廣播列表中
queue.enqueueParallelBroadcastLocked(r);
// 處理(前臺(tái)\后臺(tái))并行廣播列表中的廣播
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
// Merge into one list.
// 動(dòng)態(tài)注冊(cè)的廣播接收機(jī)宾肺、靜態(tài)注冊(cè)的廣播接收機(jī)按優(yōu)先級(jí)排序(高->低)溯饵,
// 存放到receivers中
int ir = 0;
if (receivers != null) {
...
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
while (it < NT && ir < NR) {
if (curt == null) {
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
curr = registeredReceivers.get(ir);
}
if (curr.getPriority() >= curt.priority) {
// Insert this broadcast record into the final list.
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
} else {
// Skip to the next ResolveInfo in the final list.
it++;
curt = null;
}
}
}
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
// 根據(jù)廣播類型決定發(fā)送廣播的隊(duì)列,前臺(tái)廣播由前臺(tái)廣播對(duì)表處理锨用;
// 后臺(tái)廣播由后臺(tái)廣播隊(duì)列處理
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
"Enqueueing broadcast " + r.intent.getAction());
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
// 將(前臺(tái)\后臺(tái))普通廣播放入(前臺(tái)\后臺(tái))有序廣播列表中
queue.enqueueOrderedBroadcastLocked(r);
// 處理(前臺(tái)\后臺(tái))有序廣播列表中的廣播
queue.scheduleBroadcastsLocked();
}
}
...
}
broadcastIntentLocked在進(jìn)行一系列的檢查以及特殊情況的處理后丰刊,按廣播的類型以及相應(yīng)的廣播接收機(jī)的類型進(jìn)行分發(fā)。
下面分析分發(fā)函數(shù)scheduleBroadcastsLocked
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
// 發(fā)送處理廣播的Message
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
scheduleBroadcastsLocked只是簡(jiǎn)單的發(fā)送BROADCAST_INTENT_MSG消息增拥,該消息的處理函數(shù)調(diào)用processNextBroadcast進(jìn)行分發(fā)啄巧。
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
BroadcastRecord r;
...
mService.updateCpuStats();
...
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
final int N = r.receivers.size();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ mQueueName + "] " + r);
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering non-ordered on [" + mQueueName + "] to registered "
+ target + ": " + r);
//將廣播同時(shí)發(fā)送給Parallel列表中的廣播接收機(jī)
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ mQueueName + "] " + r);
}
...
do {
if (mOrderedBroadcasts.size() == 0) {
// No more broadcasts pending, so all done!
mService.scheduleAppGcsLocked();
if (looped) {
// If we had finished the last ordered broadcast, then
// make sure all processes have correct oom and sched
// adjustments.
mService.updateOomAdjLocked();
}
return;
}
// 將廣播按照優(yōu)先級(jí)一個(gè)一個(gè)的分發(fā)給Ordered列表中的廣播接收機(jī)
r = mOrderedBroadcasts.get(0);
boolean forceReceive = false;
...
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
// 1) 廣播發(fā)送始于SystemReady之前,結(jié)束于SystemReady之后的超時(shí)檢測(cè)
// 由于SystemReady之前的廣播發(fā)送可能很慢掌栅,而且不檢測(cè)秩仆,所以超時(shí)時(shí)間為
// 2 * mTimeoutPeriod * numReceivers
// 2) 廣播發(fā)送過程中有dex2oat發(fā)生。
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
...
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
...
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// No more receivers for this broadcast! Send the final
// result if requested...
if (r.resultTo != null) {
// 廣播發(fā)送完成猾封,如果發(fā)送方需要結(jié)果澄耍,將結(jié)果反饋給發(fā)送方。
try {
if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
"Finishing broadcast [" + mQueueName + "] "
+ r.intent.getAction() + " app=" + r.callerApp);
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
r.resultTo = null;
} catch (RemoteException e) {
r.resultTo = null;
Slog.w(TAG, "Failure ["
+ mQueueName + "] sending broadcast result of "
+ r.intent, e);
}
}
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
// 一個(gè)廣播的所有接收機(jī)發(fā)送完成晌缘,取消超時(shí)消息設(shè)置齐莲。
cancelBroadcastTimeoutLocked();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"Finished with ordered broadcast " + r);
// ... and on to the next...
addBroadcastToHistoryLocked(r);
if (r.intent.getComponent() == null && r.intent.getPackage() == null
&& (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
// This was an implicit broadcast... let's record it for posterity.
mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
}
// 從Ordered隊(duì)列中移除發(fā)送完成的廣播
mOrderedBroadcasts.remove(0);
r = null;
looped = true;
continue;
}
} while (r == null);
// Get the next receiver...
// 獲取廣播的下一個(gè)接收者(可能有多個(gè))發(fā)送
int recIdx = r.nextReceiver++;
// Keep track of when this receiver started, and make sure there
// is a timeout message pending to kill it if need be.
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {
// 廣播多個(gè)接收者中的第一個(gè),記錄分發(fā)時(shí)間
r.dispatchTime = r.receiverTime;
r.dispatchClockTime = System.currentTimeMillis();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
+ mQueueName + "] " + r);
}
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Submitting BROADCAST_TIMEOUT_MSG ["
+ mQueueName + "] for " + r + " at " + timeoutTime);
// 如果沒有設(shè)定廣播發(fā)送超時(shí)時(shí)間磷箕,在這里設(shè)定
setBroadcastTimeoutLocked(timeoutTime);
}
...
final Object nextReceiver = r.receivers.get(recIdx);
if (nextReceiver instanceof BroadcastFilter) {
// Simple case: this is a registered receiver who gets
// a direct call.
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering ordered ["
+ mQueueName + "] to registered "
+ filter + ": " + r);
// 如果是動(dòng)態(tài)注冊(cè)的廣播接收機(jī)选酗,直接發(fā)送
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
// 我的理解r.ordered == true ???
if (r.receiver == null || !r.ordered) {
// The receiver has already finished, so schedule to
// process the next one.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
+ mQueueName + "]: ordered="
+ r.ordered + " receiver=" + r.receiver);
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
} else {
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(filter.owningUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
}
}
return;
}
...
// Is this receiver's application already running?
if (app != null && app.thread != null) {
// 廣播接收機(jī)Host進(jìn)程已經(jīng)運(yùn)行,發(fā)送廣播
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
// 最終通過Binder IPC運(yùn)行廣播接收機(jī)
processCurBroadcastLocked(r, app);
return;
}
}
...
// 創(chuàng)建廣播接收機(jī)Host進(jìn)程
if ((r.curApp=mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"broadcast", r.curComponent,
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) {
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
Slog.w(TAG, "Unable to launch app "
+ info.activityInfo.applicationInfo.packageName + "/"
+ info.activityInfo.applicationInfo.uid + " for broadcast "
+ r.intent + ": process is bad");
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
mPendingBroadcast = r;
}
}
簡(jiǎn)單總結(jié)一下廣播的發(fā)送超時(shí)流程:
與服務(wù)超時(shí)ANR類似岳枷,如果在規(guī)定的時(shí)間內(nèi)芒填,廣播超時(shí)消息沒有取消,就會(huì)觸發(fā)ANR嫩舟。接下來分析
廣播超時(shí)ANR的處理氢烘。
final void broadcastTimeoutLocked(boolean fromMsg) {
// fromMsg標(biāo)記超時(shí)觸發(fā)者,true表示超時(shí)消息觸發(fā)
// false表示直接調(diào)用超時(shí)處理
if (fromMsg) {
mPendingBroadcastTimeoutMessage = false;
}
if (mOrderedBroadcasts.size() == 0) {
return;
}
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mOrderedBroadcasts.get(0);
if (fromMsg) {
if (mService.mDidDexOpt) {
// Delay timeouts until dexopt finishes.
mService.mDidDexOpt = false;
long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
return;
}
if (!mService.mProcessesReady) {
// Only process broadcast timeouts if the system is ready. That way
// PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
// to do heavy lifting for system up.
return;
}
long timeoutTime = r.receiverTime + mTimeoutPeriod;
// 如果發(fā)送給當(dāng)前廣播接收機(jī)(可能多個(gè))沒有超時(shí)家厌,則重新設(shè)定超時(shí)消息播玖;從這里
// 看出超時(shí)其實(shí)是針對(duì)單個(gè)廣播接收機(jī),如果多個(gè)廣播接收機(jī)收發(fā)累計(jì)時(shí)間
// 超時(shí)饭于,并不會(huì)觸發(fā)ANR蜀踏。
if (timeoutTime > now) {
// We can observe premature timeouts because we do not cancel and reset the
// broadcast timeout message after each receiver finishes. Instead, we set up
// an initial timeout then kick it down the road a little further as needed
// when it expires.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Premature timeout ["
+ mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+ timeoutTime);
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
...
// 觸發(fā)廣播超時(shí)ANR
if (anrMessage != null) {
// Post the ANR to the handler since we do not want to process ANRs while
// potentially holding our lock.
mHandler.post(new AppNotResponding(app, anrMessage));
}
}
broadcastTimeoutLocked根據(jù)參數(shù)fromMsg進(jìn)一步判定是否確實(shí)廣播超時(shí)ANR,這里要注意如果有dex2oat掰吕,廣播超時(shí)時(shí)間被推遲果覆;如果系統(tǒng)啟動(dòng)還未就緒,不檢測(cè)廣播超時(shí)殖熟。廣播超時(shí)ANR的處理流程跟服務(wù)超時(shí)ANR類似局待,不再贅述。
- ContentProvider超時(shí)
Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface.
A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider.
If you don't need to share data amongst multiple applications you can use a database directly via SQLiteDatabase.
ContentProvider超時(shí)的示意圖如上圖所示,如果我們只是使用getContentResolver返回的對(duì)象(ApplicationContentResolver)來訪問ContentProvider提供的數(shù)據(jù)钳榨,并不會(huì)有超時(shí)ANR的檢測(cè)舰罚。當(dāng)使用ContentResolverClient來訪問數(shù)據(jù)才會(huì)有超時(shí)ANR檢測(cè)。ContentResolverClient檢測(cè)到數(shù)據(jù)操作超時(shí)后薛耻,最終通過Binder IPC通知AMS营罢,AMS的處理與之前討論的類似,不再贅述饼齿。
-
Input Dispatching超時(shí)
我們以Touch事件來說明Input Dispatching超時(shí)機(jī)制饲漾。這里只分析關(guān)鍵的函數(shù),首先看
checkWindowReadyForMoreInputLocked缕溉。
String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
const char* targetType) {
...
// Ensure that the dispatch queues aren't too far backed up for this event.
if (eventEntry->type == EventEntry::TYPE_KEY) {
// 對(duì)于按鍵事件處理考传,如果outboundQueue以及waitQueue均不為空的話
// 不能發(fā)送。
// If the event is a key event, then we must wait for all previous events to
// complete before delivering it because previous events may have the
// side-effect of transferring focus to a different window and we want to
// ensure that the following keys are sent to the new window.
//
// Suppose the user touches a button in a window then immediately presses "A".
// If the button causes a pop-up window to appear then we want to ensure that
// the "A" key is delivered to the new pop-up window. This is because users
// often anticipate pending UI changes when typing on a keyboard.
// To obtain this behavior, we must serialize key events with respect to all
// prior input events.
if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) {
return String8::format("Waiting to send key event because the %s window has not "
"finished processing all of the input events that were previously "
"delivered to it. Outbound queue length: %d. Wait queue length: %d.",
targetType, connection->outboundQueue.count(), connection->waitQueue.count());
}
} else {
// Touch事件的處理倒淫,如果waitQueue不為空伙菊,且waitQueue的head事件分發(fā)完成距離當(dāng)前已經(jīng)
// 超過STREAM_AHEAD_EVENT_TIMEOUT,不能發(fā)送敌土,
// Touch events can always be sent to a window immediately because the user intended
// to touch whatever was visible at the time. Even if focus changes or a new
// window appears moments later, the touch event was meant to be delivered to
// whatever window happened to be on screen at the time.
//
// Generic motion events, such as trackball or joystick events are a little trickier.
// Like key events, generic motion events are delivered to the focused window.
// Unlike key events, generic motion events don't tend to transfer focus to other
// windows and it is not important for them to be serialized. So we prefer to deliver
// generic motion events as soon as possible to improve efficiency and reduce lag
// through batching.
//
// The one case where we pause input event delivery is when the wait queue is piling
// up with lots of events because the application is not responding.
// This condition ensures that ANRs are detected reliably.
if (!connection->waitQueue.isEmpty()
&& currentTime >= connection->waitQueue.head->deliveryTime
+ STREAM_AHEAD_EVENT_TIMEOUT) {
return String8::format("Waiting to send non-key event because the %s window has not "
"finished processing certain input events that were delivered to it over "
"%0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.",
targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
connection->waitQueue.count(),
(currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f);
}
}
...
}
checkWindowReadyForMoreInputLocked用于檢查目標(biāo)窗口是否已經(jīng)準(zhǔn)備好接收更多的輸入事件镜硕,如果沒有準(zhǔn)備好,需要進(jìn)一步檢查窗口沒有準(zhǔn)備好的原因返干,下面分析handleTargetsNotReadyLocked兴枯。
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
const EventEntry* entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t* nextWakeupTime, const char* reason) {
...
if (applicationHandle == NULL && windowHandle == NULL) {
...
} else {
// 記錄首次目標(biāo)窗口沒有準(zhǔn)備好的時(shí)間,并計(jì)算出超時(shí)時(shí)間mInputTargetWaitTimeoutTime
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
#if DEBUG_FOCUS
ALOGD("Waiting for application to become ready for input: %s. Reason: %s",
getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),
reason);
#endif
nsecs_t timeout;
if (windowHandle != NULL) {
timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else if (applicationHandle != NULL) {
timeout = applicationHandle->getDispatchingTimeout(
DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else {
timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
}
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = currentTime + timeout;
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationHandle.clear();
...
}
}
...
if (currentTime >= mInputTargetWaitTimeoutTime) {
// Input Dispatching ANR處理
onANRLocked(currentTime, applicationHandle, windowHandle,
entry->eventTime, mInputTargetWaitStartTime, reason);
// Force poll loop to wake up immediately on next iteration once we get the
// ANR response back from the policy.
*nextWakeupTime = LONG_LONG_MIN;
return INPUT_EVENT_INJECTION_PENDING;
}
...
}
AMS對(duì)于Input Dispatching超時(shí)ANR的處理與之前類似矩欠,不再贅述财剖。
如何分析ANR
- 問題描述
ANR in com.samsung.android.email.provider during CAS Test
-
Log分析
- ANR發(fā)生的準(zhǔn)確時(shí)間及原因
01-03 03:47:07.488 1149 1313 I am_anr : [0,4910,com.samsung.android.email.provider,953695813,Input dispatching timed out (Waiting to send non-key event because the focused window has not finished processing certain input events that were delivered to it over 500.0ms ago. Wait queue length: 4. Wait queue head age: 5615.7ms.)]
- Email進(jìn)程的backtrace(/data/anr/traces.txt or dropbox)
通常重點(diǎn)關(guān)注mian線程以及binder線程
"main" prio=5 tid=1 Native | group="main" sCount=1 dsCount=0 obj=0x768f3fb8 self=0x557be4ec40 | sysTid=4910 nice=0 cgrp=default sched=0/0 handle=0x7f8bb4ffd0 | state=S schedstat=( 15510426384 7139276978 32108 ) utm=1214 stm=337 core=4 HZ=100 | stack=0x7fdda0b000-0x7fdda0d000 stackSize=8MB | held mutexes= kernel: __switch_to+0x7c/0x88 kernel: SyS_epoll_wait+0x2cc/0x394 kernel: SyS_epoll_pwait+0x9c/0x114 kernel: __sys_trace+0x48/0x4c native: #00 pc 0000000000069d94 /system/lib64/libc.so (__epoll_pwait+8) native: #01 pc 000000000001ce64 /system/lib64/libc.so (epoll_pwait+32) native: #02 pc 000000000001be88 /system/lib64/libutils.so (_ZN7android6Looper9pollInnerEi+144) native: #03 pc 000000000001c268 /system/lib64/libutils.so (_ZN7android6Looper8pollOnceEiPiS1_PPv+80) native: #04 pc 00000000000d89dc /system/lib64/libandroid_runtime.so (_ZN7android18NativeMessageQueue8pollOnceEP7_JNIEnvP8_jobjecti+48) native: #05 pc 000000000000087c /system/framework/arm64/boot.oat (Java_android_os_MessageQueue_nativePollOnce__JI+144) at android.os.MessageQueue.nativePollOnce(Native method) at android.os.MessageQueue.next(MessageQueue.java:323) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:7421) at java.lang.reflect.Method.invoke!(Native method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) "Binder_1" prio=5 tid=8 Native | group="main" sCount=1 dsCount=0 obj=0x32c072e0 self=0x557c129ec0 | sysTid=4921 nice=0 cgrp=default sched=0/0 handle=0x7f86c55450 | state=S schedstat=( 518922106 635149861 2589 ) utm=25 stm=26 core=4 HZ=100 | stack=0x7f86b59000-0x7f86b5b000 stackSize=1013KB | held mutexes= kernel: __switch_to+0x7c/0x88 kernel: binder_thread_read+0xf60/0x10d8 kernel: binder_ioctl_write_read+0x1cc/0x304 kernel: binder_ioctl+0x348/0x738 kernel: do_vfs_ioctl+0x4e0/0x5c0 kernel: SyS_ioctl+0x5c/0x88 kernel: __sys_trace+0x48/0x4c native: #00 pc 0000000000069e80 /system/lib64/libc.so (__ioctl+4) native: #01 pc 0000000000073ea4 /system/lib64/libc.so (ioctl+100) native: #02 pc 000000000002d584 /system/lib64/libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+164) native: #03 pc 000000000002de00 /system/lib64/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+24) native: #04 pc 000000000002df1c /system/lib64/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+76) native: #05 pc 0000000000036a10 /system/lib64/libbinder.so (???) native: #06 pc 00000000000167b4 /system/lib64/libutils.so (_ZN7android6Thread11_threadLoopEPv+208) native: #07 pc 00000000000948d0 /system/lib64/libandroid_runtime.so (_ZN7android14AndroidRuntime15javaThreadShellEPv+96) native: #08 pc 0000000000016004 /system/lib64/libutils.so (???) native: #09 pc 0000000000067904 /system/lib64/libc.so (_ZL15__pthread_startPv+52) native: #10 pc 000000000001c804 /system/lib64/libc.so (__start_thread+16) ...
- ANR發(fā)生時(shí)系統(tǒng)CPU使用信息
01-03 03:47:28.928 1149 1313 E android.os.Debug: ro.product_ship = false 01-03 03:47:28.928 1149 1313 E android.os.Debug: ro.debug_level = 0x494d 01-03 03:47:28.928 1149 1313 E android.os.Debug: Failed open /proc/schedinfo 01-03 03:47:28.928 1149 1313 E ActivityManager: ANR in com.samsung.android.email.provider (com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL) 01-03 03:47:28.928 1149 1313 E ActivityManager: PID: 4910 01-03 03:47:28.928 1149 1313 E ActivityManager: Reason: Input dispatching timed out (Waiting to send non-key event because the focused window has not finished processing certain input events that were delivered to it over 500.0ms ago. Wait queue length: 4. Wait queue head age: 5615.7ms.) 01-03 03:47:28.928 1149 1313 E ActivityManager: Load: 0.0 / 0.0 / 0.0 01-03 03:47:28.928 1149 1313 E ActivityManager: ------ Current CPU Core Info ------ 01-03 03:47:28.928 1149 1313 E ActivityManager: - offline : 01-03 03:47:28.928 1149 1313 E ActivityManager: - online : 0-7 01-03 03:47:28.928 1149 1313 E ActivityManager: - cpu_normalized_load : - 01-03 03:47:28.928 1149 1313 E ActivityManager: - run_queue_avg : 2.9 01-03 03:47:28.928 1149 1313 E ActivityManager: - AP Temp = 475 01-03 03:47:28.928 1149 1313 E ActivityManager: 0 1 2 3 4 5 6 7 01-03 03:47:28.928 1149 1313 E ActivityManager: ------------------------------------------------------------------------------------------------------------------ 01-03 03:47:28.928 1149 1313 E ActivityManager: scaling_cur_freq 1689600 1689600 1689600 1689600 1689600 1689600 1689600 1689600 01-03 03:47:28.928 1149 1313 E ActivityManager: scaling_governor interactive interactive interactive interactive interactive interactive interactive interactive 01-03 03:47:28.928 1149 1313 E ActivityManager: scaling_max_freq 1689600 1689600 1689600 1689600 1689600 1689600 1689600 1689600 01-03 03:47:28.928 1149 1313 E ActivityManager: ------------------------------------------------------------------------------------------------------------------ 01-03 03:47:28.928 1149 1313 E ActivityManager: CPU usage from 1277ms to -51ms ago: 01-03 03:47:28.928 1149 1313 E ActivityManager: 100% 7807/procrank: 10% user + 89% kernel / faults: 6598 minor 01-03 03:47:28.928 1149 1313 E ActivityManager: 36% 466/surfaceflinger: 9% user + 27% kernel / faults: 596 minor 01-03 03:47:28.928 1149 1313 E ActivityManager: 25% 1149/system_server: 4.4% user + 20% kernel / faults: 139 minor 01-03 03:47:28.928 1149 1313 E ActivityManager: 21% 259/spi3: 0% user + 21% kernel 01-03 03:47:28.928 1149 1313 E ActivityManager: 10% 4910/com.samsung.android.email.provider: 7.2% user + 3.6% kernel / faults: 39 minor 01-03 03:47:28.928 1149 1313 E ActivityManager: 8.6% 2837/com.samsung.android.providers.context: 3.6% user + 5% kernel / faults: 28 minor 01-03 03:47:28.928 1149 1313 E ActivityManager: 3.4% 7806/dumpstate: 0.2% user + 3.1% kernel / faults: 26 minor 01-03 03:47:28.928 1149 1313 E ActivityManager: 8.3% 506/mediaserver: 2.2% user + 6% kernel 01-03 03:47:28.928 1149 1313 E ActivityManager: 6.8% 320/mmcqd/0: 0% user + 6.8% kernel 01-03 03:47:28.928 1149 1313 E ActivityManager: 1.5% 6263/com.sec.spp.push:RemoteDlcProcess: 1.2% user + 0.3% kernel / faults: 547 minor 01-03 03:47:28.928 1149 1313 E ActivityManager: 5% 5677/com.samsung.cas: 1.4% user + 3.6% kernel / faults: 463 minor 01-03 03:47:28.928 1149 1313 E ActivityManager: 5% 7751/screenrecord: 2.1% user + 2.8% kernel 01-03 03:47:28.928 1149 1313 E ActivityManager: 4.5% 29/ksoftirqd/4: 0% user + 4.5% kernel 01-03 03:47:28.928 1149 1313 E ActivityManager: 4.4% 913/kworker/u16:9: 0% user + 4.4% kernel 01-03 03:47:28.928 1149 1313 E ActivityManager: 3.7% 34/ksoftirqd/5: 0% user + 3.7% kernel 01-03 03:47:28.928 1149 1313 E ActivityManager: 3.7% 73/kworker/u16:3: 0% user + 3.7% kernel 01-03 03:47:28.928 1149 1313 E ActivityManager: 3.7% 262/irq/69-madera: 0% user + 3.7% kernel ... 01-03 03:47:28.928 1149 1313 E ActivityManager: 62% TOTAL: 9.5% user + 50% kernel + 1.3% iowait + 0.5% softirq 01-03 03:47:28.928 1149 1313 E ActivityManager: CPU usage from 189522ms to 189522ms ago with 0% awake: 01-03 03:47:28.928 1149 1313 E ActivityManager: 0% TOTAL: 0% user + 0% kernel
- 超時(shí)ANR期間內(nèi)log(以及Service/Broadcast history等)
... Line 33430: 01-03 03:47:01.858 1149 1550 I InputDispatcher: Delivering touch to (7647): x: 520.000, y: 1562.000, flags=0x0, action: 0x0, channel '8d82752 ScrollCaptureUiService (server)', toolType: 0 ... Line 33432: 01-03 03:47:01.858 1149 1550 I InputDispatcher: Delivering touch to (7647): x: 520.456, y: 1560.577, flags=0x0, action: 0x1, channel '8d82752 ScrollCaptureUiService (server)', toolType: 0 ... Line 33447: 01-03 03:47:02.158 1149 1550 I InputDispatcher: Delivering touch to (7647): x: 369.000, y: 1089.000, flags=0x0, action: 0x0, channel '8d82752 ScrollCaptureUiService (server)', toolType: 0 ... Line 33449: 01-03 03:47:02.158 1149 1550 I InputDispatcher: Delivering touch to (7647): x: 375.404, y: 1090.664, flags=0x0, action: 0x1, channel '8d82752 ScrollCaptureUiService (server)', toolType: 0 Line 33481: 01-03 03:47:02.468 1149 1550 D InputDispatcher: Waiting for application to become ready for input: AppWindowToken{d0aeba2b3 token=Token{7242822 ActivityRecord{b24b1ed u0 com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL t1044}}} - Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}. Reason: Waiting to send non-key event because the focused window has not finished processing certain input events that were delivered to it over 500.0ms ago. Wait queue length: 4. Wait queue head age: 610.8ms. Line 33825: 01-03 03:47:07.468 1149 1550 I InputDispatcher: Application is not responding: AppWindowToken{d0aeba2b3 token=Token{7242822 ActivityRecord{b24b1ed u0 com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL t1044}}} - Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}. It has been 5007.8ms since event, 5004.9ms since wait started. Reason: Waiting to send non-key event because the focused window has not finished processing certain input events that were delivered to it over 500.0ms ago. Wait queue length: 4. Wait queue head age: 5615.7ms.
首先分析Email在發(fā)生ANR之前5~6內(nèi)的事件分發(fā)log,我們從log中發(fā)現(xiàn)在這段事件內(nèi),InputDispatcher并沒有分發(fā)輸入事件給Email的窗口癌淮,而是一直有輸入時(shí)間分發(fā)給ScrollCaptureUiService躺坟。繼續(xù)看發(fā)現(xiàn)實(shí)際上是ScrollCaptureUiService窗口不響應(yīng)輸入事件(根據(jù)waitQueue中事件數(shù)量以及事件超時(shí)時(shí)間可判定),為什么ScrollCaptureUiService不響應(yīng)輸入事件乳蓄?繼續(xù)看log發(fā)現(xiàn)在收到輸入事件的時(shí)候咪橙,com.samsung.android.app.scrollcapture發(fā)生Crash,這就可以解釋ScrollCaptureUiService窗口不響應(yīng)輸入事件啦(PS:細(xì)節(jié)后續(xù)討論debuggerd64時(shí)候再展開)虚倒。
... 01-03 03:47:01.618 7647 7647 F libc : heap corruption detected by dlmalloc 01-03 03:47:01.618 7647 7647 F libc : Fatal signal 6 (SIGABRT), code -6 in tid 7647 (p.scrollcapture) 01-03 03:47:01.628 7647 7777 D SC_ScrollCapture_JNI: getRgbSumTable : W=1080 H=1920 SumW=1027 SumH=1920 Elapsed=10ms 01-03 03:47:01.698 501 501 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 01-03 03:47:01.698 501 501 F DEBUG : Build fingerprint: 'Android/c5proltezc/c5proltechn:6.0.1/MMB29M/C5010ZCE0APJ1c:eng/test-keys' 01-03 03:47:01.698 501 501 F DEBUG : Revision: '0' 01-03 03:47:01.698 501 501 F DEBUG : ABI: 'arm64' 01-03 03:47:01.698 501 501 F DEBUG : pid: 7647, tid: 7647, name: p.scrollcapture >>> com.samsung.android.app.scrollcapture ...
既然是ScrollCaptureUiService窗口不響應(yīng)輸入事件為什么Email發(fā)生ANR?
InputDispatcher: Waiting for application to become ready for input: AppWindowToken{d0aeba2b3 token=Token{7242822 ActivityRecord{b24b1ed u0 com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL t1044}}} - Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}
從log中我們看出InputApplicationHandle是
AppWindowToken{d0aeba2b3 token=Token{7242822 ActivityRecord{b24b1ed u0 com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL t1044}}
美侦,但是InputWindowHandle是Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}
,我們可以理解為窗口覆蓋在應(yīng)用之上魂奥。對(duì)于窗口與應(yīng)用handle不一致的情況菠剩,發(fā)生ANR時(shí),首先找到窗口handle也就是Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}
對(duì)應(yīng)的應(yīng)用耻煤,如果為空的話具壮,系統(tǒng)認(rèn)為應(yīng)用handle對(duì)應(yīng)的應(yīng)用也就是com.samsung.android.email.provider
ANR准颓,這就是Email發(fā)生ANR的原因。
如何避免ANR
這里大家可以參考Android Developer網(wǎng)站給出建議棺妓,理解了ANR發(fā)生的原理瞬场,自然也就懂得避免ANR。