A Handler,

1.1 Why does Android == non-UI threads == cannot update UI ==

  • UI threading mechanism
  • Why isn’t the UI designed to be thread-safe
  • Must a non-UI thread not update the UI
1.1.1 UI threading Mechanism

    public static void main(String[] args) {
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. // The main thread will run in an infinite loop that blocks looper.loop (); // If the loop exits, the program crashes throwing new RuntimeException("Main thread loop unexpectedly exited");
    }
Copy the code

The UI thread is the thread in the process fork directly from Zygote, which is the main in Activty.

1.1.2 Why isn’t THE UI designed to be thread-safe

If you update THE UI in the subthread, you must lock the view update operation. The efficiency of locking is affected and cannot be accepted.

  • UI is variable, even at high frequencies
  • The UI is very sensitive to response time
  • UI components must be drawn in batches to ensure efficiency
1.1.3 Must non-UI threads not update the UI?

The answer is no, you can’t update directly, but you can update indirectly, such as post, postinvalidate. And the SurfaceView updates the UI directly in the child thread. You can also update the UI in child threads during the Activity’s onCreat life cycle.

Update the UI thread to check whether the thread that created the UI is to update the UI thread, update the UI is in ViewRootImpl do requestLayout, do thread detection; But ViewRoot is implemented in the onResume addDecView, so if you create a child thread in onCreat to update the UI, maybe you haven’t created the ViewRoot yet, so you get around the detection. It is possible to play the Toast in the child thread because the Toast is played on windowManager and is not related to the Activity or ViewRoot. However, since handler is used in the Toast, looper.prepare () is used in the child thread. So, it’s possible to update the UI in child threads

1.2 Is the Handler Post delay time reliable?

Answer: Definitely not, because sometimes messages can’t be processed (the Looper belt is overloaded) and there are too many time-consuming operations on the main thread for messages to be delivered in time. This results in a lag, and since the lag is not reliable, the delay is not reliable.

Insertion of messages

Boolean enqueueMessage(Message MSG, long when) {synchronized (this) {// MSG is Message MSG. When = when;  //MessageQueue maintains a Message queue (linked list data structure), p must be empty at the beginning of the Message p = mMessages; //mMessages is the pre-linked list currently maintained.if(p = = null | | the when = = 0 | | the when < p.w hen) {/ / to insert/headers/New head, wake up the event queueif blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else{// Insert the insertion point from the middle of the list is a Message prev determined by the size of the time;for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr ! = 0 because mQuitting is false.if(needWake) { nativeWake(mPtr); }}return true;
    }
Copy the code

The operation here is the insert operation of the linked list. MessageQueue chooses the linked list as the data object mainly because the insert and delete operation of the linked list is very efficient. Let’s look at the native code for message wake up

==nativeWake==

void NativeMessageQueue::wake() {
    mLooper->wake();
}

void Looper::wake() { uint64_t inc = 1; Ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(Uint64_t))));Copy the code

It simply writes the character 1 to the pipe and wakes up

Message extraction

    Message next() {
        int nextPollTimeoutMillis = 0;
        for(;;) NextPollTimeoutMillis = -1 when there is no message, the blocking program will not proceed any further, unless there is a new message or the wait time is up, and the call to nextPollTimeoutMillis will block for as long as the value is, such as 200ms, It is automatic wake up after 200ms. nativePollOnce(ptr, nextPollTimeoutMillis); Synchronized (this) {// Try to retrieve the next message.returnif found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if(msg ! = null && msg.target == null) {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. At present, mBlocked = is fetched directly and wants to be removed from the listfalse;
                        if(prevMsg ! = null) { prevMsg.next = msg.next; }else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: "+ msg); msg.markInUse(); // Execute the loop immediatelyreturnmsg; }}else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    returnnull; } pendingIdleHandlerCount = 0; nextPollTimeoutMillis = 0; }}Copy the code

How is the handler message delay implemented?

  • Is there something special about message delay
  • Message delay is the sending delay is the processing delay
  • How accurate is the message delay? Is it reliable?

Here, the message waiting blocking mechanism is mainly implemented in nativePollOnce. When the current message time is not up, or there is no message, it will wait for hibernation state, which does not consume CPU. This is the IO multiplexing mechanism of Linux

www.jianshu.com/p/c1ae21d36…

==nativePollOnce==

[-> android_os_MessageQueue.cpp]

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, Jint timeoutMillis) {// PTR message queue address NativeMessageQueue* NativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(PTR); nativeMessageQueue->pollOnce(env, obj, timeoutMillis); } void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, Int timeoutMillis) {// pollOnce mLooper->(timeoutMillis); }Copy the code

==mLooper is a Native Looper object that encapsulates epoll and has nothing to do with Java Looper. = =

[-> /system/core/libutils/Looper.cpp]

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for(;;) {...if (result != 0) {
            ...
            returnresult; } result = pollInner(timeoutMillis); } } int Looper::pollInner(int timeoutMillis) { ... int result = POLL_WAKE; mResponses.clear(); mResponseIndex = 0; mPolling =true; Struct epoll_event eventItems[EPOLL_MAX_EVENTS]; // Waiting for an event to occur or a timeout, the nativeWake() method writes characters to the pipe writer, and the method returns; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); .return result;
}

Copy the code

Looper#loop()

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 blockif (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in caseA UI event sets the logger // Messages are printed before and after distributionlogFinal Printer Logging = me.mlogging;if(logging ! = null) { logging.println(">>>>> Dispatching to " + msg.target + "" +
                        msg.callback + ":"+ msg.what); } / / distribute news MSG. Target. DispatchMessage (MSG);if(logging ! = null) { logging.println("<<<<< Finished to " + msg.target + ""+ msg.callback); }}}Copy the code

Some ideas for message queue optimization:

  • The message filter
  • Messages are mutually exclusive. For example, if a message is stopped, all messages before it need not be stopped
void removeCallbacksAndMessages(Handler h, Object object
Copy the code
  • Message reuse object pool
  /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if(sPool ! = null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clearin-use flag
                sPoolSize--;
                returnm; }}return new Message();
    }
Copy the code
  • The message is idle IdleHandler

1.3 Why does main thread Looper not cause ANR

  • What are the conditions for ANR to occur
  • How does Looper work
  • What is the essential reason why Looper does not lead to ANR
  • Does the Looper loop cause high CPU usage
1.3.1 Conditions for ANR generation

The anR generated by the ActivityServices class is an anR generated by the service. The anR generated by the ActivityServices class is an ANR generated by the service

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {/ / embedded bomb bumpServiceExecutingLocked (r,execInFg, "create"); / / execution service oncreate () operation app. Thread. ScheduleCreateService (r, r.s erviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); / / disarm bombs serviceDoneExecutingLocked (r,inDestroying, inDestroying);

        }
        

Copy the code

Embedded bomb

 private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
     
     scheduleServiceTimeoutLocked(r.app);
 }
  
  
 void scheduleServiceTimeoutLocked(ProcessRecord proc) {
        if (proc.executingServices.size() == 0 || proc.thread == null) {
            return; } Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_TIMEOUT_MSG); msg.obj = proc; / / send a message with deferred to MQ, but not necessarily perform mAm. MHandler. SendMessageDelayed (MSG, proc. ExecServicesFg? 20*000:200 * 1000); }Copy the code

Disarm bombs

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inBefore Destroying, Boolean finishing) {/ / remove the delay of message mAm. MHandler. RemoveMessages (ActivityManagerService SERVICE_TIMEOUT_MSG, r.app); }Copy the code

Summary: == execute oncreate these operations before sending a delay message, the delay time is the foreground service 20s, background service 200s, and then to execute onCREATE operation, if the execution is completed within this period of time, the delay message is removed before it is too late to execute. = =

1.3.2 Does Looper produce ANR

Of course not. Looper is the operation mechanism of the main thread, and ANR is just a detail of time monitoring designed in the main thread. There is no relationship between the two, so there is no ANR.

1.3.3 Will The Looper loop lead to high CPU Usage?

Of course not. If the Looper belt had been idling all the time, it would have been consuming CPU, but the fact is that it is not idling, it samples the ==epoll mechanism, and when there is no message coming it is in the blocking state wait, so it would not have been idling and consuming CPU. Wake Looper will resume work when there is information.

Epoll:www.jianshu.com/p/31cdfd6f5…

The epoll mechanism used here is a ==IO multiplexing mechanism ==, which can monitor multiple descriptors at the same time. When a descriptor is ready (read or write ready), it immediately notifies the corresponding program of read or write operations. In essence, synchronous I/O, that is, read and write is blocked. Therefore, the main thread is dormant most of the time and does not consume a lot of CPU resources.