All of my articles are collected in this article, and will be updated in time: Knowledge is long, the road of the walker will end in no words (My Programming Road)

preface

I swear, this is the last time I’ve analyzed Handler(this is the third time I’ve said this, hopefully it won’t be the next time)

Handler overview


1. Introduce the general routine of Handler

1. The usual Handler routine starts with an exception

Do you think I’m going to start by saying “Handler” by saying that the non-main thread is forbidden to update the UI? This exception is defined in the ViewRootImpl. Update the UI of the TextView. Why does the ViewRootImpl exception? The son does not teach, the father’s fault, teach not strict, the teacher’s laziness. Take a look at the source code of the Framework,

---->[ViewRootImpl#checkThread]-------
void checkThread() {
    if(mThread ! = Thread.currentThread()) { throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views."); }} | - visible when checkThread whether mThread is equal to the current thread, if is not equal to, were unusually | - what mThread thread? ---->[ViewRootImpl#ViewRootImpl]-------public ViewRootImpl(Context context, Display display) { mThread = Thread.currentThread(); | - assigned values in the constructor, that is, when creating ViewRootImpl threads | - where ViewRootImpl is created? There is no further talked about, is in the main thread | - from the exception is when making requestLayout method collapses - > [ViewRootImpl#requestLayout]-------
@Override
public void requestLayout() {
    if(! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested =true; scheduleTraversals(); }}Copy the code

2. How do we use Handler

Then find Handler good magic ah, as for where magic also can’t say a road, that is, mysterious Handler mask hidden a bit of a small complex message mechanism, this article will explain

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            msgTv.setText("hello"); }}; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v->{
            new Thread(new Runnable() {
                @Override
                public void run() { mHandler.sendEmptyMessage(0x01); } }).start(); }); }}Copy the code

3. Description of Handler in the source code

MessageQueue, message and runnables

| - Handler allows you to send and process associated with the thread MessageQueue messages and runnable object. Each Handler instance | - with a single thread and that thread's message queue. | - when you create a new Handler, it will be bound to the thread/message queue of the thread that is creating it, | - since then, it will be to give a message to the message queue and can be run, and from the message queue when they execute them. | - Handler has two main use: | - (1) the arrangement of the message and can be run at some point in the future to perform | - (2) will perform the operation on different threads to join the queue. | - scheduling messages through the method:  |--post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), |--sendEmptyMessage(int), sendMessage(Message),sendMessageAtTime(Message, long), |--sendMessageDelayed(Message, long).Copy the code

4. Main member variables

final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
Copy the code

5. Construction methods

Look at these seven calabash dolls. There are only two constructors in the core (three hide, not outside), and the other four can be used

