Looper is the manager of the entire cross-thread communication
    // Internal variables are as follows:
    ThreadLocal<Looper>
    MainLooper
    Observer
    MessageQueue
    Thread
Copy the code

1. Remember how to use Handler first

Communication between Android threads can be divided into the following two situations: 1. Child threads send messages to UI threads. 2. A child thread sends messages to another child thread

1. The child thread sends messages to the UI thread

class FragmentContentActivity : AppCompatActivity(a){
    val FLAG = 1
    lateinit var handler: Handler
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        handler = object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    FLAG -> {
                        findViewById<TextView>(R.id.text).text = msg.data["Text"].toString()
                    }
                }
            }
        }
        thread {
            Thread.sleep(2000L)
            handler.sendMessage(Message.obtain().apply {
                what = FLAG
                data = Bundle().apply {
                    this.putString("Text"."ThreadMessage")}})}}}Copy the code

2. The UI thread/child thread sends messages to the child thread

class FragmentContentActivity : AppCompatActivity(a){
    val THREAD_FLAG =2
    lateinit var threadHandler: Handler
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) thread { Looper.prepare() threadHandler = object :Handler(Looper.myLooper()!!) {override fun handleMessage(msg: Message) {
                    when(msg.what){
                        THREAD_FLAG -> {
                            Toast.makeText(
                                this@FragmentContentActivity."${msg.data["Text"]}",
                                Toast.LENGTH_SHORT
                            ).show()
                        }
                    }
                }
            }
            Looper.loop()
        }
    }

    override fun onResume(a) {
        super.onResume()
        findViewById<TextView>(R.id.text).postDelayed({
           threadHandler.sendMessage(Message.obtain().apply {
               what = THREAD_FLAG
               data = Bundle().apply {
                   putString("Text"."UI Message")}})},2000L)}}Copy the code

In the case of child threads, looper.prepare () and looper.loop () must be performed before and after looper.prepare (), so let’s look at the logic of Looper with this in mind


// Make sure you call loop() after prepare() and quit() to end the message loop
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));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
Copy the code

Prepare () initializes a Looper object into a ThreadLocal, initializes the Looper and queues the mQueue

    public static void loop(a){
    
    Binder.clearCallingIdentity()
for (;;) {
    Message msg = queue.next(); // might block
 
    long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
    try {
        // Loop () only makes this call. The rest of the call is to monitor whether the current message loop times out
        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 (slowDeliveryDetected) {
            if ((dispatchStart - msg.when) <= 10) {
                Slog.w(TAG, "Drained");
                slowDeliveryDetected = false; }}else {
            if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                    msg)) {
                // Once we write a slow delivery log, suppress until the queue drains.
                slowDeliveryDetected = true; }}}if (logSlowDispatch) {
        showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
    }
    // Message entity reclamation
    msg.recycleUnchecked();
Copy the code

You can see the stars. The loop is just in a for loop, get the next mQueue MSG node, and then call MSG. Target. DispatchMessage (MSG). At first glance, this is just an operation inside the MSG object.

Because loop() is logically dead-loop, this means that the spontaneous sequential execution of commands by the current thread ends, and that the current thread can only passively execute commands by triggering the handler mechanism of another thread. The current thread becomes a fully responsive thread

The Looper class is just a switch that initialates and starts the thread loop, which is done in MessageQueue

MessageQueue MessageQueue

The addition of messages to the queue is not called directly by MessageQueue, but by the Handler associated with Looper

MessageQueue internally holds the following variables: ArrayList

mMessages SparseArray

IdleHandler[] mBlocked

The main functions of MessageQueue class are as follows: insert elements into queues, obtain the head elements of queues, search for elements in queues, review is to add, delete, change and check queues, where mMessage is the entrance of this queue and the head node of this queue

  boolean enqueueMessage(Message msg,long when) // MSG elements are inserted into the queue
  boolean hasMessages(Handler h,int what,Object object) // find MSG. What /object with the same MSG
  boolean hasEqualMessages(Handler h,int what,Object obj)// find MSG. Object. Equal (obj)
  removeMessages(Handler h,int what,Object obj)/(Handler h,Runnable r,Object obj)
  removeEqualMessages(...). // Delete the MSG with the same or equal value as the msg.object argument
  
  Message next(a) // Get the header element in the queue
Copy the code

As you can see, synchronized(this) is called internally by these methods, and the queue operations are thread synchronized

Message next(a) ->
...

NextPollTimeOut is how long the bus is suspended
nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {

   final long now = SystemClock.uptimeMillis();
   Message prevMsg = null;
   Message msg = mMessages;
   // Check whether msg.target is null, indicating whether the current message is asynchronous
   if(msg ! =null && msg.target == null) {
   // Stalled by a barrier. Find the next asynchronous message in the queue.
   // Synchronization barrier: fetch asynchronous messages from the current 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.
           // Recalculate the time when the thread enters the suspended state
           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();
           returnmsg; }}else {
   // No more messages.
       nextPollTimeoutMillis = -1; }...Copy the code

You can see that there are two main types of logic inside next() to get MSG

1. The current messages are ordinary messages, sorted according to the size of msg.when. Each cycle is executed to determine whether to acquire MSG or suspend the current thread by detecting whether when is greater than now. Async (MSG. IsAsynchronous ()==true); async (MSG. IsAsynchronous ()==true);

In layman’s terms, all ordinary messages are queued according to the order of the scheduled execution time. In this way, messages can be executed according to the scheduled execution time and the maximum number of messages can be executed within a certain period with maximum efficiency. However, this ignores the time consumed by the execution of each message. For example, message A is the No.1 in the queue, the execution time of message A is 1s later, and the whole queue is in the waiting state. At this time, message B arrives, and the time of message B is 0.999s later. According to the queuing mechanism of reservation time, message B will jump the queue before message A, and THEN B becomes the No.1 in the queue. A became No.2, and the waiting time of the whole queue was still 1s(because the waiting time was set before, so there was No need to wake up). However, the execution process of B message was as long as 0.5s, which had already exceeded the scheduled execution time of many subsequent messages, so some important messages could not be executed on time.

Thus, there is the mechanism of asynchronous message synchronization barrier, which is equivalent to the ordinary message queue when a VIP message, first find their reservation time, and then shout: “Move your feet to me, no one is allowed in front of me”. At this time, all the ordinary messages in front of him can only be moved to the side of the queue, and then the queue will set the waiting time according to the VIP message. During this period, the new ordinary messages will also be inserted to the side of the queue to ensure the accurate and timely execution of the VIP message. Wait for VIP messages to finish, and then merge the queue waiting for ordinary messages. Of course, the news waiting for before all delayed, but after all, ordinary news is not important.

 // The synchronization barrier method, which is only called in the ViewRootImpl class
