ANR原理分析
什么是ANR
ANR(Application Not Responding)就是應(yīng)用在規(guī)定的時(shí)間內(nèi)沒有響應(yīng)用戶輸入或者系統(tǒng)服務(wù)熙暴。
ANR發(fā)生場景
這里以廣播超時(shí)和輸入事件為例講述ANR發(fā)生場景,其余組件如Activity慌盯,Service,ContentProvider實(shí)現(xiàn)原理非常類似.
1. BroadcastReceiver 超時(shí)
在分析Broadcast ANR之前我們先簡單了解下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.
BroadcastReceiver有兩種注冊方式:
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)注冊的廣播接收機(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)注冊的廣播接收機(jī)
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
}
}
...
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// 發(fā)送普通廣播到動(dòng)態(tài)注冊的廣播接收機(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ì)列乃摹,前臺廣播由前臺廣播對表處理禁漓;
// 后臺廣播由后臺廣播隊(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) {
// 將(前臺\后臺)普通廣播放入(前臺\后臺)并行廣播列表中
queue.enqueueParallelBroadcastLocked(r);
// 處理(前臺\后臺)并行廣播列表中的廣播
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
// Merge into one list.
// 動(dòng)態(tài)注冊的廣播接收機(jī)、靜態(tài)注冊的廣播接收機(jī)按優(yōu)先級排序(高->低)孵睬,
// 存放到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ì)列播歼,前臺廣播由前臺廣播對表處理;
// 后臺廣播由后臺廣播隊(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) {
// 將(前臺\后臺)普通廣播放入(前臺\后臺)有序廣播列表中
queue.enqueueOrderedBroadcastLocked(r);
// 處理(前臺\后臺)有序廣播列表中的廣播
queue.scheduleBroadcastsLocked();
}
}
...
}
broadcastIntentLocked在進(jìn)行一系列的檢查以及特殊情況的處理后掰读,按廣播的類型以及相應(yīng)的廣播接收機(jī)的類型進(jìn)行分發(fā)秘狞。
下面分析分發(fā)函數(shù)scheduleBroadcastsLocked
android/frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
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只是簡單的發(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)先級一個(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í)檢測
// 由于SystemReady之前的廣播發(fā)送可能很慢雇初,而且不檢測,所以超時(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)注冊的廣播接收機(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;
}
}
超時(shí)消息處理绞愚,超時(shí)時(shí)間到直接調(diào)用broadcastTimeoutLocked處理
161 private final class BroadcastHandler extends Handler {
162 public BroadcastHandler(Looper looper) {
163 super(looper, null, true);
164 }
165
166 @Override
167 public void handleMessage(Message msg) {
168 switch (msg.what) {
169 case BROADCAST_INTENT_MSG: {
170 if (DEBUG_BROADCAST) Slog.v(
171 TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
172 processNextBroadcast(true);
173 } break;
174 case BROADCAST_TIMEOUT_MSG: {
175 synchronized (mService) {
176 broadcastTimeoutLocked(true);
177 }
178 } break;
179 }
180 }
181 }
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í)是針對單個(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,這里需要注意并不是沒發(fā)送一條廣播就發(fā)送BROADCAST_TIMEOUT_MSG消息佛致,而在每個(gè)receiver接收處理后才cancelBroadcastTimeoutLocked, 而是在每次timeout之后判斷當(dāng)前廣播處理時(shí)間是否超時(shí)贮缕,沒有超時(shí)會(huì)重新發(fā)送BROADCAST_TIMEOUT_MSG,用于延長timeout時(shí)間俺榆,這樣可以避免比如100條有序廣播就要發(fā)送100個(gè)BROADCAST_TIMEOUT_MSG消息以及100次cancelBroadcastTimeoutLocked
2. 輸入事件超時(shí)
/frameworks/native/services/inputflinger/InputDispatcher.cpp
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
int32_t injectionResult;
std::string reason;
... ...
// Check whether the window is ready for more input.
reason = checkWindowReadyForMoreInputLocked(currentTime,
mFocusedWindowHandle, entry, "focused");
if (!reason.empty()) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.c_str());
goto Unresponsive;
}
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
addWindowTargetLocked(mFocusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
inputTargets);
// Done.
Failed:
Unresponsive:
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
updateDispatchStatisticsLocked(currentTime, entry,
injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
ALOGD("findFocusedWindow finished: injectionResult=%d, "
"timeSpentWaitingForApplication=%0.1fms",
injectionResult, timeSpentWaitingForApplication / 1000000.0);
#endif
return injectionResult;
}
checkWindowReadyForMoreInputLocked方法判斷窗口是否準(zhǔn)備號接受事件
std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
const char* targetType) {
// If the window is paused then keep waiting.
if (windowHandle->getInfo()->paused) {
return StringPrintf("Waiting because the %s window is paused.", targetType);
}
//獲取InputChannel在mConnectionsByFd中的索引,從而得到對應(yīng)的Connection對象
// If the window's connection is not registered then keep waiting.
ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
if (connectionIndex < 0) {
return StringPrintf("Waiting because the %s window's input channel is not "
"registered with the input dispatcher. The window may be in the process "
"of being removed.", targetType);
}
// If the connection is dead then keep waiting.
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
if (connection->status != Connection::STATUS_NORMAL) {
return StringPrintf("Waiting because the %s window's input connection is %s."
"The window may be in the process of being removed.", targetType,
connection->getStatusLabel());
}
//inputPublisher被block,比如窗口反饋慢,導(dǎo)致InputChannel被寫滿,inputPublisher就會(huì)被block
// If the connection is backed up then keep waiting.
if (connection->inputPublisherBlocked) {
return StringPrintf("Waiting because the %s window's input channel is full. "
"Outbound queue length: %d. Wait queue length: %d.",
targetType, connection->outboundQueue.count(), connection->waitQueue.count());
}
//Key事件要求outboundQueue和waitQueue全部為空才繼續(xù)分發(fā)
// Ensure that the dispatch queues aren't too far backed up for this event.
if (eventEntry->type == EventEntry::TYPE_KEY) {
// 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 StringPrintf("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事件只要求在0.5s內(nèi)窗口接到反饋即可,這個(gè)用waitQueue頭事件的處理事件作為判斷
// 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 StringPrintf("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);
}
}
return "";
}
checkWindowReadyForMoreInputLocked返回reason不為空,則繼續(xù)調(diào)用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) {
//系統(tǒng)沒起來
if (applicationHandle == NULL && windowHandle == NULL) {
... ...
}
} else {
//ANR開始時(shí)間點(diǎn)記錄, 因?yàn)閙InputTargetWaitCause初始值為INPUT_TARGET_WAIT_CAUSE_NONE
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).c_str(),
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 (windowHandle != NULL) {
mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
}
if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
mInputTargetWaitApplicationHandle = applicationHandle;
}
}
}
if (mInputTargetWaitTimeoutExpired) {
return INPUT_EVENT_INJECTION_TIMED_OUT;
}
//再次窗口沒準(zhǔn)備好則會(huì)判斷是否超過ANR時(shí)間,如果超過引發(fā)ANR
if (currentTime >= mInputTargetWaitTimeoutTime) {
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;
} else {
// Force poll loop to wake up when timeout is due.
if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
*nextWakeupTime = mInputTargetWaitTimeoutTime;
}
return INPUT_EVENT_INJECTION_PENDING;
}
}
所以引發(fā)ANR的整體流程可以概況為:
inputdispacher派發(fā)線程在找到分發(fā)窗口后,首先判斷窗口是否準(zhǔn)備好了,如果準(zhǔn)備好了(Key事件兩個(gè)隊(duì)列都為空,touch事件0.5秒內(nèi)有反饋),那么直接寫入InputChannel 分發(fā)給窗口, 分發(fā)線程進(jìn)入休眠等待反饋,準(zhǔn)備進(jìn)行下次循環(huán),如果窗口沒有準(zhǔn)備好,那么記錄ANR開始時(shí)間,在下一次分發(fā)時(shí)如果窗口還沒有準(zhǔn)備好,那么判斷時(shí)間,超時(shí)則拋出ANR
ANR Trace打印
1. AMS中如何處理ANR感昼?
無論是哪種超時(shí)最終都會(huì)調(diào)用方法AppErrors.a(chǎn)ppNotResponding去處理ANR后續(xù)工作:
android/frameworks/base/services/core/java/com/android/server/am/AppErrors.java
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
... ...
//1. 將persistent進(jìn)程和treatLikeActivity(輸入法)還有當(dāng)前發(fā)生ANR 的進(jìn)程放入firstPids中,其余正在運(yùn)行的進(jìn)程放入lastPids中
for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mService.mLruProcesses.get(i);
if (r != null && r.thread != null) {
int pid = r.pid;
if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
if (r.persistent) {
firstPids.add(pid);
if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
} else if (r.treatLikeActivity) {
firstPids.add(pid);
if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
} else {
lastPids.put(pid, Boolean.TRUE);
if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
}
}
}
}
}
}
...
//2. 這里創(chuàng)建和打印ANR trace文件
File tracesFile = ActivityManagerService.dumpStackTraces(
true, firstPids,
(isSilentANR) ? null : processCpuTracker,
(isSilentANR) ? null : lastPids,
nativePids);
//3. 打印CPUInfo
String cpuInfo = null;
if (ActivityManagerService.MONITOR_CPU_USAGE) {
mService.updateCpuStatsNow();
synchronized (mService.mProcessCpuTracker) {
cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
}
info.append(processCpuTracker.printCurrentLoad());
info.append(cpuInfo);
}
info.append(processCpuTracker.printCurrentState(anrTime));
Slog.e(TAG, info.toString());
if (tracesFile == null) {
// There is no trace file, so dump (only) the alleged culprit's threads to the log
Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
}
...
// 4.顯示ANR dialog
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
mService.mUiHandler.sendMessage(msg);
}
}
這里我們重點(diǎn)關(guān)心第二部打印ANR trace文件罐脊,我們在ANR trace文件中會(huì)看到很多進(jìn)程都打出了他們的線程堆棧等信息定嗓,那么是怎么決定哪些進(jìn)程可以打印出來呢,就是上面計(jì)算的firstPids和lastPids決定:
- 發(fā)生ANR的進(jìn)程第一個(gè)打印
- 按最近使用進(jìn)程排序萍桌,persistent和treatLikeActivity進(jìn)程打印
- 其余的最多使用CPU 的前5個(gè)進(jìn)程打印
android/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public static File dumpStackTraces(ArrayList<Integer> firstPids,
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
ArrayList<Integer> nativePids) {
ArrayList<Integer> extraPids = null;
Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
...
//開始創(chuàng)建data/anr路徑下的trace文件
final File tracesDir = new File(ANR_TRACE_DIR);
// Each set of ANR traces is written to a separate file and dumpstate will process
// all such files and add them to a captured bug report if they're recent enough.
maybePruneOldTraces(tracesDir);
// NOTE: We should consider creating the file in native code atomically once we've
// gotten rid of the old scheme of dumping and lot of the code that deals with paths
// can be removed.
File tracesFile = createAnrDumpFile(tracesDir);
if (tracesFile == null) {
return null;
}
dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
return tracesFile;
}
public static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
Slog.i(TAG, "Dumping to " + tracesFile);
// We don't need any sort of inotify based monitoring when we're dumping traces via
// tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
// control of all writes to the file in question.
// We must complete all stack dumps within 20 seconds.
long remainingTime = 20 * 1000;
// First collect all of the stacks of the most important pids.
if (firstPids != null) {
int num = firstPids.size();
for (int i = 0; i < num; i++) {
Slog.i(TAG, "Collecting stacks for pid " + firstPids.get(i));
final long timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile,
remainingTime);
remainingTime -= timeTaken;
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
"); deadline exceeded.");
return;
}
if (DEBUG_ANR) {
Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
}
}
}
∠Α...
Slog.i(TAG, "Done dumping");
}
這個(gè)方法的最終目標(biāo)是中向各個(gè)java/native 進(jìn)程通過標(biāo)準(zhǔn)Linux接口sigqueue發(fā)送SIGQUIT.
2. 應(yīng)用進(jìn)程在trace中的信息是如何打印的?
Zygote fork 子進(jìn)程的時(shí)候會(huì)調(diào)用InitNonZygoteOrPostFork方法上炎,在這里創(chuàng)建SignalCatcher線程恃逻,而這個(gè)線程就是用于監(jiān)控ANR并打印trace
/art/runtime/runtime.cc
void Runtime::InitNonZygoteOrPostFork(
JNIEnv* env,
bool is_system_server,
NativeBridgeAction action,
const char* isa,
bool profile_system_server) {
...
StartSignalCatcher();
...
}
void Runtime::StartSignalCatcher() {
if (!is_zygote_) {
signal_catcher_ = new SignalCatcher(stack_trace_file_, use_tombstoned_traces_);
}
}
接下來看SignalCatcher構(gòu)造方法:
/art/runtime/signal_catcher.cc
SignalCatcher::SignalCatcher(const std::string& stack_trace_file,
bool use_tombstoned_stack_trace_fd)
: stack_trace_file_(stack_trace_file),
use_tombstoned_stack_trace_fd_(use_tombstoned_stack_trace_fd),
lock_("SignalCatcher lock"),
cond_("SignalCatcher::cond_", lock_),
thread_(nullptr) {
...
SetHaltFlag(false);
// Create a raw pthread; its start routine will attach to the runtime.
CHECK_PTHREAD_CALL(pthread_create, (&pthread_, nullptr, &Run, this), "signal catcher thread");
}
這里使用pthread_create系統(tǒng)調(diào)用創(chuàng)建一個(gè)linux線程并開始執(zhí)行其run方法
void* SignalCatcher::Run(void* arg) {
SignalCatcher* signal_catcher = reinterpret_cast<SignalCatcher*>(arg);
CHECK(signal_catcher != nullptr);
Runtime* runtime = Runtime::Current();
//(1)將當(dāng)前線程鏈接到JavaVM,是Linux線程擁有java線程狀態(tài)可以打印堆棧等
CHECK(runtime->AttachCurrentThread("Signal Catcher", true, runtime->GetSystemThreadGroup(),
!runtime->IsAotCompiler()));
Thread* self = Thread::Current();
DCHECK_NE(self->GetState(), kRunnable);
{
MutexLock mu(self, signal_catcher->lock_);
signal_catcher->thread_ = self;
signal_catcher->cond_.Broadcast(self);
}
// Set up mask with signals we want to handle.
SignalSet signals;
//(2) 接收SIGQUIT信號
signals.Add(SIGQUIT);
signals.Add(SIGUSR1);
//(3)循環(huán)等待SIGQUIT信號,當(dāng)有信號到來時(shí)辛块,WaitForSignal方法會(huì)返回
while (true) {
//
int signal_number = signal_catcher->WaitForSignal(self, signals);
if (signal_catcher->ShouldHalt()) {
runtime->DetachCurrentThread();
return nullptr;
}
switch (signal_number) {
case SIGQUIT:
signal_catcher->HandleSigQuit();
break;
case SIGUSR1:
signal_catcher->HandleSigUsr1();
break;
default:
LOG(ERROR) << "Unexpected signal %d" << signal_number;
break;
}
}
}
-
AttachCurrentThread
可參考JNI Tips:所有的線程都是Linux線程畔派,都有l(wèi)inux內(nèi)核統(tǒng)一調(diào)度。他們通常都是由托管代碼(Java或Kotlin)啟動(dòng)(通過使用Thread.start),但它們也能夠在其他任何地方創(chuàng)建润绵,然后連接(attach)到JavaVM线椰。例如,一個(gè)用pthread_create啟動(dòng)的線程能夠使用JNI AttachCurrentThread 或 AttachCurrentThreadAsDaemon函數(shù)連接到JavaVM尘盼。在一個(gè)線程成功連接(attach)之前憨愉,它沒有JNIEnv,不能夠調(diào)用JNI函數(shù)卿捎。
接受到SIGQUIT信號后在HandleSigQuit方法中去處理配紫,這里主要調(diào)用DumpForSigQuit
/art/runtime/runtime.cc
void Runtime::DumpForSigQuit(std::ostream& os) {
GetClassLinker()->DumpForSigQuit(os);//打印ClassLoader信息
GetInternTable()->DumpForSigQuit(os);
GetJavaVM()->DumpForSigQuit(os);//虛擬機(jī)信息
GetHeap()->DumpForSigQuit(os);//堆信息,對象分配情況
oat_file_manager_->DumpForSigQuit(os);
if (GetJit() != nullptr) {
GetJit()->DumpForSigQuit(os);
} else {
os << "Running non JIT\n";
}
DumpDeoptimizations(os);
TrackedAllocators::Dump(os);
os << "\n";
thread_list_->DumpForSigQuit(os);//線程調(diào)用棧午阵,這里首先suspendall 再打印堆棧
BaseMutex::DumpAll(os);
// Inform anyone else who is interested in SigQuit.
{
ScopedObjectAccess soa(Thread::Current());
callbacks_->SigQuit();
}
}
打印ClassLoader,堆躺孝,GC以及線程堆棧信息,除了我們平時(shí)最關(guān)心的線程堆棧底桂,其實(shí)還有很多信息植袍,我們以堆為例看下在anr文件中是什么樣子的
/art/runtime/gc/heap.cc
void Heap::DumpForSigQuit(std::ostream& os) {
os << "Heap: " << GetPercentFree() << "% free, " << PrettySize(GetBytesAllocated()) << "/"
<< PrettySize(GetTotalMemory()) << "; " << GetObjectsAllocated() << " objects\n";
DumpGcPerformanceInfo(os);
}
----- pid 18841 at 2020-07-01 11:54:25 -----
Cmd line: com.dunkinbrands.otgo
Build fingerprint: 'TCL/Alcatel_5002R/Seoul_ATT:10/QP1A.190711.020/vGZA4-0:user/release-keys'
ABI: 'arm'
Build type: optimized
Zygote loaded classes=9140 post zygote classes=8201
Dumping registered class loaders
#0 dalvik.system.PathClassLoader: [], parent #1
#1 java.lang.BootClassLoader: [], no parent
#2 dalvik.system.PathClassLoader: [/data/app/com.dunkinbrands.otgo--NP2B6q6I0_yAuykgwOpBA==/base.apk:/data/app/com.dunkinbrands.otgo--NP2B6q6I0_yAuykgwOpBA==/base.apk!classes2.dex], parent #1
#3 dalvik.system.InMemoryDexClassLoader: [/data/user/0/com.dunkinbrands.otgo/Anonymous-DexFile@2165240094.jar], parent #2
#4 dalvik.system.InMemoryDexClassLoader: [/data/user/0/com.dunkinbrands.otgo/Anonymous-DexFile@1741528175.jar], parent #2
#5 dalvik.system.DexClassLoader: [/data/user/0/com.dunkinbrands.otgo/cache/generated-3F92340A3D311257BAA79F7EDB761B9E6E665EE8.jar], parent #2
#6 dalvik.system.PathClassLoader: [/data/app/com.google.android.gms-7yZdufzTzp3vlSkd2gcjDQ==/base.apk:/data/app/com.google.android.gms-7yZdufzTzp3vlSkd2gcjDQ==/base.apk!classes2.dex:/data/app/com.google.android.gms-7yZdufzTzp3vlSkd2gcjDQ==/base.apk!classes3.dex:/data/app/com.google.android.gms-7yZdufzTzp3vlSkd2gcjDQ==/base.apk!classes4.dex:/data/app/com.google.android.gms-7yZdufzTzp3vlSkd2gcjDQ==/base.apk!classes5.dex:/data/app/com.google.android.gms-7yZdufzTzp3vlSkd2gcjDQ==/base.apk!classes6.dex], parent #1
#7 dalvik.system.PathClassLoader: [/data/app/com.google.android.trichromelibrary_410410680-CHXkMAKnKoz7r9p8q3qhLw==/base.apk], parent #1
#8 dalvik.system.PathClassLoader: [/data/app/com.google.android.webview-3b4RqE2Pf-rc8rUq7I6Mag==/base.apk], parent #1
#9 dalvik.system.PathClassLoader: [/system/framework/org.apache.http.legacy.jar], parent #1
#10 com.google.android.gms.dynamite.zzh: [/data/user_de/0/com.google.android.gms/app_chimera/m/0000000c/DynamiteLoader.apk], parent #0
#11 dalvik.system.DelegateLastClassLoader: [/data/user_de/0/com.google.android.gms/app_chimera/m/00000010/MapsDynamite.apk], parent #2
#12 dalvik.system.DelegateLastClassLoader: [/data/user_de/0/com.google.android.gms/app_chimera/m/0000000f/GoogleCertificates.apk], parent #2
Done dumping class loaders
Intern table: 49095 strong; 2443 weak
JNI: CheckJNI is off; globals=983 (plus 110 weak)
Libraries: /data/app/com.dunkinbrands.otgo--NP2B6q6I0_yAuykgwOpBA==/lib/arm/libag3.so /data/app/com.google.android.gms-7yZdufzTzp3vlSkd2gcjDQ==/lib/arm/libconscrypt_gmscore_jni.so /data/app/com.google.android.trichromelibrary_410410680-CHXkMAKnKoz7r9p8q3qhLw==/base.apk!/lib/armeabi-v7a/libmonochrome.so /data/user/0/com.dunkinbrands.otgo/files/libcrashreport.so /system/lib/libwebviewchromium_plat_support.so libandroid.so libcompiler_rt.so libdcfdecoderjni.so libjavacore.so libjavacrypto.so libjnigraphics.so libmedia_jni.so libopenjdk.so libsoundpool.so libwebviewchromium_loader.so (15)
Heap: 6% free, 38MB/41MB; 657564 objects
... ...
總結(jié)
本文主要主要介紹了如下內(nèi)容:
- 以廣播超時(shí)和輸入時(shí)間處理超時(shí)為例介紹了ANR發(fā)生場景,這里簡單介紹了有序和無序廣播實(shí)現(xiàn)原理籽懦,而只有有序廣播才會(huì)存在觸發(fā)ANR的情況,輸入事件超時(shí)利用的是判斷waitQueue中事件處理的時(shí)間于个,即應(yīng)用處理事件反饋時(shí)長
- 符合觸發(fā)ANR 條件后,AMS是如何處理的暮顺,主要包括Log中打印ANR原因以及CPU Info厅篓,創(chuàng)建和打印trace文件,彈出dialog殺應(yīng)用進(jìn)程等捶码,這里重點(diǎn)關(guān)注AMS通知應(yīng)用進(jìn)程dump進(jìn)程信息是通過信號通信方式羽氮,及發(fā)送SIGQUIT
- 應(yīng)用進(jìn)程創(chuàng)建時(shí)會(huì)創(chuàng)建一個(gè)SignalCatcher線程,SignalCatcher線程監(jiān)聽到SIGQUIT宙项,首先suspendall 再打印線程堆棧等信息