Android的Handler基本原理源码分析

Posted hymKing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android的Handler基本原理源码分析相关的知识,希望对你有一定的参考价值。

一、前言

学习android基础知识,都离不开handler,最开始最基础的使用从Handler.post去发送一个消息,Android系统的设计中,为了保证UI渲染的一致性(本质上线程并发问题)和无卡顿体验。耗时的操作需要放在工作线程中执行(一般是开发者自行创建的子线程)、Ui更新放在主线程。这样从各自职责上来讲是非常清晰的,Handler的出现的一个核心目的,就是解决工作线程处理的数据如何传递给Ui线程进行使用。

**故而,**我们在研发中,子线程比如做耗时数据处理操作后,调用通过主线程创建的Handler发送数据消息,最终并由当前的Hander处理消息,实现子线程数据切换到主线程处理的逻辑。

这边文章内容,会深入去理解Android中的Handler机制。先列出这篇文章要解答分析的一个问题列表如下:

  • Handler死循环为什么不会导致应用程序卡死?[答案在3.3节]
  • MessageQueue的优先级队列是如何进行排序的?[答案在2.1节]
  • Message的数据结构是什么?如何保证线程安全的?[答案在2.1节]
  • 如何创建Message对象?【答案在3.1节】
  • 一个线程有几个Handler?如何实现这个机制的?[答案在2.2.3节]
  • 为什么主线程可以直接new Handler进行使用,子线程中new Handler如何操作?[答案在2.2.3节]
  • 为什么Handler容易出现内存泄漏?【答案在3.2节】
  • Handler、Looper、MessageQueue、message之间的关系?【答案在4节】

接下来的文章内容,会从Handler的知识体系的三条主线问题进行展开

  1. Handler的请求发送到哪里去了?
  2. Handler的请求是如何被处理的?
  3. Handler,Looper,MessageQueue、Message之间的关系?

注:文章中涉及到的framework层的源码版本为android29!!!

二、原理源码解析

2.1Handler请求发送

首先我们还是从一个最简单的Demo入手

public class MainActivity extends AppCompatActivity 
    private static final int HANDLER_FLAG = 666;

    private Handler mHandler = new Handler() 
        @Override
        public void handleMessage(@NonNull Message msg) 
            super.handleMessage(msg);

        
    ;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        //此处通过handler发送一个空消息
        mHandler.sendEmptyMessageDelayed(HANDLER_FLAG, 1000);
    

handler发送消息的方法有多个,如sendMessageDelayed、sendMessage、sendEmptyMessage等,追踪源码最终都会调用到Handler中的sendMessageAtTime():

public class Handler 
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) 
        //构建Message对象,通过Message.obtain()获得
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    
    /**
     * 根据绝对时间将message加入到message queue中.
     * @param uptimeMillis 消息被分发的绝对时间     
     * @return Returns true 消息被成功的入队,表示之后将被执行。如果未执行前,looper退出,则
     * 消息终止执行。
     */
    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) 
      //获取到了主线程的Message queue 后文会有对mQueue的创建分析  
      MessageQueue queue = mQueue;
        //queue为空的异常处理
        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);
    
    
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) 
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) 
            msg.setAsynchronous(true);
        
        //这里会调用MessageQueue的入队方法
        return queue.enqueueMessage(msg, uptimeMillis);
    

从这个方法文档注释可以看到handler请求的发送,本质上是将message加入到了message queue中,最终调用的是enqueueMessage(queue,msg,uptimeMillis)方法,看MessageQueue源码中入队列的实现:

/**
 * 会被looper消费分发的消息列表。 Messages 不会直接添加到 message queue中,而是通过和looper
 * 关联的handler对象来添加
 * 要检索当前线程messageQueue,可以通过looper.myQueue获得
 */
public final class MessageQueue 
   Message mMessages;
   boolean enqueueMessage(Message msg, long when) 
        .....
        //同步对象锁
        synchronized (this) 
            ...
            //消息标记为正在使用
            msg.markInUse();
            msg.when = when;
            //MessageQueue中实际用于存储消息的队列的是message对象,并非一个普通的list队列
            //Message的实现是一个单链表结构
            Message p = mMessages;
            boolean needWake;
            //收条message到达时,内部的message单链表为空链表会进入到此判定条件的执行中
            //或者是后进入的消息的计划执行时间小于前面的已经进入链中的消息的执行时间
            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 
                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;
            
          //本段中if...else...就是对单链表的核心操作。
        .....
        
        return true;
    

Message是一种单链表的数据结构,Message的源码实现比较简单,核心是有一个指向下一个节点的next属性。

单链表数据结构的特点是:

非线性、非顺序的物理结构,由n个连上的节点组成;

链表靠“见缝插针”的存储方式、不要求内存连续,靠next指针进行关联,对碎片内存的利用率高;

存储的时候是随机存储,访问的时候是顺序访问的,对于长链表,显示访问的效率会降低。

下面通过图解的方式,解析上述MessagQueue中的单链表操作:

首条消息进入到MessageQueue:

