Pattern use Handler and source code analysis

  • 2021-11-01 04:28:17
  • OfStack

A few days ago, when discussing a problem with the company bosses, I saw a way for him to use Handler, and my colleague next to him said: It was not used like this before. This question aroused my curiosity. Although I found out the truth at that time, I still want to share it with you.

Handler also talked about his use and source code analysis before, and I believe everyone knows how to use it. I'm afraid the most common use method is the following:


Handler handler = new Handler(){
  @Override
  public void handleMessage(Message msg) {
    super.handleMessage(msg);
  }
};

There is a problem in this case: we all know that Handler can be used in the sub-thread to update the main thread. When the sub-thread calls back to the main thread, Handler in the main thread executes the corresponding task by receiving the corresponding message sent. For the above Handler object, if it is in the main thread, then our sub-thread needs to get the Handler object of the main thread.


  final Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
    }
  };
  new Thread(new Runnable() {
    @Override
    public void run() {
      handler.sendMessage(new Message());
    }
  }).start();

However, the above writing is really not good-looking, and handler is still a local variable, which cannot be used in other methods.


  Handler handler;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.e(TAG, "onCreate: " );
    handler = new Handler(){
      @Override
      public void handleMessage(Message msg) {
        super.handleMessage(msg);
      }
    };
    new Thread(new Runnable() {
      @Override
      public void run() {
        handler.sendMessage(new Message());
      }
    }).start();
  }

This should look much better, and it may also be a way of writing by most people.

In fact, to put it bluntly, if we want to call back the main thread/corresponding thread in the sub-thread, then 1 must get the index of Handler in the main thread. This is very direct. In some cases, you may not be able to get the Handler of the main thread/corresponding thread, or the method of getting it is very troublesome:


public class MyService extends Service {
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }
  @Override
  public void onCreate() {
    super.onCreate();
    new Thread(new Runnable() {
      @Override
      public void run() {
        // Perform related time-consuming operations, and then pass the Handler Callback to the main thread 
      }
    }).start();
  }
}

Now I have this requirement: Create a service, open a child thread in the service to perform time-consuming operations, and call back in the main thread when the execution is finished. In this case, it is not impossible to get the Handler object of the main thread. There are still many methods. Write the handler of the main thread as static, create a class to inherit Handler and serialize it, and then pass it in through intent... There may be other methods, but as far as these situations are concerned, they don't seem to be very friendly. Here is a more elegant and convenient method:


public class MyService extends Service {
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }
  @Override
  public void onCreate() {
    super.onCreate();
    new Thread(new Runnable() {
      @Override
      public void run() {
        /* Perform related time-consuming operations, and then pass the Handler Callback to the main thread */
        new Handler(Looper.getMainLooper()).sendMessage(new Message());
      }
    }).start();
  }
}

There is only one sentence: it is convenient to watch and comfortable.

(Some friends may not know what Looper is. I have written an article about Handler before, hoping to help everyone: https://www.ofstack.com/article/55386. htm)

Through the Looper. getMainLooper method, you can get the Looper object of the main thread.

Although we said earlier that we need Handler created in the main thread, it is strictly wrong. The basic reason is that the main thread has already loaded mainLooper for itself, and when we are in the main thread, new Handler will get the Looper reference of the main thread by default.


  public static void main(String[] args) {
    //pass
    Looper.prepareMainLooper();
    //pass
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
  }
  public Handler(Callback callback, boolean async) {
    //pass
    mLooper = Looper.myLooper();// In the main thread new Adj. Handler Obtained looper Is the main thread mainLooper
    if (mLooper == null) {
      throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //pass
  }

Now it seems very clear that creating Handler in the main thread is just a cover, and it is looper object that really manipulates 1 cut behind it. So just let the mLooper reference of Handler get the reference of the main thread.

Moreover, the Looper. getMainLooper method is externally visible, so it is bold to guess that this method exists for this convenient writing. We can get the looper of the main thread through this method, and let him receive callbacks in the main thread.


  public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
  }

But notice how we write it above: new Handler (....). sendMessage

No matter how you implement this method, it can't get a response in the main thread (it digs a hole for everyone), for the simple reason that the Handler. handlerMessage method has not been rewritten.

There are three ways to accept messages using Handler:

Overrides the Handler. handlerMessage method in which the Implement Callback interface in Handler constructor and receive it in callback interface No processing is done, but the message is sent using post.

In the past, almost all the messages we saw when Handler received messages were handleMessage methods. In fact, this is only one of the methods. Before executing this method, there will be a distributed method dispatchMessage:


  /**
   * Handle system messages here.
   **/
  public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//msg In callback , this is through post Method itself encapsulates the msg (Check the source code), the priority is the highest 
      handleCallback(msg);
    } else {// Or in the constructor Handler Adj. Callback Interface, this priority is the first 2
      if (mCallback != null) {
        if (mCallback.handleMessage(msg)) {
          return;
        }
      }
      handleMessage(msg);// This is the most commonly used method before, the lowest priority 
    }
  }

You can see that there are two callback methods before the handlerMessage method. We didn't override Method 3 in the above case, so we can take the above two scenarios for using Handler anonymously in child threads. The code will not be written, and everyone is smart.

Ok, more use of Handler is here, and friends who like it hope to support it a lot. Those who have different opinions and understandings hope that there will be more exchanges in the comment area.

Summarize


Related articles: