Read before thinking

The best way to learn a skill or read an article is to study with questions, so that in the process, you will have a sense of clarity, lights out, and memory will be more profound.

  1. How do I get Message for Message sending
  2. How can I avoid memory leaks caused by using handlers?
  3. Handler Looper messageQueue Specifies the relationship
  4. How does the program distinguish which Handler to send a message to?

An overview of the

Android development is inseparable from dealing with Handler, which is usually used as a communication tool between the master thread and child threads. Handler, as an important part of the Messaging mechanism in Android, has indeed brought great convenience to our development.

You can say that whenever there’s an asynchronous thread talking to the main thread there’s going to be a Handler.

1. Basic use of Handler

1.1 create a Handler

Handler allows us to send delayed messages that will leak if the user closes the Activity during the delay.

This leak happens because Message holds Handler, and because of Java’s nature, inner classes hold external classes, so the Activity is held by Handler, which eventually leads to Activity leaks.

The most efficient way to solve this problem is to define Handler as a static inner class that holds weak references to the Activity and removes all messages in a timely manner.

The following is an example:

public class Part8HandlerActivity extends AppCompatActivity { private Button bt_handler_send; Private static class MyHandler extends Handler {// Weak reference holds Part8HandlerActivity, Private WeakReference<Part8HandlerActivity> WeakReference; public MyHandler(Part8HandlerActivity activity) { this.weakReference = new WeakReference(activity); } @Override public void handleMessage(Message msg) { Part8HandlerActivity activity = weakReference.get(); super.handleMessage(msg);if(null ! = activity) {// Execute the business logic toast.maketext (activity,"handleMessage",Toast.LENGTH_SHORT).show();
            }
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_part8_handler); // Create Handler final MyHandler Handler = new MyHandler(this); bt_handler_send = findViewById(R.id.bt_handler_send); bt_handler_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {// Use handler to send an empty message handler.sendemptyMessage (0); } }).start(); }}); } @Override protected voidonDestroy() {/ / remove all callbacks and message myHandler. RemoveCallbacksAndMessages (null); super.onDestroy(); }}Copy the code

Note: Simply removing the message on onDestroy is not safe because onDestroy does not have to be executed.

1.2 the Message gets

There are several ways to get a Message:

Message message = myHandler.obtainMessage(); Message1 = message.obtain (); Message2 = new Message(); // Create a new Message instance directlyCopy the code

Looking at the source code, you can see that the Handler’s obtainMessage() method also calls the Message obtain() method

public final Message obtainMessage()
{
    return Message.obtain(this);
}
Copy the code

By looking at the Message obtain method

Public static Message obtain(Handler h) {// Call the following method to obtain Message m = obtain(); // Specify the current Handler to the target of message, which Handler is the message.returnm; } // Fetch Message from the Message pool, return if there is one, otherwise create a new Message public static Messageobtain() {
        synchronized (sPoolSync) {
            if(sPool ! = null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clearin-use flag
                sPoolSize--;
                returnm; }}return new Message();
    }
Copy the code

To save overhead, we try to reuse messages as much as possible and create them in the first two ways.

1.3 Handler Sends messages

Handler provides a list of methods to send messages, such as the Send () series and the Post () series.

But no matter what method we call, will eventually go to MessageQueue. EnqueueMessage (Message, long) method. Take the sendEmptyMessage(int) method for example:

//Handler
sendEmptyMessage(int)
  -> sendEmptyMessageDelayed(int,int)
    -> sendMessageAtTime(Message,long)
      -> enqueueMessage(MessageQueue,Message,long)
  			-> queue.enqueueMessage(Message, long);

Copy the code

MessageQueue is the MessageQueue, which is responsible for the message entering and leaving the queue.

2. Parse the principle of Handler

1. Constructor

When instantiating a Handler, the Handler checks to see if the current thread’s Looper exists, and raises an exception if it doesn’t. In other words, you must create a Looper before creating a Handler.

Public Handler(Callback Callback, Boolean async) {// Check whether the current thread holds 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 holds a MessageQueue mQueue = mlooper.mqueue; mCallback = callback; mAsynchronous = async; }Copy the code

2. Looper

Looper can be created in the current thread by calling looper.prepare (), and then looper.loop () to loop the message queue.

The code is as follows:

Private static void prepare(Boolean quitAllowed) {// If Looper already exists on the current thread, an exception will be raisedif(sThreadLocal.get() ! = null) { throw new RuntimeException("Only one Looper may be created per thread"); Set (new Looper(quitAllowed)); }Copy the code

Looper.loop() code

public static void loopFinal Looper me = myLooper(); 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 {/ / messages through the handler MSG. Target. DispatchMessage (MSG); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { //.. } / /.. // Discard Message msg.recycleunchecked (); }}Copy the code

3. Summary

Handler is assisted by Looper and MessageQueue, and the three work together with a clear division of labor. Try to summarize their responsibilities as follows:

Looper: The thread is responsible for the associated thread and Message distribution. The thread obtains the Message from MessageQueue and distributes it to the Handler. MessageQueue: a queue that stores and manages messages sent by the Handler. Handler: Responsible for sending and processing messages, facing developers, providing apis, and hiding the implementation details behind them.

Messages sent by the Handler are stored and managed by MessageQueue, and the Loopler is responsible for calling back messages to handleMessage().

Thread conversion is done by Looper, and the thread of handleMessage() is determined by the thread of the looper.loop () caller.

3, summarize

There is a lot of wisdom behind the simplicity of the Handler, so try to learn from them.

Hopefully, this article will help you understand Handler better.

4, reference

Handler

Android Messaging 1-Handler(Java Layer)

Handler doesn’t understand why he should jump ship.