Detailed analysis of the use and source code of Handler in Android

[This article has participated in the "New Talent Creation Ceremony" event, and we will start the road of creation of the Nuggets together]

1. The role of Handler in Android

In Android, the Android system encapsulates a set of multi-thread message communication mechanisms for us, the handle mechanism, which is mainly used for message passing between multi-threads and for updating UI operations.

2. Implementation mechanism of Handler

Before talking about the implementation mechanism of handle, first understand a few basic concepts:

  • Handler message sender and handler
  • Message message carrier
  • The loop poller is responsible for taking messages from the message queue and distributing them to the handle
  • MessageQueue maintains the message queue sent by the storage handle

insert image description here

3. The specific use of Handle

3.1 The child thread sends a message to the UI thread

(1) Create a Handler to send messages and rewrite handleMessage to process messages. The internal static class is used to define Handle because the non-static internal class holds the reference of the external class by default. When the Activity is destroyed, it is easy to cause memory leaks, but the static class is used. Yes, an activity reference is required in the handle, and all references to external activities are held by weak references. When the JVM performs garbage collection, the objects associated with weak references will be recycled regardless of whether the memory is sufficient.

  static class MyHandle extends Handler{
  
        WeakReference<Activity > mActivityReference;

        MyHandle(Activity activity) {
            mActivityReference= new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MyTheadActivity activity = (MyTheadActivity) mActivityReference.get();
            activity.textView.setText("接收到数据:"+msg.arg1);
        }
    }
    MyHandle myHandle=new MyHandle(this);
复制代码

Create a child thread to send data

 new Thread(){
            @Override
            public void run() {
                super.run();
                while (num>0){
                    try {
                        num--;
                        Thread.sleep(100);
                        Message message = Message.obtain();
                        message.arg1=num;
                        myHandle.sendMessage(message);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
复制代码
3.2 UI thread sends message to child thread

Create a Handle object in the child thread;

private void initSubThread() {
        new Thread(){
            @Override
            public void run() {
                super.run();
                Looper.prepare();
                myHandle1 =new MyHandle(MyTheadActivity.this);
                Looper.loop();

            }
        }.start();
    }
复制代码

Send messages from the main thread to child threads

 textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message msg = Message.obtain();
                msg.arg1=100;
                myHandle1.sendMessage(msg);
            }
        });
复制代码

The whole process creates handle objects in the child thread and sends messages in the main thread.

08-05 21:53:34.963 1782-1795/camera.test.com.networkdata E/TAG: 线程名称:Thread-77接收到消息:100
复制代码

At this time, two methods were called when the Handle was created Looper.prepare(); Looper.loop();. What kind of classes are these two methods?

I said before that the handle sends a message to a Message Queue, and then Looper polls to take out the message from the MessaeQueue queue, and then distributes the message through the dispatchMessage method, and finally handleMessage processes the message in the handle. Then definitely need one Looper对象和MessageQueue消息队列to cooperate with the message passing in Handle multithreading. Next, let's see what the source code does specifically through the example of the main thread sending a message to the child thread.

4、handle 源码看看

从上边的主线程发消息到子线程开始,加入不调用Looper.prepare(); Looper.loop(); 方法会怎么样。

08-05 22:08:12.423 1897-1916/camera.test.com.networkdata E/AndroidRuntime: FATAL EXCEPTION: Thread-80
    Process: camera.test.com.networkdata, PID: 1897
    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:200)
        at android.os.Handler.<init>(Handler.java:114)
        at camera.test.com.networkdata.MyTheadActivity$MyHandle.<init>(MyTheadActivity.java:29)
        at camera.test.com.networkdata.MyTheadActivity$2.run(MyTheadActivity.java:68)
复制代码

发现 程序直接崩溃了,意思就是mLooper不能为空。需要个Looper,这个错误我们也可以通过查看Handle的源码看到

源码分析一:
 public Handler(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;
    }
复制代码

可以看到 Looper.myLooper(); 获取的Looper对象为null的时候就抛出RuntimeException异常。

源码分析二: Looper.myLooper();