| - with the specified callback interface of the current thread using stars, and sets the handler should be asynchronous. | - by default, the handler is synchronous, unless you use this constructor to create a strict asynchronous handler. | - (inserted words, this method is hidden, that outside the caller is unable to create the asynchronous handler) | - an asynchronous message said: don't need to global synchronous message sorting interrupt or events. | - an asynchronous message from MessageQueue#enqueueSyncBarrier(long) limits the synchronization barrier introduced.* @param callback Callback interface that handles messages, or null. * @param async if it istrue, the handler will call * Message for every Message or Runnable sent to it#setAsynchronous(boolean)
---->[Handler#Handler(Callback,boolean)]-------------------------------
public Handler(Callback callback, boolean async) {
    if(FIND_POTENTIAL_LEAKS) {//final constant --false, so regardless of his 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()); } } mLooper = Looper.myLooper(); // Assign a value to mLooper via the myLooper method of Looperif(mLooper == null) {// throw new RuntimeException(mLooper == null)"Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; //mQueue gets mLooper = callback; // mAsynchronous = async; / / into the ginseng} | -- not seem to do anything, just mLooper, mQueue, mCallback, mAsynchronous assignment | - focus on stars. MyLooper () - > [Handler#Handler(Callback,boolean)]-------------------------------public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } | - three bosses very capricious, direct assignment, so the structure of the Handler function nothing too special | - look at the stars below classCopy the code

Two, faithful :Looper

1:ThreadLocal

I spent some time looking at ThreadLocal, has been written separately, details see: Java point general platform: multi-image clone [-threadLocal -] Here not to discuss ThreadLocal, to give a small example, see its function

Public class ThreadLocalTest {static final ThreadLocal<Integer> sThreadLocal = new ThreadLocal<>(); Static int count = 10; public static void main(String[] args) throws InterruptedException { sThreadLocal.set(count); Thread thread = newThread() {
            @Override
            public void run() {// create thread count++; sThreadLocal.set(count); System.out.println("new :"+ sThreadLocal.get()); }}; thread.start(); thread.join(); // In order to avoid ambiguity, the new thread joins and the main thread prints after the new thread executes system.out.println ("main:"+sThreadLocal.get()); }}Copy the code

Sthreadlocal.set () does not affect the main thread’s get() in the new thread, so the shared member variable count is placed in the sThreadLocal basket to ensure that the shared variable is independent between threads


2. Which class (Stars: / 'lu ː p goes/cycle)
[] Package name: Android.os Number of dependent classes :7 Number of internal classes/interfaces: 0 Number of source lines: 344 number of source lines (except for comments):158 Number of properties: 8 Number of methods :20 Public method number :18Copy the code

---->[Looper# member variable]------------Static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); SMainLooper private static Looper sMainLooper; //MessageQueue final MessageQueue mQueue; // Final Thread mThread; ---->[Looper#Looper]------------private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } | - in which the constructor, respectively for mQueue, initialized mThread | - pay attention to the constructor is private, so can't directly which object constructionCopy the code

So there must be a new Looper in this class.

// Public static methods: prepare ---->[Looper#prepare]------------
public static void prepare() {
    prepare(true); } // Private static methods: prepare ---->[Looper#prepare(boolean)]------------
private static void prepare(boolean quitAllowed) {
    if(sThreadLocal.get() ! = null) {// if a thread can only create a Looper throw new RuntimeException("Only one Looper may be created per thread"); } // Create a Looper object with sThreadLocal as the key; Sthreadlocal. set(new Looper(quitAllowed)); // Set sthreadLocal. set(new Looper(quitAllowed)) to the current thread's threadLocals(ThreadLocalMap object); } ---->[Looper#prepare(boolean)]------------
 public static @Nullable Looper myLooper() {
        returnsThreadLocal.get(); } | - which will be in for a ThreadLocal independent in each thread, the thread | - generated stars-sThreadlocal.get () will get null, but where is prepare() initialized at the framework level?Copy the code

3. Initialization of Looper in ActivityThread

Looking at the source code for the Framework layer, the program entry main method is in ActivityThread

---->[ActivityThread# member variable]---------
final Looper mLooper = Looper.myLooper();

---->[ActivityThread#main]---------Public static void main(String[] args) { Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false);
    if (sMainThreadHandler == null) {
        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(); // Start loop throw new RuntimeException("Main thread loop unexpectedly exited");
    
---->[Looper#prepareMainLooper]---------
public static void prepareMainLooper() {// here we call the prepare method, set Looper for sThreadLocal, and call it from the main thread.false);
    synchronized (Looper.class) {
        if(sMainLooper ! = null) {// If you are prepared, throw new IllegalStateException();"The main Looper has already been prepared."); } sMainLooper = myLooper(); // Initialize the member variable sMainLooper}}Copy the code

Looper.mylooper () ¶ Looper.myLooper(); looper.prepare (); looper.prepare (); looper.prepare (); So looper.myLooper (), that is, sthreadLocal.get (), cannot get Looper in the child thread. This is what ThreadLocal does


Three, look at Handler

1. Use the constructor with Callback

We can completely build the Handler by passing back calls, and with Java8’s simplified writing, it looks a lot better, and it works fine

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler ;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(msg -> {
            msgTv.setText("hello");
            return true;
        });
        setContentView(R.layout.ac_handler); msgTv = findViewById(R.id.id_tv_handler); msgTv.setOnClickListener(v->{ Thread thread = new Thread(() -> mHandler.sendEmptyMessage(0x01)); thread.start(); }); }}Copy the code

2. Take a look at the CallBack interface
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. You can use the callback interface when you instantiate the Handler to avoid having to implement your own processing Handler subclass. */ ---->[Handler$Callback]----------------------
public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return*/ public Boolean handleMessage(Message MSG); } /** * Handle system messages here. */ ---->[Handler#dispatchMessage]----------------------
public void dispatchMessage(Message msg) {
    if(msg.callback ! = null) { handleCallback(msg); }else {
        if(mCallback ! = null) {// if mCallback is not empty // call back McAllback.handlemessage (MSG), returntrueAnd then after thatreturn// What's the use? If you override the Handler's handleMessage and have both Callback and handleMessagetrueThe Handler's handleMessage method is no longer executed, as shown in the following exampleif (mCallback.handleMessage(msg)) {
                return; } } handleMessage(msg); }}Copy the code

3. Override handleMessage with Callback

Return true; return true; So hello,return false; The result is hello2

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(msg -> {
            msgTv.setText("hello");
            return true;
        }) {
            @Override
            public void handleMessage(Message msg) {
                msgTv.setText("hello2"); }};setContentView(R.layout.ac_handler); msgTv = findViewById(R.id.id_tv_handler); msgTv.setOnClickListener(v -> { Thread thread = new Thread(() -> { mHandler.sendEmptyMessage(0x01); }); thread.start(); }); }}Copy the code

4. Is it useful to create Handler objects in child threads?

Now we will create the Handler on the child thread, and as expected, it will crash

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        setContentView(R.layout.ac_handler); msgTv = findViewById(R.id.id_tv_handler); msgTv.setOnClickListener(v -> { Thread thread = new Thread(() -> { mHandler = new Handler(callback); / / < - create a Handler mHandler. SendEmptyMessage (0 x01); }); thread.start(); }); }} ----> ---------------- ---->[Handler#Handler(Callback, boolean)]----------------Public Handler(Callback Callback, Boolean async) {// Omit... mLooper = Looper.myLooper();if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } | - we have been debug, in front of the stars in the child thread. MyLooper () is empty, so will crashCopy the code

5. How to create a Handler in a child thread

In addition, the Context also provides us with a method to obtain Looper from the main thread. Debug can see that the two are the same object

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true; }; Looper looper = Looper.myLooper(); // Get looper for main threadsetContentView(R.layout.ac_handler); msgTv = findViewById(R.id.id_tv_handler); msgTv.setOnClickListener(v -> { Thread thread = new Thread(() -> { mHandler = new Handler(looper,callback); mHandler.sendEmptyMessage(0x01); }); thread.start(); }); }}Copy the code

Iv. Message queue (difficulty, core)

1. Back to the simplest message sending
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        mHandler = new Handler(callback);

        setContentView(R.layout.ac_handler); msgTv = findViewById(R.id.id_tv_handler); msgTv.setOnClickListener(v -> { Thread thread = new Thread(() -> { mHandler.sendEmptyMessage(0x01); }); thread.start(); }); }}Copy the code

2. Go to the Handler source code

A series of sendMessageXXX calls enqueueMessage(MessageQueue queue,Message MSG, long uptimeMillis)

sendEmptyMessage(int what) : Send an empty message, sendEmptyMessageDelayed(int what, long delayMillis), Send delayed empty Message sendMessageDelayed(Message MSG, Long delayMillis) Send delayed Message sendMessageAtTime(Message MSG, EnqueueMessage (MessageQueue Queue, Message MSG, Long uptimeMillis) Indicates that a Message is queuedCopy the code

---->[Handler#sendEmptyMessage]-----------------| - only the message what general message (table), call: sendEmptyMessageDelayed public final Boolean sendEmptyMessage (int I) {return sendEmptyMessageDelayed(what, 0);
}

---->[Handler#sendEmptyMessageDelayed]-----------------| - only the message what general message (table), the delay in milliseconds, call: sendMessageDelayed public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what;return sendMessageDelayed(msg, delayMillis);
}

---->[Handler#sendMessageDelayed]-----------------| - receives a Message object, delay milliseconds, call sendMessageAtTime public final Boolean sendMessageDelayed (Message MSG, long delayMillis) {if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

---->[Handler#sendMessageAtTime]-----------------| - receives a message object, delay milliseconds, Public Boolean sendMessageAtTime(Message MSG, long uptimeMillis) {MessageQueue queue = mQueue; // Get the message queueif(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);
}

---->[Handler#enqueueMessage]-----------------private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; // Note that the current Handler is used as the target of the messageif (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    returnqueue.enqueueMessage(msg, uptimeMillis); // MessageQueue enqueueMessage is called}Copy the code

We now have two classes: Message and MessageQueue


3. Look at the Message class first

Message has a lot of member variables

What ----int, used to read the information arg1----int, stored simple int arg2----int, stored simple int obj -- any type, Store any data optional Messenger of the replyTo ---- Messenger type, where a replyTo this message can be sent. Exactly how you use its semantics depends on the sender and receiver.Copy the code

Let’s start with an example to introduce Message


3.1:new Message()withMessage.obtain()withHandler.obtain()

So all three of them work, so what’s the difference?

/** * Author: Zhang Feng Jiete Lie <br/> * Time: 2019/1/25/025:14:24<br/> * Email: [email protected]<br/> * Description: Public class HandlerActivity extends AppCompatActivity {private TextView msgTv; private Handler mHandler; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Handler.Callback callback = msg -> { String txt = (String) msg.obj; switch (msg.what) {case 0x01:
                    txt += "---- # 1";
                    break;
                case 0x02:
                    txt += "---- Article 2";
                    break;
                case 0x03:
                    txt += "---- Article 3";
                    break;
            }
            msgTv.setText(txt);
            return true;
        };
        mHandler = new Handler(callback);
        setContentView(R.layout.ac_handler); msgTv = findViewById(R.id.id_tv_handler); Msgtv.setonclicklistener (v -> {Thread Thread = new Thread(() -> {//new Message() Message newMsg = new Message(); newMsg.what = 0x01; newMsg.obj ="The Dragon of the Moon"; mHandler.sendMessage(newMsg); Message msgObtain = Message. Obtain (); msgObtain.what = 0x02; msgObtain.obj ="Zhang Feng Jie Te Li"; mHandler.sendMessageDelayed(msgObtain, 3000); // mhandler.obtainMessage () Create Message Message handlerObtain = mhandler.obtainMessage (); handlerObtain.what = 0x03; handlerObtain.obj ="Thousands of miles of silks."; mHandler.sendMessageDelayed(handlerObtain, 6000); }); thread.start(); }); }}Copy the code

3.2: Differences among the three
2.---->[Message]------------------ private static final Object sPoolSync = new Object(); Private static Message sPool; Private static int sPoolSize = 0; private static int sPoolSize = 0; Private static final int MAX_POOL_SIZE = 50; private static final int MAX_POOL_SIZE = 50; // Maximum size of the Message pool ---->[Message#obtain]------------------
public static Message obtain() {synchronized (sPoolSync) {// synchronizedif(sPool ! = null) {Message m = sPool if the Message pool is not empty; // assign the sPool object to the m object sPool = m.ext; // next is assigned to the sPool object m.ext = null; M.f lags = 0; // lags = 0; // clearin-use flag sPoolSize--; // The size of the message pool is -1returnm; // Return the message}}returnnew Message(); / / if the news when the pool is empty, returns the new Message object} | - obviously used singly linked lists, here will be a Message from the Message out in the pool. | - needless to say, the benefits of the maintenance Message pool to avoid frequent created and destroyed the Message, | - such as frequently send messages (round), every time switch issued a Message, using the Message pool can become better 3. -- -- -- - > [Handler#obtainMessage]------------------
public final Message obtainMessage() {return Message.obtain(this);
}

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;
    returnm; } | - to get the Message will be set as the target after the current handler, this step has enqueueMessage, | so both - in addition to the Message there is a lot of overloaded ` Message obtain `, are based on Message, obtain, | - at the same time to set some properties for its, essentially nothing too big difference, oneself have a look at the lineCopy the code

MessageQueue MessageQueue

Message has no method to add messages to the Message pool, so how is the Message pool implemented? How is the message added to the message queue from Handler#enqueueMessage

---->[Handler#enqueueMessage]------------------------private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; // Set the target of the current message to this Handlerif (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

---->[MessageQueue#enqueueMessage]------------------------
boolean enqueueMessage(Message msg, long when) {
    if(MSG. Target == null) {// if the target is null, throw new IllegalArgumentException("Message must have a target.");
    }
    if(MSG. IsInUse ()) {// The message is already in use, throw new IllegalStateException(MSG +)" This message is already in use."); } synchronized (this) {// synchronizedif (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; // Assign mMessages to p Boolean needWake;if(p = = null | | the when = = 0 | | the when < p.w hen) {/ / / / p empty new queue head, if blocked, awaken the event queue MSG. The next = p; MMessages = MSG; NeedWake = mBlocked; }elseNeedWake = mBlocked && p.target == null && msg.isasynchronous (); Message prev; // Declare the previous messagefor(;;) {/ / equivalentwhile(truePrev = p; // assign the original prev element to the prev variable p = p.ext; // Assign the next message from P to pif (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false; } } msg.next = p; Next = MSG; // Redirect the current message to p prev.next = MSG; } // We can assume mPtr! = 0 because mQuitting is false.if(needWake) { nativeWake(mPtr); }}return true; } > < span style = "box-sizing: border-box; color: RGB (74, 74, 74)Copy the code

MSG: Message p = mMessages p: Message prev; MSG: Message p = mMessages P: Message prev // Declare the previous messagefor(;;) {/ / equivalentwhile(truePrev = p; //prev: p = p.next; P.ext =null, p=null after executionif (p == null || when < p.when) {
        break;
    }
    if (needWake && p.isAsynchronous()) {
        needWake = false; } } msg.next = p; Null prev. Next = MSG; // MSG: Message p = mMessages p: Message prev; // MSG: Message prev; // Declare the previous messagefor(;;) {/ / equivalentwhile(true// The first cycle --prev: yumianyi Xinglong // The second cycle --prev: Zhang Feng Jieteli Prev = p; // Second loop -- p.ext =null, p=null p= p.ext; // Second loop --p =null p= p.ext;if (p == null || when < p.when) {
        break;
    }
    if (needWake && p.isAsynchronous()) {
        needWake = false; } } msg.next = p; Next = MSG; // prev = prev; / / jade face game star dragon - > packer jet fierce - > thyme WuYing - > null | - thus, each add a message is added to the partyCopy the code

Looper’s loop method

How is the message retrieved after it has been queued? How to trigger the Handler’s dispatchMessage callback method? Here omits some of log statements, visible loop method always will call queue. The next () until the MSG = = null set out after get news MSG. Target. DispatchMessage (MSG); But when there is no message messagequue #next() will block, causing the loop to block. So next cannot stop the loop to close the loop using Messagequue #quit.

---->[Looper#loop]---------
public static void loop() {// final Looper me = myLooper();if(me == null) {// If it is null, an exception is reported. Throw new RuntimeException(Looper object)"No Looper; Looper.prepare() wasn't called on this thread."} final MessageQueue queue = me. MQueue; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();for(;;) {/ / equivalentwhile(trueMessage MSG = queue.next(); / / messageif (msg == null) {
            return; } / / slightly... final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; / / a little... final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; Try {/ / call the MSG here. The target is the handler method dispatchMessage MSG. Target. DispatchMessage (MSG); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally {if(traceTag ! = 0) { Trace.traceEnd(traceTag); }} msg.recycleUnchecked(); } } ---->[MessageQueue#next]---------------
Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; 
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if(nextPollTimeoutMillis ! = 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); Synchronized (this) {// attempts to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; MSG = mMessages; // MSG is the team leaderif(msg ! = null && MSG. Target == null) {// The target is blocked by an obstacle. Look for the next asynchronous message in the queuedo {
                    prevMsg = msg;
                    msg = msg.next;
                } while(msg ! = null && ! msg.isAsynchronous()); }if(msg ! = null) {if(now < MSG. When) {// The next message is not ready. Set timeout to wake up nextPollTimeoutMillis = (int) math.min (MSG. When - now, Integ}else{// Get a MSG ------- 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; }}elseNextPollTimeoutMillis = -1; } // Now that all pending messages are complete, please process the exit messageif (mQuitting) {
                dispose();
                return null;
            }
   
            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(pendingIdleHandl } mPendingIdleHandlers  = mIdleHandlers.toArray(mPendingIdleHandlers); }for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the hand
            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 wedo not run them again.
        pendingIdleHandlerCount = 0;
        // While calling an idle handler, a new message could have been delivere
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

---->[Handler#dispatchMessage]---------------
public void dispatchMessage(Message msg) {
    if(msg.callback ! = null) {// If MSG has a callback handleCallback(MSG); // Handle the MSG callback}else {
        if(mCallback ! = null) {//if (mCallback.handleMessage(msg)) {
                return; } } handleMessage(msg); // Callback to the override handleMessage method}} ---->[Handler#handleCallback]---------------
private static void handleCallback(Message message) {
    message.callback.run();
}
Copy the code

As for the messagequue # Next method, it is nothing more than to let the message out of the queue, and next will block all the time when there is no message. Android MessageQueue message loop processing mechanism is recommended here


Five,Handler#postXXXWith the Messagecallback

1. Analyze the dispatchMessage method

I don’t know if you’ve noticed that there are some callbacks in MSG, but to emphasize dispatchMessage, let’s look again

---->[Handler#dispatchMessage]---------------
public void dispatchMessage(Message msg) {
    if(msg.callback ! = null) {// If MSG has a callback handleCallback(MSG); // Handle the MSG callback}else {
        if(mCallback ! = null) {//if (mCallback.handleMessage(msg)) {
                return; } } handleMessage(msg); // Callback to the override handleMessage method}} ---->[Handler#handleCallback]---------------
private static void handleCallback(Message message) {
    message.callback.run();
}
Copy the code

2. MSG can add a Runnable callback

Unfortunately, the callback is package access level, and there is no set method provided, so an override of obtain is not available but is provided to place the callback

---->[Message#obtain(Handler, Runnable)]
public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback;
    returnm; } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - / * * * the author: packer fierce < br / > * time: jet 2019/1/25/025: save < br / > * email address: [email protected]<br/> * When MSG itself have a Runnable callback * / public class HandlerMsgWithCbkActivity extends AppCompatActivity {private TextView msgTv; private Handler mHandler; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Handler.Callback callback = msg -> { msgTv.setText("Callback handleMessage");
            return true;
        };

        mHandler = new Handler(callback){
            @Override
            public void handleMessage(Message msg) {
                msgTv.setText("Overwritten handleMessage"); }};setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);

        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {

                Message msgObtain = Message.obtain(mHandler, new Runnable() {
                    @Override
                    public void run() {
                        msgTv.setText("Message + Runnable"); }}); msgObtain.what = 0x02; msgObtain.obj ="Zhang Feng Jie Te Li"; mHandler.sendMessageDelayed(msgObtain, 3000); }); thread.start(); }); }} | - run results as follows, in combination with analysis diagram dispatchMessage, should be enough to illustrateCopy the code


3. Several postXXX methods for Handler

Boolean Post (Runnable r) Post a Runnable Boolean postAtTime(Runnable r, Long uptimeMillis Boolean postAtTime(Runnable r, Object token, Long uptimeMillis + token Boolean postDelayed(Runnable r, Long delayMillis) demo Boolean postAtFrontOfQueue(Runnable r) Post to the first queue ---->[Handler#post]-----------------
public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}

---->[Handler#getPostMessage]-----------------
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    returnm; } | -- not visible post on how tall, just call the sendMessageDelayed | - the Message through getPostMessage access, In the getPostMessage | - through Message. Obtain () to retrieve a Message from the Message pool, and add the callback | - behind several methods about the same, just give the Message to add more information, understand the Message, Won't be a problem |, the several methods of Handler encapsulates the to us, to complete the above tests, you can: ---->[HandlerMsgWithCbkActivity]----------------- msgTv.setOnClickListener(v -> { Thread thread = new Thread(() -> { mHandler.postDelayed(newRunnable() {
            @Override
            public void run() {
                msgTv.setText("Message + Runnable"); }}, 3000); }); thread.start(); }); | - note that use postXXX, can let a Handler. The callback and overwrite the handleMessage failure | - because its essence is to pass the Message of the callback, Once you've already talked about the Message. The callback exist | -- is no longer on both.Copy the code

Handler:

Let’s look back at the message mechanism behind Handler:

Message bearer unit: Message manager: MessageQueue Message mechanism driver: Looper Message Handler: Handler ----> This story is purely fictional ------------- legend in 10 billion years ago, the universe is more than one, people live in the universe is called the main universe in addition to the main universe, called the sub-universe, because the universe is numerous, is called three thousand universe some people try to change their appearance in the sub-universe (including clothes), start a new life, However, there are many people in the main universe, and resources are relatively scarce. Many people have to go to the sub-universe to get resources. A girl named TextView wants a beautiful dress, and Handler, as her boyfriend, is willing to go into the sub-universe alone. He popped out three very beautiful pieces of clothing, knowing that if TextView came directly to the subuniverse and put them on, she would immediately go up in smoke. So he put three pieces of clothing in three space cubes (Message), And identifies the target of the message as itself. Then the three cubes are placed into the MessageQueue in turn. The powerful Looper energy in the main universe drives the Parallel Universe Transmission Station, which has been running since the moment the universe was born (looper.loop). According to target, it finds its owner's handleMessage and then puts three beautiful clothes on TextView in turn, so that it can transmit the resources of the subuniverse to the universe. Some people who work in the subuniverse all the year round also use this mechanism. They send a letter home, And these were their daily lives at that time...Copy the code
One final question: can Handler just pass resources between the main and subuniverses?

Each subuniverse can have its own Looper, and subuniverses can communicate with each other through handlers

/** * Author: Zhang Feng Jiete Lie <br/> * Time: 2019/1/25/025:14:24<br/> * Email: [email protected]<br/> * Description: Public class HandlerOtherActivity extends AppCompatActivity {private TextView msgTv; private Handler mHandler; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            new Thread("First child universe") {// first child universe @override public voidrun() {
                    Message newMsg = new Message();
                    newMsg.what = 0x01;
                    newMsg.obj = "The Dragon of the Moon";
                    mHandler.sendMessage(newMsg);
                    Log.e("HandlerOtherActivity"."Current thread name:" + Thread.currentThread().getName() + "Send:" + ne
                }
            }.start();
        });
        new Thread("Second child universe") {// Second child universe @override public voidrun() { Looper.prepare(); MHandler = new Handler(MSG -> {log.e ();"HandlerOtherActivity"."Current thread name:" + Thread.currentThread().getName() + "Receive:" + ms
                    return false;
                });
                Log.e("HandlerOtherActivity"."run: "); Looper.loop(); //looper starts -- this thread will block the log.e (------) method."HandlerOtherActivity"."run:-------- "); } }.start(); }} | - note: when the first thread to transfer data to the second thread, mHandler will have the second thread which | - that is, let the Handler in the second thread is created, below is very clear to express: | - the first line cheng passed a message to the second thread, and the second thread will be blocked in the stars. The loop (), next is unable to printCopy the code


To give you a better idea of what a Looper is, let’s write it another way

The handler is created on thread 1 using thread 2’s looper. The handler is sent on thread 1. The message is still executed on thread 2. Which thread will the message be sent to?)

/** * Author: Zhang Feng Jiete Lie <br/> * Time: 2019/1/25/025:14:24<br/> * Email: [email protected]<br/> * Description: Public class HandlerOtherActivity extends AppCompatActivity {private TextView msgTv; private Handler mHandler; private Looper mOtherLooper; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);

        msgTv.setOnClickListener(v -> {

            new Thread("First child universe") {// first child universe @override public voidrun() {
                    Message newMsg = new Message();
                    newMsg.what = 0x01;
                    newMsg.obj = "The Dragon of the Moon";
                    mHandler.sendMessage(newMsg);
                    Log.e("HandlerOtherActivity"."Current thread name:" + Thread.currentThread().getName() + "Send:" + newMsg.obj);
                }
            }.start();
        });

        new Thread("Second child universe") {// Second child universe @override public voidrun() { Looper.prepare(); // Let the current subuniverse form --looper energy mOtherLooper = looper.mylooper (); Log.e("HandlerOtherActivity"."run: "); Looper.loop(); //looper starts -- this thread will block the log.e (------) method."HandlerOtherActivity"."run:-------- "); } }.start(); try { Thread.sleep(10); MHandler = new Handler(mOtherLooper, MSG -> {log.e (mOtherLooper, MSG -> {log.e (mOtherLooper, MSG -> {Log."HandlerOtherActivity"."Current thread name:" + Thread.currentThread().getName() + "Receive:" + msg.obj);
                return false; }); } catch (InterruptedException e) { e.printStackTrace(); }}}Copy the code