[Android R source code] Source code analysis of Hnadler message processing mechanism

1. Common usage methods of Handler

Handler is a message mechanism provided by Android. It is usually used to accept the data sent by the child thread, and use this data to cooperate with the main thread to update the UI.

For example, clicking a button goes to the server to request data. If the request operation is performed directly on the main thread (UI thread), the interface will appear suspended animation, and if it has not been completed for a long time, an error message "Application Not Responding (ANR)" will be received from the Android system. why? Because in Android, the responsiveness of the App is monitored by the Activity Manager and Window Manager system services. Usually, the ANR dialog box will pop up in the following three situations :

1: KeyDispatchTimeout (Google default 5s, MTK platform is 8s) -- main type key or touch event does not respond within a specific time

2: BroadcastTimeout(10s) BroadcastReceiver cannot complete processing within a specific time

3: ServiceTimeout(20s) -- small probability type Service cannot be processed within a specific time.

There are many ways for Handler to send messages

  • msg.sendToTarget();
  • mHandler.sendMessageAtFrontOfQueue();
  • mHandler.sendEmptyMessage()
  • mHandler.sendEmptyMessageDelayed()
  • mHandler.post()
  • mHandler.postDelayed()

Then we put these time-consuming operations in the child thread to execute. But here comes the problem, the data needs to be filled into related controls for display. However, updating the UI in Android can only be updated in the main thread, and operations in sub-threads are dangerous, so you need to use a handler.

// 创建一个Handler 对象,运行在主线程 ui线程中
    private final Handler myHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case 0:
                    String reslut = (String) msg.obj;
                    textView.setText(reslut);
            }
        }
    };

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = findViewById(R.id.myText);
            button = findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    threadRun();
                }
            });
        }


        @Override
        protected void onResume() {
            super.onResume();
        }

// 创建个 子线程
        private void threadRun() {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int i = 0; i < 100; i++) {
                            result += i;
                        }
// 休眠个 6 s
                        Thread.sleep(6000);
                        Message message = Message.obtain();
                        message.what = 0;
                        message.obj = result;

// 通过handler将子线程数据传给主线程
                        myHandler.sendMessage(message);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
        }

2. Important classes of message processing mechanism

  • ThreadLocal

ThreadLocal is a local variable of the thread, which is held individually by each thread. When using ThreadLocal to maintain variables, provide an independent copy of the variable for each thread that uses the variable, that is, each thread will have a variable inside, so that multiple threads accessing the variable at the same time will not affect each other, so they All are used are copies of variables copied from memory, so there is no thread safety problem.

  • Message 

Message implements the Parcelable interface, that is, realizes serialization , indicating that Message can be used for inter-process communication.   

  • MessageQueue

MessageQueue is indeed a container for storing messages (Message objects). It is a one-way linked list . The Message object has a next field to save the next one in the list, and mMessages in MessageQueue saves the first element of the linked list.

  • Looper 

Looper circulator, Looper is to provide a message loop for threads. By default, the thread does not have a Looper, you can use prepare() to get a loop, and use the loop() method to start processing information until the loop stops.
Most of the interaction with the information loop is through the Handler class.

A MessageQueue needs a Looper. 
  • Handler 

Handler handler

Responsible for the sending and processing of Message. When using Handler, you need to implement the handleMessage(Message msg) method to process a specific Message and save it in the message queue, such as updating the UI. 

Android Handler Mechanism_Android Handler

 Combined with the code example above and the implementation process in the above figure, to use Handler to implement asynchronous message processing, first we need to create a Handler object in the main thread and rewrite the handleMessage() method, and then when UI operations are required in the sub-thread , create a Message object and send this message through Handlerr. After that, the message will be added to the MessageQueue queue to be processed, and the Looper will always try to take out the pending message from the MessageQueue, and finally distribute it back to the Handler's handleMessage() method. Since Halldler is created in the main thread, the code in the handleMessage() method will also run in the main thread at this time, so that the child thread can realize the purpose of UI thread operation through the Handler mechanism.
 

3. Source code analysis

First look at the creation of the main thread:

ActivityThread is what we often call the main thread or UI thread. The main method of ActivityThread is the real entry point of an APP, and MainLooper is created in its main method. Both Handler and Looper are created and initialized at this stage

  • frameworks/base/core/java/android/app/ActivityThread.java
    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // Install selective syscall interception
        AndroidOs.install();

        // 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();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        // Call per-process mainline module initialization.
        initializeMainlineModules();

        Process.setArgV0("<pre-initialized>");

// 开始主线程循环
        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
// 创建 thread 对象
        ActivityThread thread = new ActivityThread();
// 在attach方法中会完成Application对象的初始化,然后调用Application的onCreate()方法
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
// 获取到 Handler 对象
            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");
    }


--------------------------------------
其中:获取到 Handler 对象
    final H mH = new H();

    @UnsupportedAppUsage
    public Handler getHandler() {
        return mH;
    }

    class H extends Handler {
        public static final int BIND_APPLICATION        = 110;

        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
。。。。。。
// 创建服务
                case CREATE_SERVICE:
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                ("serviceCreate: " + String.valueOf(msg.obj)));
                    }
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
// 绑定服务
                case BIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;


The Handler of the main thread, as a member variable of the ActivityThread, is initialized when the main method of the ActivityThread is executed and the ActivityThread is created. MessageQueue is initialized and created as a member variable when Looper is created.

1) Handler  sendMessage sends a message to the message queue

If the child thread wants to communicate with the ui thread and send a message, then start the analysis from myHandler.sendMessage(message):

  • frameworks/base/core/java/android/os/Handler.java
    public final boolean sendMessage(@NonNull Message msg) {
// 延时为 0
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {

// 先获取到消息队列
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
// 最终调用到消息队列去 入队列
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Call enqueueMessage() to send the Message to the MessageQueue. So what is MessageQueue? As the name implies, it is a message queue. In fact, when we create a Handler, it needs to be associated with a Looper. The Looper class has a member variable

Take a look to get the message queue MessageQueue queue = mQueue, where does mQueue come from

It is assigned a value in the constructor of Handler

    public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
// 这里通过 Looper 获取到 looper 对象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
// 通过 looper 获取到 消息队列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

// 第二个构造方法,直接传入 looper
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

The enqueueMessage method of MessageQueue goes into the queue

  • frameworks/base/core/java/android/os/MessageQueue.java
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
// 如果队列前面没有元素p == null,如果没有延时,时间为0,则马上执行,则直接入队列首位(链表实现),等待loop的轮询
// 排在队列第一位的先执行
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
// 遍历所有的message对象
                for (;;) {
                    prev = p;
                    p = p.next;
// 
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                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;
    }

If none of the above conditions are met, enter else. We can see that line 17 has a for loop to traverse the existing message objects, and line 20 has an if statement when < p.when when is a new message. Execution time, p.when is the execution time of the message message in the queue. If a message that is executed later than the new message is found, the operation of inserting into the linked list is executed:

msg.next = p; 

prev.next = msg;

That is, insert it in front of the message, and execute the new message first.

2) Create a Looper object

In the front, you can see that when the main thread is created, the prepareMainLooper method will be called to create a looper method, so there will always be a Looper object in the main thread of the application, so there is no need to manually call the Looper.prepare() method. Create a looper on the main thread.

  • frameworks/base/core/java/android/os/Looper.java
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    public static void prepareMainLooper() {
// 在prepare 会创建个对象 Looper
// bool 参数是用于判断是否可以 退出循环,主线程为 false,不退出
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

// 创建对象 Looper,保存到 sThreadLocal中
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));

// 通过 sThreadLocal获取对应创建的 Looper 
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }    

The sThreadLocal field is modified by static final, indicating that it is a constant shared by the class, which provides a copy of the Looper class for each thread.