首条消息进入MessageQueue的最终结果是全局的mMessages引用指向msg1,msg1的next指针指向P为null.此时MessageQueue中持有的Message链上,有效Message只有msg1。

接下来,第二条消息入链,when条件小于P.when,仍然进入第一个分支条件:

第三条消息入链过程,when>p.when,进入第二个分支:

单链表中,根据message的when时间来确定插入的位置,最终单链表中的链是按时间链接的。

上述单链表的插入流程也并不复杂,且注意在equeue方法的执行时,添加了同步锁。通过handler请求发送的分析,我们也回答了开头部分的提出的这几个问题:

  • MessageQueue的优先级队列是如何进行排序的?

    排序规则,是根据加入到MessageQueue中message链的message对象所携带when信息,通过遍历链表的方式,比对when信息进行单链表的插入的。

  • Message的数据结构是什么?如何保证线程安全的?

    数据结构是单链表的message结构,在messageQueue中被维护,线程安全的设计是入队操作的核心方法体内部使用synchronized的同步对象锁,来保证入队的线程安全性。

2.2Handler请求的处理

2.2.1Looper循环器的创建和启动

handler发送的请求是如何处理的呢,这里面就会涉及到一个和Handler关联的重要的角色,做过android应用程序启动源码分析的同学都知道,应用程序的启动,最终会调用到ActivityThread的main方法,而looper的创建的来源,我们需要从ActivityThread的main方法入手。接下来,我们看ActivityThread的相关源码。

public final class ActivityThread extends ClientTransactionHandler 
  public static void main(String[] args) 
     ....
     //准备looper循环器
     Looper.prepareMainLooper();
     ....
     //Looper循环器的启动
     Looper.loop();
  

在应用程序启动的时候,就创建了主线程的Looper循环器,具体实现看Looper的类的源码:

public final class Looper 
  
 /**初始化当前线程的Looper,作为应用程序的主looper,主looper的创建是android系统环境来创建的
  * 所以不需要开发者自己来调用此方法
  */
   public static void prepareMainLooper() 
        prepare(false);
        synchronized (Looper.class) 
            if (sMainLooper != null) 
                throw new IllegalStateException("The main Looper has already been prepared.");
            
            sMainLooper = myLooper();
        
    
  //调用下面重载方法
   public static void prepare() 
        prepare(true);
    
   //获取或创建Looper实例
   private static void prepare(boolean quitAllowed) 
        if (sThreadLocal.get() != null) 
            //这个异常表明,每个线程只能保持一个独立的looper
            throw new RuntimeException("Only one Looper may be created per thread");
        
        sThreadLocal.set(new Looper(quitAllowed));
    

上面的两段源码中主要体现了Looper的创建和启动,另外我们在分析Activity的启动原理的时候,也看到过在系统源码层面的的handler的创建,一个Looper可以对应多个Handler,故而我们自己的应用开发的时候,如果需要主线程处理我们的消息,我们也经常自己创建所需的Handler,直接发送消息。

2.2.3Looper循环器获取和处理消息

Looper.loop方法被调用后,到底在干什么呢,我们需要通过查看loop()源码的实现如下:

public static void loop() 
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    .....
    //这里面是我们非常熟悉的死循环写法,也就是Looper真正循环起来
    for (;;) 
        //这里面的实现最终是对MessageQueue中的mMessages进行单链表的遍历
        Message msg = queue.next(); // might block
        .....
        try 
            msg.target.dispatchMessage(msg);
            ......
        
        msg.recycleUnchecked();
    

上述源码中省去了大部分非关键代码,从源码中可以看到,loop()的实现中,通过一个死循环不断的从MessageQueue的单链表的消息结构中取出message,最终通过msg.target.dispatchMessage(msg)将消息分发出去。很显然,现在我们只需要知道这个target是谁就可以了。我们需要从发送消息的源头来追踪msg对象中的target是谁。

public class Handler 
  final MessageQueue mQueue;
  public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) 
        //同样这里的mQueue,也需要通过源码分析其创建的时机
        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);
    
   private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) 
        //msg的target被赋值的地方
        msg.target = this;
        .....
        return queue.enqueueMessage(msg, uptimeMillis);
    

在handler的源码中的将消息入链的方法enqueueMessage()方法中,msg.target=this,this就是当前的handler对象,故而,我们知道了msg.target.dispatchMessage方法调用主体就是发送消息的主体,即当前发送消息的Handler。

**简单梳理下:**我们在主线程中创建了Handler,通过handler发送消息,消息以单链表的形式存入到MessageQueue中,而系统启动的时候,创建了Looper循环器,并将looper循环器启动,从messageQueue中获取消息,然后通过dispatchMessage方法将消息重新交给发送当前消息的handler去处理。

Handler中的dispatchMessage方法的实现:

public void dispatchMessage(@NonNull Message msg) 
    //如果有callBack就是callback处理
    if (msg.callback != null) 
        handleCallback(msg);
     else 
        if (mCallback != null) 
            if (mCallback.handleMessage(msg)) 
                return;
            
        
        //交给子类实现
        handleMessage(msg);
    
   
    /**
     * 源码中的英文注释:Subclasses must implement this to receive messages.
     * 子类必须实现这个方法
     */
    public void handleMessage(@NonNull Message msg) 
    

