A, definitions,

  • An Android messaging/asynchronous communication mechanism.


Second, the role of

  • In multi-threaded scenarios, sub-threads need to pass UI update operation information to the main thread to realize asynchronous message processing.


Three, use,

There are two ways to use it:

  • One is throughsendMessage()To achieve asynchronous communication.
  • One is throughmHandler.post()To achieve asynchronous communication.


3.1 sendMessage()way

1. CreateHandlerObject, listed below3Kind of way.
  • Anonymous inner class
// Create a Handler class object from an anonymous inner class in the main thread.
private Handler mHandler = new Handler(){
// Update the UI by overriding the handlerMessage method
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        / /... Perform the UI update operation}};Copy the code
  • implementationCallbackinterface
// The main thread implements the Callback interface to create a handler class object.
private Handler mHandler  = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false; }});Copy the code
  • Created in a child threadHandlerClass object (rare, rarely used)
 new Thread(new Runnable() {
            @Override
            public void run(a) {
                Looper.prepare();
                Handler mHandler = new Handler();
                Looper.loop();
            }
        }).start();
Copy the code


2. To createMessageObject, at the same timehandlerSend a message.
  1. createMessageobject
// Method 1:
Message message1 = new Message();
// Method 2:
Message message2 = Message.obtain();
// Method 3:
Message message3 = mHandler.obtainMessage();
Copy the code
  1. MessageObject carrying data
  Message message = new Message();
  message.what = 1; // Identifies the message
  
  Arg1, arg2
  message.obj = "La la la la"; // object
  message.arg1 = 1; / / int type
  message.arg2 = 2; / / int type
  
  // Method 2: Transfer by Bundle
  Bundle bundle = new Bundle();
  bundle.putInt("1".1);
  message.setData(bundle);
Copy the code
  1. handlerSend a message
// Method 1: Send a common message
mHandler.sendMessage(message);

// Method 2: Send an empty message and pass in the what value for easy judgment in the received message.
mHandler.sendEmptyMessage(0);

// Mode 3: Delay sending messages
mHandler.sendEmptyMessageDelayed(0.1000);
Copy the code


3. handlerReceive the message and process it.
private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        // Determine different messages according to the what identifier
        switch (msg.what) {
            case 0:
                System.out.println("Received message is empty message");
                break;
            case 1:
                System.out.println(msg.obj.toString());
                break;
            default:
                break; }}};Copy the code


3.2 mHandler.post()way

  • 1. CreateHandlerObject.
  • 2. In the child thread by calling the instance objectpost()Method, passed inrunnableObject, overriderun()Method in rewriterun()Update in methodUI.
private Handler mHandler = new Handler();
new Thread(new Runnable() {
            @Override
            public void run(a) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run(a) {
                       // Update UI operations}}); }});Copy the code





Ok, about the basic use of Handler is said, the following through a problem and the corresponding problem of the source code to deepen our understanding of Handler, do know what it is.





Four, common questions and source solutions

1. Create the main threadHandlerCreate with child threadHandlerWhat’s the difference?

Create Handler for main thread:

private Handler mHandler  = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false; }});Copy the code

Child thread create Handler:

 new Thread(new Runnable() {
            @Override
            public void run(a) {
                Looper.prepare();
                Handler mHandler = new Handler();
                Looper.loop();
            }
        }).start();
Copy the code

By comparison, we can see that there are two more lines of code to create the Handler on the child thread, so why not on the main thread? Let’s use the source code to explain:

At startup, the application calls the ActivityThread class. In the main() method of this class, looper.prepareMainLooper () is called:

// The main method in ActivityThread
 public static void main(String[] args) {
        / /...
        // Create Looper for the main thread
        Looper.prepareMainLooper();
        / /...
        Looper.loop();
    }
Copy the code

Looper.prepareMainLooper();

  • The implication of this line of code is to initialize one for the current threadLooper, so it’s not that the main thread doesn’t need to be calledLooper.prepare()Method, but the system has already done something.

Looper.loop();

  • What this line of code means is that it turns on polling for the main thread, which is an infinite loop that keeps polling.

At this point, some of you might be wondering, what if the system automatically added these two lines of code to the main thread when it created the Handler? What if we didn’t add these two lines to the child thread when it created the Handler? The answer is no, no, exceptions will be reported:

 throw new RuntimeException("Only one Looper may be created per thread");
Copy the code

And that’s when we think, why does the system throw an exception? We still need to find the answer from the source code. We need to look at the source code for new Handler().

 public Handler(@Nullable Callback callback, boolean async) {
     
       / /...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
       //....
    }
Copy the code

We can see from the source that the member variable mLooper is assigned. If it does not get the value, it will throw the exception we mentioned earlier, so we need to see what looper.mylooper () does.

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 
 / /...
 public static @Nullable Looper myLooper(a) {
      return sThreadLocal.get();
  }
Copy the code

The return value of this method is the Looper object, and the return value of this method is the Looper object.

public T get(a) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if(e ! =null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                returnresult; }}return setInitialValue();
    }
Copy the code

ThreadLocalMap (); ThreadLocalMap (); ThreadLocalMap (); Return to the main() method of ActivityThread and click prepareMainLooper() of Looper.prepareMainLooper() :

public static void prepareMainLooper(a) {
        prepare(false);
        synchronized (Looper.class) {
            if(sMainLooper ! =null) {
                throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}Copy the code

Click the prepare() method in this method again:

 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));
    }
Copy the code

Here is the sthreadlocal.set () method:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
    }
Copy the code

If we look at these two methods side by side, we can see that when the application starts, a unique piece of data is created in the ThreadLocalMap. The key value is the main thread, and the value is the main thread’s Looper object. When we create the Handler in the child thread, The value of the key passed in is the child thread. Of course, we can’t get the Looper object from ThreadLocalMap because the value of the key is different. So the Looper object is null, which throws the above exception.


From this question, the following conclusions can be made:

  • Child threads do not exist by defaultLooperIf neededHandlerMust be created for the threadLooper.
  • There must be one in a threadLooperThat is, in a thread, if you want to useHandlerYou have to have oneLooper. Want to create properly in child threadshandlerObject as created abovehandlerThe first object3Kind of method.
  • HandlerThe current thread will be used for creationLooperTo construct the message loop system, because when the application starts,ActivityThreadThe class is initializedLooper, so it can be used by default in the main threadhandler, but you cannot say that the main thread is not createdLooperObject.


2. Does updating control content really only run in the main thread?

First give the conclusion, this sentence is too absolute, not necessarily. Textview.settext () : textView.settext () : textView.settext () : textView.settext () :

Click through the setText() method to find the checkForRelayout() method in the last setText method. Click on this method to get to the bottom of the code. You can see that either the if code or the else code will end up with the requestLayout() and invalidate() lines. Then let’s see what’s going on in requestLayout().

public void requestLayout(a) {
        if(mMeasureCache ! =null) mMeasureCache.clear();

        if(mAttachInfo ! =null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
          
            ------ ViewRootImpl implements the mParent interface --------
            ViewRootImpl viewRoot = getViewRootImpl();
            if(viewRoot ! =null && viewRoot.isInLayout()) {
                if(! viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }

        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;

        if(mParent ! =null && !mParent.isLayoutRequested()) {
            // ---------mParent is an interface ---------
            mParent.requestLayout();
        }
        if(mAttachInfo ! =null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null; }}Copy the code

ViewRootImpl implements the mParent interface, which calls requestLayout() in ViewRootImpl.

@Override
    public void requestLayout(a) {
        if(! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested =true; scheduleTraversals(); }}Copy the code

There is a method to check the thread: checkThread()

void checkThread(a) {
        if(mThread ! = Thread.currentThread()) {throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views."); }}Copy the code

This code checks if the current thread is the main thread and raises an exception if it is not.

If we update the UI in a child thread, we will not raise an exception if the requestLayout() checking thread is slower than the invalidate() drawing interface.

To verify this, delay the child thread by one second to update the UI.

 Process: com.example.handlerdemo, PID: 10569
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7753)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1225)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:360)
        at android.view.View.requestLayout(View.java:23093)
        at android.widget.TextView.checkForRelayout(TextView.java:8908)
        at android.widget.TextView.setText(TextView.java:5730)
        at android.widget.TextView.setText(TextView.java:5571)
        at android.widget.TextView.setText(TextView.java:5528)
        at com.example.handlerdemo.MainActivity$3.run(MainActivity.java:75)
        at java.lang.Thread.run(Thread.java:764)
Copy the code

Can see out pointing error is reading the source code we see ViewRootImpl. RequestLayout this check threads.

UI updates can only be performed on the main thread.


3. Create the main threadHandlerWhat’s the difference between the two?

A:

 private Handler mHandler1 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false; }});Copy the code