How to ensure that there is only one Looper in a thread? The constructor of Looper is private and can only be called in prepare. And if a ThreadLocal gets the corresponding value, it means it has already been created. will throw an exception. In its constructor, each looper object binds the current thread with a message queue

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
// 获取当前线程
        mThread = Thread.currentThread();
    }

 A most standard way of writing asynchronous message processing thread:

class LooperThread extends Thread {
      public Handler mHandler;
 
      public void run() {
          Looper.prepare();
 
          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };
 
          Looper.loop();
      }
  }

// 在子线程中发送消息

    public void click(View v){
    final Handler handler =  new Handler();
    new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            handler.sendMessage();
            Looper.loop();
        }
    }) .start();
}

3) Start working with Looper

It is to call the loop() method: poll to get the message Message

  • frameworks/base/core/java/android/os/Looper.java
    public static void loop() {

// 获取 Looper 对象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        me.mSlowDeliveryDetected = false;

// 
        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

loopOnce method:

    @SuppressWarnings("AndroidFrameworkBinderIdentity")
    private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {

// 获取到 消息队列的元素
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }

        // 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);
        }
        // Make sure the observer won't change while processing a transaction.
        final Observer observer = sObserver;

        final long traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
        if (thresholdOverride > 0) {
            slowDispatchThresholdMs = thresholdOverride;
            slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }

        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        Object token = null;
        if (observer != null) {
            token = observer.messageDispatchStarting();
        }
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
// 将消息分发,发送给 自己定义的子类
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (logSlowDelivery) {
            if (me.mSlowDeliveryDetected) {
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    me.mSlowDeliveryDetected = false;
                }
            } else {
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    me.mSlowDeliveryDetected = true;
                }
            }
        }
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();

        return true;
    }

An inter-process communication mechanism of Linux: pipe. Principle: There is a special file in memory, this file has two handles (references), one is a read handle, and the other is a write handle

The main thread Looper reads messages from the message queue. When all messages are read, it goes to sleep and the main thread blocks. The sub-thread sends a message to the message queue and writes data to the pipeline file. The main thread is woken up to read data from the pipeline file. The main thread is woken up just to read the message. When the message is read, it sleeps again. So it will not consume too much performance.

Read data from MessageQueue

  • frameworks/base/core/java/android/os/MessageQueue.java
    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 {
// 取出对应的 message
                        // 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);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

nextPollTimeoutMillis is a very important parameter. During normal looping, this parameter is 0. When there is a delayed message and the message is not ready, a delay time will be set for the next cycle to execute. When the message is empty, this parameter will be set to -1.

The nativePollOnce method is a native method, and it is also a method of blocking the message queue. When the previous parameter is -1, the message queue will fall into a waiting state.

Handler message processing mechanism flow:

1. If you want Handler to work normally, you must have a Looper object in the current thread

2. When the Looper object is initialized, a MessageQueue associated with it will be created;

3.MessageQueue stores and manages Message

4. Message is the message object received and processed by Handler

Therefore, the Handler processing of the Android source code is divided into two major processes: acceptance and processing:

1. Send a message: send a message through post, send and other methods (essentially send) to send a message (runnable is also converted into a message at the end). In the end, the enqueueMessage method of MessageQueue is called to insert the Message into MessageQueue

2. Receive messages: We must enable Looper.loop() to poll, and remove and obtain the previously sent Message object by calling the next() method in MessageQueue (when MessageQueue is empty, next blocks). The obtained Message object is finally processed by the dispatchMessage method of the Handler object (message.target) that sent the message. The dispatchMessage method will eventually go to the handlerMessage() method. So when we create a Handler, we can set it in its handlerMessage() or callback

4) Common interview questions

1.  Why does the Looper.loop() in the main thread always loop infinitely without causing ANR ?

Because when the Looper finishes processing all the messages, it will enter the blocking state, and when a new Message comes in, it will break the blocking and continue to execute.