从ThreadLocal 中获取Looper对象


   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
   
 /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
复制代码
源码分析三: Looper.prepare();

通过以上分析一和分析二知道,子线程中使用handle必须需要一个Looper否则就会报错,那接下在看下Looper.prepare(); 都做了些什么

 public static void prepare() {
        prepare(true);
    }
	
    private static void prepare(boolean quitAllowed) {
     //解释一: 一个线程中只能有一个Looper对象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread"); 
        }
        解释二: 通过ThreadLocal 存储Looper对象
        
        sThreadLocal.set(new Looper(quitAllowed)); // 源码分析四
    }
复制代码

总结:一个线程中只能有一个Looper对象,创建的Looper对象通过ThreadLocal进行存储,和创建Handle时候的sThreadLocal.get(); 相对应。

源码分析四: new Looper(quitAllowed)
 private Looper(boolean quitAllowed) {
 		解释一 :创建消息队列
        mQueue = new MessageQueue(quitAllowed);
        解释二 : 绑定到当前线程
        mThread = Thread.currentThread();
    }
复制代码

总结:终于看到MessageQueue 了,再不见还以为他是个摆设呢,创建Looper对象的时候,自动创建MessageQueue 消息队列。

通过以上分析:知道Looper.prepare(); 创建了 Looper对象和MessageQueue消息队列对象,并绑定到当前线程。以上只是创建了Looper和MessageQueue,并没有见如何 轮询从MessageQueue中取出数据,接下来就看下Looper.loop();

源码分析五: Looper.loop();
 public static void loop() {
 //  解释一 :获取Looper对象
        final Looper me = myLooper();
  //解释二 :如果looper是null抛出异常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
    //解释三 :获取MessageQueue  消息队列
        final MessageQueue queue = me.mQueue;
        
          省略一些代码
     
     // 解释四 : 开启一个无限循环从MessaQueue中取出消息
     
        for (;;) {
        消息队列中取出消息,消息为空return
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

          省略了一些代码
          
            try {
           // 解释五: 	取出的消息进行消息分发,msg.target 是啥呀?就是发送消息的Handle 对象,
           最终调用的是handle中 dispatchMessage方法/
                msg.target.dispatchMessage(msg);    源码分析 六
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
        
            msg.recycleUnchecked();
        }
    }
复制代码

总结: 通过以上代码知道,Looper.loop(); 就是looper 轮询器开始循环的从消息队列中取出数据,然后交给Handle去处理。

源码分析 六 dispatchMessage()
public void dispatchMessage(Message msg) {
	
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
          
            handleMessage(msg);
        }
    }
复制代码

总结:通过看handle中的dispatchMessage方法,发现如果mCallback 不为null的时候,最终调用handleMessage()方法,也就是我们经常重写处理消息的方法。

总结:通过以上分析,可以得出handle同过sendMessage方法发送消息,到消息队列然后looper从消息队列中取出消息然后最终还是交给handle去处理,那么handle是怎么和Looper、MessaheQueue进行关联的的嘞?接下来看下 创建Hanlde过程中都做了那些操作

源码分析 七 创建Handle过程

Handle创建有几种方式

第一类:不需要指定Looper对象,

 public Handler() {}
 
 public Handler(boolean async){}
 
 public  Handler(Callback callback) {}
  
 public Handler(Callback callback, boolean async) {}
 
 
第二类:自己传一个Looper 对象

 public Handler(Looper looper){}

 public Handler(Looper looper, Callback callback){}
 
 public Handler(Looper looper, Callback callback, boolean async) {} 

复制代码

先看下第一种创建方式,第一种最终都会调用 public Handler(Callback callback, boolean async) {} 方法

public Handler(Callback callback, boolean async) {
      
       省略一些代码
      //获取Looper对象,之前源码分析二中分析过了
        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;
    }
复制代码

总结 :通过以上创建Handle的源码,发现初始化了Looper对象和mQueue 消息队列,callback回调,

然后再看下第二种创建方式,最后调用的是 public Handler(Looper looper, Callback callback, boolean async) {}

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