Method 2:

    private Handler mHandler2 = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg); mTextView.setText(msg.obj.toString()); }};Copy the code

Those of you who have used it know that method two has a yellow warning, which is Google’s backup API and not recommended.

From the previous analysis, we know that the main() method of AcitivityThread creates a Looper object and calls looper.loop () to loop through the message queue.

/ / which class

 for (;;) {
 //....
 try {
                msg.target.dispatchMessage(msg);
                if(observer ! =null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; }}/ /...
Copy the code

In this loop, the dispatchMessage() method of MSG. Target is called to distribute the message. What is MSG. MSG is a Message object. If we go to the source code of the Message class, we can see the following code:

/ / the Message class

@UnsupportedAppUsage
/*package*/ Handler target;
Copy the code

MSG. Target is the Handler object, so Handler’s dispatchMessage() method is called. So let’s look at the dispatchMessage() method in the Handler class:

/ / Handler class

public void dispatchMessage(@NonNull Message msg) {
        if(msg.callback ! =null) {
            handleCallback(msg);
        } else {
            if(mCallback ! =null) {
                if (mCallback.handleMessage(msg)) {
                    return; } } handleMessage(msg); }}Copy the code

If (mhandler.post ()) {if (mCallback) {if (mCallback) {if (mCallback) {if (mCallback) {if (mCallback) {if (mCallback) {if (mCallback) {if (mCallback) {if (mCallback) {if (mCallback) {if (mCallback);

/ / Handler class

final Callback mCallback;
public interface Callback {
        / * * *@param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }
Copy the code

A callback object is passed in to a new Handler, so McAllback.handlemessage () is not null. Method, which is the handleMessage() method we implement in the callBack interface. In the method we override, there is no difference between return true or return false.

If we return true, the source code will simply return. If we return false, the source code will go to the following handleMessage(), which is a public method exposed in the Handler class that can be overridden, but whose method name is the same as the one in the interface. We can’t override the handleMessage() method in the Handler class of method 1, so it makes no difference whether we write return true or return false.

If we had written in method 2, we would have followed the handleMessage() method below. That is, the rewritable public method handleMessage() exposed in the Handler class we overwrote in Approach 2.

Ok, so that’s the difference between the two methods. Now let’s talk about the if in the dispatchMessage() method that we ignored earlier. Why is the code in the if for mhandler.post ()?

Let’s look at our own mhandler.post ():

new Thread(new Runnable() {
            @Override
            public void run(a) {
                mHandler1.post(new Runnable() {
                    @Override
                    public void run(a) {
                          / /...}}); }});Copy the code

Handler: post();

/ / Handler class

public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
Copy the code

As you can see from the Runnable object we passed in, we call getPostMessage(r) to wrap the Runnable object we passed in. So what’s going on in this method?

/ / Handler class

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
Copy the code

Message.callback is a callback attribute in the Message class, which is a Runnable object:

 // in the Message class
 
 @UnsupportedAppUsage
 /*package*/ Runnable callback;
Copy the code

The Message object returned by the above method and its callback property value is the Runnable object passed in by the POST () method.

So let’s go back to the if in the dispatchMessage() method of the Handler class:

/ / Handler class

   public void dispatchMessage(@NonNull Message msg) {
        if(msg.callback ! =null) {
            handleCallback(msg);
        } else {
            if(mCallback ! =null) {
                if (mCallback.handleMessage(msg)) {
                    return; } } handleMessage(msg); }}}Copy the code

Mhandler.post () executes the code in if when receiving the message, since msg.callback is the Runnable object passed in and is not null, handleCallback() is executed:

/ / Handler class

private static void handleCallback(Message message) {
        message.callback.run();
    }
Copy the code

You can see that the handleCallback() method executes message.callback’s run() method, which is overridden by the Runnable interface we passed in.


4. CreateMessageWhat is the difference between the two methods?

A:

Message message = Message.obtain();
Copy the code

Method 2:

Message message = mHandler.obtainMessage();
Copy the code


Let’s take a look at the source code for the Obtain () method in the Message class in approach 1:

    public static Message obtain(a) {
        synchronized (sPoolSync) {
            if(sPool ! =null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                returnm; }}return new Message();
    }
Copy the code

From the source code, Android provides a cache pool for Message, which can cache used messages for future use. When we obtain a Message using the message.obtain () method, we obtain the Message from the cache pool first, and create the Message only if it is not available. Doing so optimizes memory. The cache pool sPool is also pointed to the Message next.


Now look at the source code for obtainMessage() in method 2:

 public final Message obtainMessage(a)
    {
        return Message.obtain(this);
    }

Copy the code

The Messageobtain() method is actually called, passing the current handler object as an argument.

 public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }
Copy the code

The obtain() method on the first line is the same as the message.obtain () method on the first line, with a second line assigning handler to message.target. SendToTarget () = message.sendtotarget () = message.sendtotarget () = message.sendtotarget () = message.sendtotarget () = message.sendtotarget () = message.sendtotarget ()

public void sendToTarget(a) {
        target.sendMessage(this);
    }
Copy the code

Target = handler; this = message; this = handler. SendMessage (message);


5. Why does improper use of Handler cause memory leakage?

Remember that when we used the second method to create a Handler, there was a yellow warning to the effect that this Handler was not set to static and that the resulting memory leak would occur on the external class that holds the Handler class, such as MainActivity.

The second way to create a Handler is not set to static, so it is possible to leak memory.

Two things to know first:

  • HandlerWe get the main thread when we create itLooperObject, and thisLooperThe life cycle of an object is the same as the life cycle of an application.
  • Java BasicsNon-static inner classorAnonymous inner classIt’s defaultHolds a reference to an external class.

Based on the above two theoretical knowledge, we imagine a scenario in which there are unprocessed messages waiting to be processed in the MessageQueue in the Handler. By default, message in the message queue holds a reference to Handler, which in turn holds a reference to an external class such as MainActivity by default. Assuming Handler is created in MainActivity, So if we destroy the MainActivity, the garbage collector will not be able to reclaim the MainActivity because of the above reference, resulting in a memory leak.


Now that we know why memory leaks occur, what can be done to fix them? From the cause, of course:

  • Static inner class + weak reference.
  • Empty the external class when it is about to be destroyedHandlerThe message queue of theHandlerSet tonull.

The principle of the first approach is that static inner classes do not hold references to external classes by default, and weakly referenced objects have a short lifetime. When the garbage collector thread scans, once it finds an object with only weak references, it reclaims its memory regardless of whether the current memory space is sufficient or not.

The second method works by emptying the message queue so that the reference relationship does not exist and synchronizes the lifecycles of MainActivity and Looper objects.


The first way to do:

public class HandlerActivity extends AppCompatActivity {

    private final MyHandler mHandler = new MyHandler(this);

    private static final Runnable mRunnable = new Runnable() {
        @Override
        public void run(a) {
            / / operation}};@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fourth);

        mHandler.postDelayed(mRunnable, 1000*10);
        
        finish();   
    }


    private static class MyHandler extends Handler {
        WeakReference<HandlerActivity> mWeakActivity;

        public MyHandler(HandlerActivity activity) {
            this.mWeakActivity = new WeakReference<HandlerActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            final HandlerActivity mActivity = mWeakActivity.get();
            if(mActivity ! =null) {
                // Process the message}}}}Copy the code


The second way is:

 @Override
    protected void onDestroy(a) {
        super.onDestroy();
        Log.e("TAG >>>>"."onDestroy");

        // Handle memory leaks correctly
        mHandler.removeCallbacksAndMessages(null);
        mHandler = null;
    }
Copy the code





Five,HandlerMechanism source code explanation

Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler

First to aWorth ten millionThe graph:

For Hanlder’s asynchronous messaging mechanism, there are four steps:

Step 1:

The looper.PrepareMainLooper () method is called in the main() method of the ActivityThread class when the application starts. Prepare () is called in prepareMainLooper(), which creates a globally unique main thread Looper object.

 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));
    }
Copy the code

The sthreadlocal.set () method inserts the new Looper into the ThreadLocalMap. The key value is the current thread (the main thread) and the value of the Looper is the main thread’s Looper.

/ / private
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
Copy the code

So the Looper object can only be created using the static prepare() method exposed by the Looper class. This creates a globally unique main thread Looper object. The MessageQueue object, which is the globally unique MessageQueue for the main thread, is also created.


Step 2: When we create the Handler object on the main thread, i.e., new Handler, look at the source code:

public Handler(@Nullable Callback callback, boolean async) {
      / /..
       mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread " + Thread.currentThread()
                       + " that has not called Looper.prepare()");
       }
       mQueue = mLooper.mQueue;
       mCallback = callback;
       mAsynchronous = async;
   }
Copy the code

MLooper and Queue are initialized. The Looper object created by the main thread is assigned to the member variable mLooper in the Handler class. The MessageQueue MessageQueue in the main thread Looper is assigned to the member variable mQueue in the Handler class.


Step 3:

whenHandlerWhen we send the message, we knowHandlerThere are many ways to send messages:But no matter how many ways you can send a message, by looking at the source code, you will eventually call itenqueueMessage()Methods:

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);
    }
Copy the code

MSG. Target = this; MSG. Target = this; This line of code, which is the Handler object that calls this method, assigns the Handler object to the target property of the passed Meessage object.

EnqueueMessage: MessageQueue enqueueMessage: MessageQueue enqueueMessage

boolean enqueueMessage(Message msg, long when) {
       / /...

        synchronized (this) {
            / /...
            Message p = mMessages;
            boolean needWake;
            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 {
                / /...
            }
            //....
        }
        return true;
    }
Copy the code

We see this line of code in there: mMessages = MSG; MessageQueue (MessageQueue) MessageQueue (MessageQueue) MessageQueue (MessageQueue) The value of MSG is also assigned to the mMessages attribute in the main thread MessageQueue (MessageQueue class).


Step 4:

How does the Handler receive and process the message? Looper.loop() is called to loop messages in ActivityThread after the application starts.

 public static void loop(a) {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
         //....
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            / /...
            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); }}/ /...}}Copy the code

We see one line of code that MSG. Target. DispatchMessage (MSG); The Looper message pump is always fetching messages from the MessageQueue. Once a message is fetched from the MessageQueue, it calls the dispatchMessage () method to distribute the message. So what is msg.target? Remember when the Handler used to call enqueueMessage() no matter what method it called to send a message? In that method, the caller Handler is assigned to the message.target property. So the way to distribute messages here is by calling the Handler’s own dispatchMessage() method. The dispatchMessage() method of the Handler itself has been examined before:

 public void dispatchMessage(@NonNull Message msg) {
        if(msg.callback ! =null) {
            handleCallback(msg);
        } else {
            if(mCallback ! =null) {
                if (mCallback.handleMessage(msg)) {
                    return; } } handleMessage(msg); }}Copy the code

If you are sending a message by calling mHandler.post(), then the message will be processed by following the code in the if above and executing the Runnable interface passed in by mHandler.post(Runnable r) to implement the Runnable class’s re-abstract run() method.

If you created the Handler object by implementing the callback interface, then use the if code in the else section to call the handleMessage() method that implements the callback interface.

If you create a Handler object from an anonymous object class and override the handleMessage() method exposed in the Handler class, go to the last handleMessage() method.





At the end of the article

The paper come zhongjue shallow, and must know this to practice. Winter Night reading shows the child Yu — Lu You

Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler