First, let’s list the general situations of ANR:
1. Keyboard input event, touch no response for 5s;
2. Receiver broadcast receiver 10s timeout and no response;
3. Service service 20s timeout and no response;
...
Let me briefly introduce
Handler will start in the main method of ActivityThread after the application starts:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Here we can see three key lines:
Looper.prepareMainLooper();
...
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop(); //loop
The loop method in Looper will take out the message from the next method in MessageQueue
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
msg.recycleUnchecked();
}
}
Message msg = queue.next(); // might block
The next method will call the native method nativePollOnce ,
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
...
}
}
When there is no message in the MessageQueue , queue.next() will be blocked in the nativePollOnce() method. When nativePollOnce() is blocked, the main thread will release CPU resources and enter a dormant state.
Until a new Message is obtained , the main thread will be woken up to continue working.
There is a difference between the main thread going to sleep and an infinite loop here:
The sleep state means that in the kernel state, the main thread is suspended, and the thread state is transferred to the sleep state.
Infinite loop refers to stuck execution of a time-consuming operation in the main thread, loop() will always process a message, and there are many messages in the for loop that need to be processed, and this message takes a long time to process, this message The processing time will turn into no response to other click events, thus triggering ANR.