欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Handler消息机制源码分析

程序员文章站 2022-07-14 19:42:06
...

一,前言

众多周知, Android 只允许在主线程中更新UI,因此主线程也称为UI线程(ActivityThread)。

如此设计原因有二:

(1) 由于UI操作的方法都不是线程安全的,如果多个线程都可以更新UI,会造成UI界面混乱。

(2)UI操作都加锁,线程同步的话,非常影响性能。

故Android干脆只允许在单个线程-->主线程中操作UI, 否则会报android.view.ViewRootImpl$CalledFromWrongThreadException:只有创建该view的线程才可以改变该view的状态。

Google 为上述单一线程更新UI设计了Handler消息机制,主要场景是为了更新UI,更广泛的应用场景是线程之间进行通信。

二, Handler消息机制

        Handler 消息机制主要包含三个重要角色:Handler, MessageQueue, Looper

        Handler 发送消息,压入Messagequeue,Looper 作为轮询器,不停地从MessageQueue中获取Message,并再次分发dispatchMessage给Handler, 由handler的handleMessage()方法处理。


1, Handler  消息发送处理者

         Handler持有一个Looper对象和一个MessageQueue对象(即looper.mQueue), 在创建Handler对象时可以通过指定Looper(如果不指定,默认为当前线程的Looper对象 ),来确定Handler为哪个目标线程服务: 发送消息到该目标线程,并在目标线程里处理消息。 

        Handler提供了多种发送消息的方法,它们之间的调用关系如下

在post(Runnable r)   / postDelayed(Runnable r,  long delay)  / sendMessage(msg)里调用的是sendMessageDelayed(msg, delay ) ,进而sendMessgaeAtTime(msg, SystemClock.uptimeMillis()+ delay)。

其中post方法是将runnable 通过getPostMessageRunnable(r) 转化为一个Message对象,msg.callback = r;

最终通过enqueueMessage()将消息压入队列中。可以看到此处将message.target 指定为该handler,(后面会讲target的作用),然后调用了MessageQueue.enquqeueMessage()

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){  

      msg.target = this; 

      return queue.enqueueMessage(msg, uptimeMillis);

}     

另外Handler提供了可以将消息发送到队头的特殊方法, 这样可以使得消息可以优先被处理,postAtFrontOfQueue(msg)、 sendMessageAtFront|OfQueue(msg), 其实本质也一样,只不过消息的时间戳设为0, 最终是sendMessgaeAtTime(msg, 0)

2, Looper 轮询者

       Looper作为轮询器,提供的都是静态方法。

        (1)   Looper.prepare() 初始化

        内部创建一个Looper对象,并存在ThreadLocal里。ThreadLocal是线程的数据隔离区,可以存放各个线程单独的数据,存取ThreadLocal数据,不受多线程影响。 new Looper()时,looper对象持有一个新建的MessageQueue对象 和 当前线程对象(Thead.currentThread())。sThreadLocal.set(new Looper() ) ; 

        (2)   Looper.loop()  循环获取messageQueue里的消息

         里面有一个死循环for(;;)    去调用   messageQueue.next() 方法获得队头的msg, 然后分发给handler去处理。loop()方法是一个无限循环,之后的代码不会再执行。

        (3)Looper.getMainLooper()  获取UI线程的Looper对象 

              Looper.myLooper()获取当前线程的Looper对象

       主线程的looper对象早在ActivityThread启动的时候通过prepareMainLooper()创建好了,因此每次直接获得即可。而获得当前线程的looper对象也很简单, 直接取sThreadLocal存放的looper对象,与prepare()相呼应,sThreadLocal.get()。

3, MessageQueue消息队列

        虽然看起来MessageQueue只是一个链表存储的消息容器,但是真正的处理方法都封装在这个类里, 发送消息最终调用的是enqueueMessage(), 轮询消息调用的是next()。这里也巧妙的使用了生产者消费者模式。

       enqueueMessage(msg,uptimeMillis)方法 :将消息压入队列,每个消息有时间戳,, msg.when = uptimeMillis;按照uptimeMillis的大小从小到大依次排序插入队列中,uptimeMillis相等的,则先入队列的先处理。

      next()方法:获取队列的头部message,即链表的header,值得注意的是,next()方法里也有一个死循环,直到取到满足条件的message才跳出循环,这个条件就是message的执行时间要小于当前时间,message.when < now。如果msg.when <= now, 则不阻塞,直接取出,这不必说, 如果msg.when > now, 就进到下一轮循环里, 直到msg.when <= now.

4, Message消息

      一个数据model 本来没什么要说的,但是它有自己的神奇之处。它所能携带的信息自不必说,what唯一标示消息种类,arg1,arg2用于携带简单的数字,obj携带对象类数据, setData(Bundle)携带更复杂的数据。

       在Message里有一个实例变量target,target是个handler对象,非常关键, 轮询获取到msg后, msg自调target的dispatchMessage()方法, 继而回调handleMessage()方法处理消息。 这里要注意的是处理消息有一个顺序: 首先msg的callback如有先处理,其次handler创建时带的callback如有则处理,最后才轮到handleMessage()方法处理。 

     

                                                   源码结构示意图(可放大查看)

要点:

1,一个线程有且仅有一个Looper和一个MessageQueue!!! 但是可以有多个Handler, 一个msg到底由哪个handler对象来处理,会通过msg.target 来决定

2, Looper.loop()方法一定要在prepare()之后调用,先初始化了才能调,并且 Looper.loop()  之后的代码不能执行,因为是个死循环for(;;) {mQueue.next();  源码可以看到 ActivityThread的main方法里最后一句就是loop()方法; 我们在自己实现子线程的消息通信时,一定要记得这点。(当然HandlerThread推荐给你,本质是一个封装好Looper的Thread子类)

3,MessageQueue里有很多native方法,最主要的两个方法next()和enqueueMessage(),mQueue.next() 方法里也有一个死循环,只有获取到一个有效的msg才跳出循环。

4, Handler构造方法里mLooper = Looper.myLooper();若不指定,则为当前线程, 因为是从ThreadLocal里取出的Looper对象

5,Handler sendMessage→ sendMessageDelayed()→sendMessageAtTime()
每个消息是有时间戳的,postAtFrontOfQueue(),时间戳的值=0,而一般消息的时间戳是当前系统时间,

6,处理消息有一个顺序: 首先msg的callback如有先处理,其次handler创建时带的callback如有则处理,最后才轮到handleMessage()方法处理。 其中post(r), getPostMessage(r)就将r作为msg的callback发出去一个msg。 这里肯定优先执行r,而不是handleMessge()方法。

三, HandlerThread

    本质是一个封装好了Looper的Thread子类。HandlerThread extends Thread , 持有一个成员变量mLooper,提供有getLooper()方法,其中在Thread的run() 方法中代()码如下,很简洁,可以看到Looper.loop()方法是最后一行。

@Override 
public void run() {   
    Looper.prepare();    
    synchronized (this) {       
        mLooper = Looper.myLooper();        
        notifyAll();    
    }       
     Looper.loop();    
}复制代码

使用HandlerThread的方法

   HandlerThread handlerThread = new HandlerThread();        
   handlerThread.start();
   Handler handler = new Handler(handlerThread.getLooper()) {
        @Override   
        public void handleMessge() { 
        }
   };
   handler. sendMessage(msg);
注意: handler的创建必须在HandlerThread.复制代码

Handler的创建必须在handlerThread.start()之后,由源码中可以看到Looper的初始化是在handlerThread的run()方法里!!









转载于:https://juejin.im/post/5cefc6b0e51d45109725fe0b