private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;
        // Target is not set
        Message prev = null;
        Message p = mMessages;
        if(when ! =0) {
            while(p ! =null&& p.when <= when) { prev = p; p = p.next; }}if(prev ! =null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
        //mMessages becomes synchronization barrier messages, and next() returns the synchronization barrier first in the next loop
            mMessages = msg;
        }
        return token;
    }
    // ViewRootImpl
 void scheduleTraversals(a) {
        if(! mTraversalScheduled) { mTraversalScheduled =true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if(! mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); }// After the synchronization barrier is set, all Msg messages sent by handlers with Aysnc flag bits are asynchronous messages.
//MessageQueue also prioritises such asynchronous messages until the synchronization barrier flag is removed and then the MessageQueue is restored.
Copy the code

The synchronization barrier must be set up to ensure that the Vsync signal is refreshed on time. SendMsg (1,time0.2)-> asynChandler. sendMsg(2,time0.5) -> 2->1.

Then look at Handler

The Handler member variables are as follows

MQueue: MessageQueue queue reference mAsynchronous obtained from Looper. MQueue: MessageQueue queue reference mAsynchronous obtained from Looper. MQueue: MessageQueue queue reference mAsynchronous obtained from Looper. MMessenger: IMessager indicates whether the Handler is currently sending asynchronous messages. MCallback: The Handler’s own callback interface, which precedes message. callback

Most of these member variables are final, which means that Handler is also final in its use, meaning that there is no way to avoid memory leaks by separating the Handler from the context’s lifecycle

The Handler method is as follows

    The first method is to set what, data for a Message
    // Do not set runnable: Callback
    boolean sendMessage(Message msg) -> boolean sendMessageDelayed(Message msg,long delayTime)
    -> boolean sendMessageAtTimeThe Message (MSG, SystemClock uptimeMillis ()+delayTime)
    -> mQueue.enqueueMessage(msg,uptime)
    // In the second way, Message sets only runnable:Callback
    boolean postAtTime(Runnable r, Object token,long uptime)
    -> sendMessageAtTime(getPostMessage(r,token),uptime)
    --> Message getPostMessage(Runnable r,Object token){
        Message.obtain().callback=r
        ...
        }
   // Remove Message and verify Message
   removeMessages() 
   hasMessages()
   ...
   // The Message callback executes
   void dispatchMessage(Message msg){
       if(msg.callback! =null){
           handleCallback(msg) ->
       }else{
           if(mCallback! =null){
            mCallback.handleMessage(msg)
           }
           handleMessage(msg)
   }
   // You can see that the Message callback has three levels
   //No.1 MSG callback
   //No.2 Handler's own mCallback member variable. MCallback is final
   // Subclasses of Handler override the handleMessage method
Copy the code
Message

Message implements the Parcelable interface, which means it can be used as a vehicle for interprocess communication

The Message member variables are as follows

    int what //Handler sends the Message code under the body Message
    int arg1 // Low cost parameter passing
    int arg2 
    Object obj // A token object that can be nullable, usually used in process communication
    Bundle data // A container of common parameters used in thread communication
    Handler target // Send the body
    Runnable callback //Message callback itself
    Messenger replyTo   // Process communication, commonly used in AMS
    ------
    // Message cache pool correlation
    Object sPoolSync = new Object() // Synchronize the flag
    Messsage next 
    static Message sPool
    static int sPoolSize
Copy the code

The Message method follows

    // You can see that this is a very clever method
    static Message obtain(a){
        synchronized(sPoolsSync){
            if(sPools! =null){
                Message m= sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                returnm; }}return new Message();
    }
    // The main body is a synchronous factory pattern with a linked list of cache pools, and also allows for more multithreaded blocking
    // You can declare the initializer directly
    
    // Reclaim the Message object to the cache pool list
    void recycleUnchecked(a){... Parameter =null
        synchronized(sPoolSync){
            if(sPoolSize < MAX_SIZE){
                next = sPool;
                sPools = this; sPoolSize++; }}}Copy the code

Other bits and pieces about the Handler

1. RunWithScissors () / / to be continued

2. What are the three types of messages

Ordinary Message Asynchronous Message: isAsynchronous(true) barrier flag Message: Target == NULL

3. IdleHandler / / to be continued

4. Why use nativePollOnce() /nativeWeak(mPtr)

The Blocking of the Linux message bus, which ensures that messages from other threads can be received at the native layer, can be handled internally by the JVM using Wait /notify

5. When does nativeWeak(mPtr) wake up the current thread

1. The current new MSG. When < mMessage. When | | MSG. The when = 0 | | MSG. The target = = null is the current message booking time efficiency of the head of a queue waiting time, or the current news is synchronous barrier marker, then need to wake up the queue 2. When removeSyncBarriar() is removed, the mQueue becomes a normal message queue and needs to be woken up

6. Can Handler be used for IPC communication?

Since Looper objects within handlers reside in ThreadLocal and can only be retrieved within the same process, they cannot communicate between processes, but Message should act as a container for interprocess communication, with a Messenger reference