回想我们对Handler的使用,通过Handler发送的消息,最终会在我们创建的Handler的handleMessage方法中被处理。

接下来,我们看MessageQueue是如何创建的。

Handler的源码中MessageQueue queue = mQueue;而mQueue是在Handler的构造函数中被赋值的。

public Handler(@Nullable 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()");
    
    //handler中的mQueue被赋值
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;

而Looper的mQueue是在Looper的构造函数中被赋值的:

private Looper(boolean quitAllowed) 
    //创建了对应当前Looper的messageQueue
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();

通过上面的分析,我们可以回到文章开头部分的以下问题:

  • 一个线程有几个Handler?如何实现这个机制的?

一个线程可以有多个Handler。

实现机制:一个主线程只能有一个独立的Looper,应用启动时创建。而一个Looper很显然也独立对一个messageQueue。在当前线程中可以创建多个Handler,当时Handler发送的消息,都进入到当前线程Looper所对应的唯一的messageQueue中。

  • 为什么主线程可以直接new Handler进行使用,子线程中new Handler如何操作?

主线程的直接创建handler,就能被使用是因为应用在启动的时候,在ActivityThread的main方法中已经实现了Looper的创建和启动,同时在Looper创建的同时,也对应创建了MessageQueue,之后只要在主线程创建的Handler发送的消息,都会由系统已经创建好的Looper来进行处理。

而子线程要创建Handler,并且能使用Handler发送和处理消息,则需要主动调用Looper.prepare()、Looper.loop()来创建对应子线程的的Looper和与其对应的MessageQueue。官方源码的文档注释中的经典demo:

  class LooperThread extends Thread 
    public Handler mHandler;   
    public void run() 
         //prepare:创建looper和messageQueue
         Looper.prepare();
         mHandler = new Handler() 
             public void handleMessage(Message msg) 
                 //处理消息
             
         ;
         //启动循环器
         Looper.loop();
     
 

三、其它几个问题的分析

3.1如何创建Message对象?

看一下message的构函数:

/** Constructor (but the preferred way to get a Message is to call @link #obtain() Message.obtain()).
*/
public Message() 

Message对象的创建方式有很显然除了通过构造函数直接new的方式:new Message(),源码的文档注释中更推荐使用Message.obtain的方式创建。

/**
 * 从全局池中获取message实例对象。 避免创建多个对象。
 */
public static Message obtain() 
    synchronized (sPoolSync) 
        if (sPool != null) 
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        
    
    return new Message();

使用obtain的方法创建对象,本质上是对性能的优化,使用了缓冲池来提供可用的Message对象。

3.2为什么Handler容易造成内存泄漏?

从文章最开始的demo看,我们所创建的handler是MainActivity的内部类,内部类handler在字节码层面的实现会持有MainAcitity的引用。当通过handler.sendMessage时,会将Handler让Message持有(msg.target=this),之后又会将msg加入到messageQueue。而MessageQueue的在ActivityThread的main方法中创建mLooper时创建的。

从引用链上来讲,长生命周期的messageQueue对象持有了短生命周期的Activity的对象,必然可能导致内存泄漏。

解决方案:可以使用静态内部类或者在 onPause()中使用 Handler 的 removeCallbacksAndMessages(null)方法清除所有消息及回调

3.3Handler死循环为什么不会导致引用程序卡死?

主要是通过 Linux 的 epoll 机制实现的,这里需要 Linux 、 jni 等知识,简单说:

Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程。

A. Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的。

B. Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程。

C. Android应用程序的主线程进入空闲等待状态的方式实际上就是在管道的读端等待管道中有新的内容可读,具体来说就是是通过Linux系统的Epoll机制中的epoll_wait函数进行的。

D. 当往Android应用程序的消息队列中加入新的消息时,会同时往管道中的写端写入内容,通过这种方式就可以唤醒正在等待消息到来的应用程序主线程。

E. 当应用程序主线程在进入空闲等待前,会认为当前线程处理空闲状态,于是就会调用那些已经注册了的IdleHandler接口,使得应用程序有机会在空闲的时候处理一些事情。

这个问题的答案摘自知乎,因为涉及Linux底层知识。参见https://www.zhihu.com/question/34652589/answer/157834250

四、总结

通过上面的分析,我们使用一张图解来梳理Handler、Looper、MessageQueue、Message之间的关系。

  • Handler通过sendMessage()等方法发送Message到MessageQueue中的,实际持有的是一个Message的单链表对象;
  • Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target(即handler)来处理;
  • 经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理。

更多内容,关注:

以上是关于Android的Handler基本原理源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Android的Handler基本原理源码分析

Android的Handler基本原理源码分析

Handler源码分析

Handler源码分析

原创源码角度分析Android的消息机制系列——Handler的工作原理

从架构师的角度分析Android Handler 源码的正确姿势