This actually does not understand the concept of ANR. ANR, full name Application Not Responding. When I send a UI drawing message to the main thread Handler and it is not executed after a certain period of time, an ANR exception is thrown. Looper's infinite loop is to perform various transactions in a loop, including UI drawing transactions. The infinite loop of the Looper indicates that the thread is not dead. If the Looper stops looping, the thread ends and exits. Looper's infinite loop itself is one of the reasons to ensure that UI drawing tasks can be executed. At the same time, the UI drawing task has a synchronization barrier, which can ensure faster execution of drawing.

2. Why doesn't the main thread initialize Looper?

Because the application has initialized the main thread Looper during the startup process.

Every java application has a main method entry, and Android is a Java-based program is no exception. The entry of the Android program is in the main method of ActivityThread:

public static void main(String[] args) {
    ...
 // 初始化主线程Looper
    Looper.prepareMainLooper();
    ...
    // 新建一个ActivityThread对象
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    // 获取ActivityThread的Handler,也是他的内部类H
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    ...
    Looper.loop();
 // 如果loop方法结束则抛出异常,程序结束
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

In the main method, first initialize the main thread Looper, create a new ActivityThread object, and then start the Looper, so that the main thread Looper will run when the program starts. We don't need to initialize the main thread Looper anymore.

3. How does Handler ensure the security of concurrent access to MessageQueue?

Cyclic locking, with blocking wake-up mechanism.

We can find that MessageQueue is actually a "producer-consumer" model. Handler keeps putting in messages, and Looper keeps taking them out, which involves deadlock problems. If the Looper gets the lock, but there is no message in the queue, it will wait forever, and the Handler needs to put the message in, but the lock is held by the Looper and cannot join the queue, which causes a deadlock. The solution to the Handler mechanism is circular locking . In the next method of MessageQueue:

Message next() {
   ...
    for (;;) {
  ...
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            ...
        }
    }
}

We can see that his waiting is outside the lock. When there is no message in the queue, he will release the lock first, and then wait until he is woken up. This will not cause deadlock problems.

Then when entering the queue, will it hold the lock while waiting for the message to be processed because the queue is full? The difference is that MessageQueue has no upper limit for messages, or its upper limit is the memory allocated by the JVM to the program. If the memory exceeds the limit, an exception will be thrown, but generally it will not.

4. How does Handler switch threads?

Use Looper of different threads to process messages.

The execution thread of the code is not determined by the code itself, but which thread the logic of executing this code is in, or which thread's logic is called. Each Looper runs in the corresponding thread, so the dispatchMessage method called by different Looper runs in the thread where it is located.

5. What is the blocking wakeup mechanism of Handler?

Answer: The blocking wake-up mechanism of Handler is based on the blocking wake-up mechanism of Linux.

This mechanism is also a pattern similar to the handler mechanism. Create a file descriptor locally, and then the party that needs to wait listens to this file descriptor, and the party that wakes up only needs to modify the file, then the party that is waiting will receive the file and break the wake-up. It is similar to Looper listening to MessageQueue and Handler adding messages.

6. Solve the Handler's memory leak method

The Handler writing method of the anonymous inner class, the inner class holds the outer class, which is the cause of the activity, causing a memory leak.

The most direct idea is to avoid using non-static inner classes. When using Handler, put it in a new file to inherit Handler or use a static inner class instead. Static inner classes do not implicitly hold references to outer classes, so there will be no memory leaks in this activity.

Two solutions are as follows:

  1. Weak reference (WeakReference)

  public class SampleActivity extends Activity {

  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   * 弱引用的方式
   */
    private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;
    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
         //to Something
      }
    }
 }

2. Use inner static class

  //定义成static的,因为静态内部类不会持有外部类的引用
  private final MyHandler mHandler = new MyHandler(this);
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() {//to something}
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
    finish();
  }
}

In fact, most of the memory leaks in android are caused by the use of non-static inner classes by Activity, so we should pay special attention when using inner classes. If the references it holds exceed the scope of the life cycle, it is very likely There will be a memory leak.

reference:

Comprehensive analysis of the Handler mechanism interview

Handler mechanism exploration and principle

Handler mechanism

Guess you like

Origin blog.csdn.net/qq_40587575/article/details/121283409