Source code analysis
Through the above simple code, we can realize the interception and monitoring of crash and ANR, but we may not know why, including we know that ANR occurs, but we need to further analyze where ANR occurs and how to solve it. The problems analyzed today are:
- How to intercept global crash and avoid APP exit.
- How to realize ANR monitoring.
- Use Handler to realize the function of single process pool.
- Why is the life cycle of an Activity sent and executed by a Handler.
- How to implement the delayed operation of Handler.
Source code involved
/java/android/os/Handler.java /java/android/os/MessageQueue.java /java/android/os/Looper.java /java/android.app/ActivityThread.java Copy code
Let's start with the APP startup. The APP startup method is to create the looper of the main thread in the ActivityThread and the main method, that is, the current process. And finally called Looper. at the end of the main method. Loop() handles the task scheduling of the main thread in this method. Once this method is executed, it means that the APP is exited. If we want to avoid the APP being exited, we must let the APP continue to execute loop loop().
package android.app; public final class ActivityThread extends ClientTransactionHandler { ... public static void main(String[] args) { ... Looper.prepareMainLooper(); ... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } }
Looper.loop()
Let's further analyze looper Loop () method, in which a loop is written, only when queue Exit only when next() = = null. We may have a question in our mind. If there is no main thread task, is it looper The loop () method exits? Actually, queue Next () is actually a blocking method. If there is no task or active exit, it will always block and wait for the main thread task to be added.
When there are tasks in the queue, the information Dispatching to... Will be printed, Then call MSG target. dispatchMessage(msg); When the task is executed, the information will be printed, We can analyze the ANR through the printed information. Once the task is executed for more than 5 seconds, the system will trigger the system to prompt the ANR, but we must be more strict with our APP. We can set a goal for us and report statistics beyond the specified time to help us optimize.
public final class Looper { final MessageQueue mQueue; 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; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } try { msg.target.dispatchMessage(msg); } finally {} if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } msg.recycleUnchecked(); } } public void quit() { mQueue.quit(false); } }
If an exception occurs in the main thread, it will exit the loop, which means that the APP crashes. Therefore, we need to try catch to avoid APP exit. We can start another looper in the main thread Loop () executes the main thread task, and then try catch the looper Loop () method, it will not exit.
Implementation of single thread thread pool based on Handler
From looper Loop (), we can use the Handler to realize the single thread pool function, and like the main thread, this thread pool has powerful functions such as immediate post(), delayed post(), and timed postAtTime().
// Incorrect usage var handler: Handler? = null Thread({ handler = Handler() }).start()
When we execute the above code on an asynchronous thread, we will report an error can't create Handler inside thread [thread-2,5, main] that has not called Looper prepare(). This is because the Handler relies on loopers. You must create loopers for threads to function normally. The correct usage is as follows:
// Correct usage var handler: Handler? = null Thread({ Looper.prepare() handler = Handler() Looper.loop() }).start()
Test:
button.setOnClickListener { handler?.post { println(Thread.currentThread()) } handler?.post { println(Thread.currentThread()) } }
Output results:
System.out: Thread[Thread-2,5,main] System.out: Thread[Thread-2,5,main]
HandlerThread
HandlerThread is the encapsulation of Thread by Android. It adds the support of Handler. The implementation is to realize the functions of the previous example
val handlerThread = HandlerThread("test") handlerThread.start() handler = Handler(handlerThread.looper)
Source code analysis of MessageQueue
As we all know, Handler has rich functions, including immediate post(), delayed post(), and timed postAtTime(). Here is how to implement it from the source code analysis.
public final class MessageQueue { Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. 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); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); #### last If you want to know more about the interview in a large factory, you can praise and support it. In addition, I also share some free high-quality resources, including:**Android study PDF+Architecture video+Source notes**,**Advanced architecture technology, advanced brain map Android Develop interview materials and advanced architecture materials** The contents of these pieces. To share with you, it is very suitable for friends who have recent interviews and want to continue to improve on the technical road. **[CodeChina Open source projects:< Android Summary of study notes+Mobile architecture video+Real interview questions for large factories+Project practice source code](https://codechina.csdn.net/m0_60958482/android_p7)**