Notes on Android's Asynchronous Message Processing Mechanism

Posted by Gazan on Mon, 01 Jul 2019 21:17:44 +0200

Label (Space Separation): Uncategorized

Study Android's asynchronous message processing mechanism and take notes.
First of all, I read Guo Lin's article. The Android asynchronous message processing mechanism is fully parsed, giving you a thorough understanding of the source code perspective
Processing flow:

After reading it, I summarize it:

1. Handler objects can be created directly in the main thread, while Looper.prepare() needs to be called first in the sub-thread to create Handler objects.

2. The principle of updating the UI in the main thread is that the handlerMessage() method is the thread that runs in the Looper bound by the Handler.

        new Thread(new Runnable() {
            @Override
            public void run() {
                handler = new Handler(getMainLooper()){
                    @Override
                    public void handleMessage(Message msg) {
                        //At this point handleMessage runs in the UI thread
                        Log.e("lzz",Thread.currentThread().getName());
                    }
                };
                handler.sendEmptyMessage(0);
            }
        }).start();
new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                handler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        //At this point handleMessage runs in a sub-thread
                        Log.e("lzz",Thread.currentThread().getName());
                    }
                };
                handler.sendEmptyMessage(0);
                Looper.loop();
            }
        }).start();

3. Understanding of Looper.loop().

The Looper.loop() method opens a dead loop to iterate over messages in the queue if the code that created Handler in the above subthread changes to this

Looper.loop();// Here's a dead cycle.
// Thereafter the code could not be executed
handler.sendEmptyMessage(0);

So why does Looper.loop() in the main thread keep looping indefinitely? Why doesn't it cause ANR?
Refer to the following to understand

Turn:

As we know, time-consuming operations in the main thread in Android can cause ANR (Application Not Responding) exceptions.

There are generally two reasons for ANR:

    Current events have no chance of being processed (that is, the main thread is processing the previous event, not completing in time, or looper is blocked for some reason)
    Current events are being handled, but not completed in time.

To avoid ANR exceptions, android uses the Handler message processing mechanism. Let time-consuming operations run in sub-threads.

So a question arises: Looper.loop() in the main thread keeps looping indefinitely to detect whether there are new messages in the message queue. Why doesn't it cause ANR?

Source code analysis

ActivityThread.Java is the main thread entry class. Here you can see the common main method for writing Java programs, and the main method is the entry for the entire Java program.

    ActivityThread Source code

    public static final void main(String[] args) {
        ...
        //Create Looper and MessageQueue
        Looper.prepareMainLooper();
        ...
        //Poller begins polling
        Looper.loop();
        ...
    }

    Looper.loop()Method

    while (true) {
    //Messages that take out message queues may be blocked
    Message msg = queue.next(); // might block
    ...
    //Parse and distribute messages
   msg.target.dispatchMessage(msg);
    ...
}

Obviously, if looper is not looped in the main method, the main thread will exit as soon as it runs. This is an egg!

Summary: ActivityThread's main method is to do message loops. Once you exit the message loops, your application will exit.

We know the need for message loops, so why does this dead loop not cause ANR exceptions?

Because Android is event-driven, Looper.loop() constantly receives and processes events. Every click-touch or Activity's life cycle runs under the control of Looper.loop(), and if it stops, the application stops. Only one message or the processing of the message blocked Looper.loop(), not Looper.loop().

That is to say, our code is actually executed in this loop, of course, will not block.

handleMessage Method partial source code

 public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case RELAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                handleRelaunchActivity(r);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case PAUSE_ACTIVITY:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0);
                maybeSnapshot();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case PAUSE_ACTIVITY_FINISHING:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            ...........
        }
    }

It can be seen that the life cycle of Activity depends on the Loper. loop of the main thread, and the corresponding measures are taken when different messages are received.

If a message takes too long to process, such as when you process time-consuming operations in onCreate () and onResume (), then the next message, such as the user's click event, can't be processed, and the whole cycle will produce a carton, which will become ANR in a long time.

Let's look at the causes of ANR again, and you may understand.

There are generally two reasons for ANR:

    Current events have no chance of being processed (that is, the main thread is processing the previous event, not completing in time, or looper is blocked for some reason)
    Current events are being handled, but not completed in time.

Moreover, the main thread Looper reads messages from the message queue, and when all messages are read, the main thread blocks. Subthreads send messages to message queues and write data to pipeline files. The main thread is awakened to read data from pipeline files. The main thread is awakened only to read messages. When the message is read, it sleeps again. So loop loops don't consume too much CPU performance.

Summary: The Looer.loop() method may cause blocking of the main thread, but as long as its message loop is not blocked, the ANR exception will not occur if it can handle events all the time.

Above.

Topics: Android Java