和第一种方式区别不是很大,只是Looper是自己创建的,传进来的,注意Lopoer在那个线程创建,这个线程就属于一个Looper线程。

总结:通过以上源码可以知道,在创建Handle的时候,handle和当前的线程的Looer、messageQueue进行关联的。然后handle发送消息到MessageQueue中,looper从MessageQueue取出消息,交给handle处理,整个流程就已经清晰可见。但是handle是怎么发送消息到消息队列的呢。

源码分析 八 消息发送
第一种 sendXXX 系列 最终都调用的是 sendMessageAtTime

public final boolean sendMessage(Message msg)

public final boolean sendEmptyMessage(int what){}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) 

public final boolean sendMessageDelayed(Message msg, long delayMillis)

public boolean sendMessageAtTime(Message msg, long uptimeMillis) 

第二种  postXX系列  其实最红调用的也是 sendMessageAtTime方法

 public final boolean post(Runnable r)

 public final boolean postAtTime(Runnable r, long uptimeMillis)

 public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)

 public final boolean postDelayed(Runnable r, long delayMillis)

复制代码

第一种和第二种方法不一样的地方就是,第二种方法需要一个Runnable 接口。很熟悉者不是创建线程的时候的Runnable 的么怎么这里也用到看下 public final boolean post(Runnable r)

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
复制代码

getPostMessage( r ) 获取一个Message对象

  private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
复制代码

Runnable 直接赋值给Messge的callback了。那么有什么用呢,这个看下面的handle的dispatchMessage方法。

接下来看下sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
		获取到MessageQueue
        MessageQueue queue = mQueue;
        if (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);
    }
复制代码

enqueueMessage方法

  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  // 当前handle对象赋值给 message的target 指定处理message对象
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        
        return queue.enqueueMessage(msg, uptimeMillis);
    }
复制代码

queue.enqueueMessage 存放消息到消息队列 (大概知道就行)

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
         省略一些代码
         // 整体意思就是 ,按照消息发送时间存放到对列当中 ,存放成功返回True 
            msg.when = when;
            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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
复制代码

Summary: The above is the whole process of sending a message. Through the sendXXX or postXXX methods, the sendMessageAtTime() method is ultimately called to send the message to the Message Queue message queue and specify the message processing object.

As mentioned above, the postXX method is passed in a Runnable interface, and finally assigned to the callBack of Message, then what is the use? Let’s look at dispatchMessage again.

 public void dispatchMessage(Message msg) {
 // 看到了把 如果msg.callback != null 调用  handleCallback(msg);
        if (msg.callback != null) {
            handleCallback(msg);
        } else {  
         //mCallback 又是什么 ,通过第一种创建 Handle方式,
            有一个参数为Callback ,这也是一个接口,如果不为空的话,就直接调用 他的 handleMessage 
            进行消息处理,   否则再交给 handle自己的handleMessage 处理
            if (mCallback != null) { 
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
复制代码

See what handleCallback does

 private static void handleCallback(Message message) {
    //  最终调用的是 接口run方法。 而且不会再走    handleMessage(msg); 方法了
        message.callback.run();
    }
复制代码

Through the dispatchMessage method, we know that the handleMessage of Handle has the lowest priority of processing messages. As long as our msg.callback and mCallback are not empty, we can intercept the message processing of handle.

Summary : The above is the entire analysis process of handle communication in multiple threads. Create Looper and MessageQueue objects in the current thread, bind Handle to them, turn the current thread into a Looper thread, and enable monitoring of the message queue. The handle passes through Send the message to MessageQueue, then take out the message through Looper, and hand it over to the handle for processing. You can also intercept the handleMessage of the handle by sending the message through the handle.

5. Why didn't the UI thread create Looper and MessageQueue?

That's because when the program starts, the system has already created the specific code for us to see the main method of ActivityThread

  public static void main(String[] args) {
  
      //  省略一些代码

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

     //   省略一些代码
      
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
复制代码

The above code is created for us when the system starts up. The specific creation is similar to the analysis above, so I won't say more.

Guess you like

Origin juejin.im/post/7084169943503077383