Android Handler机制,如何学习阅读Handler源码,最简单的方式一步一步开始了解其原理

相信关于解释Handler的运行机制和源码分析的文章有很多,但可能大部分文章虽然写得不错,但如果对于新手想了解Handler的人群, 来说就算看了也很难理解。 自己以前曾很多次尝试阅读Handler的源码,但发现读着读着就越来越绕的感觉,觉得很大原因就在于阅读源码的顺序不对,以及容易被无关代码带偏了,在这里记录下如何学习阅读Handler源码。

 

  • 阅读源码前,首先要知道Handler的使用方式

     相信大家都知道Handler是用于线程间消息通信的,使用方法也比较简单,主线程声明一个Handler,重写其handleMessage方法接收Message消息,然后子线程创建Message对象包装发送的数据,然后发送给handler 。

下面是最简单的Handler使用 :

public class MainActivity extends AppCompatActivity {

    //定义一个Handler接收子线程消息
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            //这里拿到接收到的Message
            Log.d("TAG", "--msg-- obj:" + msg.obj.toString() + " what:" + msg.what);

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //启用子线程给主线程发送一条消息
        new Thread(new Runnable() {
            @Override
            public void run() {

                //创建一个Message包装数据,发送给Handler
                Message message = Message.obtain();
                message.obj = "子线程的消息";
                message.what = 1;
                handler.sendMessage(message);

            }
        }).start();
   
    }
}

 

 

  • 然后更深一层的去了解Handler是怎样将子线程的数据发送给主线程的,也就是常说的Handler机制

Handler机制应该是面试经常问到的一部分,想当年刚入行面试的时候被问到这个问题,都是把下面这张图用文字描述下,如果面试官不继续深入问的话都基本可以蒙混过关了。

Android Handler机制,如何学习阅读Handler源码,最简单的方式一步一步开始了解其原理_第1张图片

这是网上找的一个Handler机制图,可以看到它的运行机制有熟悉的Handler和Message对象,也有代码中没看到过的 Looper和MessageQueue对象。因此整个Handler运行,涉及到比较重要的几个类就是:

Handler  Looper MessageQueue Message  以及还有一个比较关键的角色  ThreadLocal  

Message  :可以理解为一个JavaBean类,封装了消息的各种数据参数

MessageQueue :消息队列,按照链表结构来存放 Message 

Looper :每个线程只能拥有一个Looper ,且每个Looper持有一个MessageQueue ,通过自身的loop()方法负责不断的取出 MessageQueue Message 数据并发送给 HandlerhandleMessage() 方法处理。

Handler  :负责将 Message 插入到Handler对象所在线程的MessageQueue ,以及接收Looper回传来的消息

ThreadLocal :线程内部的存储类,类似HashMap一样通过Key-Value存储数据,只不过当前线程存储的数据在另一个线程是取不到的,数据是每个线程独立的。

简单概括一下这个流程图, 就是主线程当创建一个Handler对象,默认绑定了一个Looper 对象,Looper对象拥有一个MessageQueue负责存放Handler的sendMessage()方法发送过来的数据,通过Looper 的 loop()方法不断地从MessageQueue取出Message交给Handler的handleMessage()进行处理。

 

 

  • 带着问题深入探究源码,第一步要先了解主线程的 Looper 是什么时候创建的?

一个APP启动首先是先走 ActivityThread.java 的 main方法,ActivityThread就是相当于我们所说的主线程或UI线程,但实际上它并不是继承Thread。来看程序从一开始运行都做了哪些有关Handler相关的东西。下面是源码部分流程图:

Android Handler机制,如何学习阅读Handler源码,最简单的方式一步一步开始了解其原理_第2张图片

可以看到分析的第一波就这么简单几行代码。

首先在main方法中,我们可以看到最先调用的与Handler有关的方法是 Looper.prepareMainLooper() ,  跟踪代码的流程可看到调用链 prepareMainLooper() --> prepare(false) -->  sThreadLocal.set( new Looper(quitAllowed) ) ;

                                                               ---> sMainLooper = myLooper()  -->  sThreadLocal.get()  ;

 

上面分析得出 App 一启动时则在主线程中创建了一个新的 Looper 对象 。 这里Looper 使用到了 ThreadLocal 作为数据存储类,存储的是当前线程对应的Looper对象上面解释了这个ThreadLocal 这个类是类似Hashmap Key-Value的方式进行存储的,而且数据是线程独立的。 ThreadLocal.set(value)方法默认使用当前线程作为Key存储valueThreadLocal.get()方法默认取出的是Key为当前线程的value ,如果当前线程没有数据取出则为null .


Looper 声明了一个全局唯一的ThreadLocal  集合,以及声明了一个保存主线程Looper的变量sMainLooper 。 因此Main方法中 Looper.prepareMainLooper() 最后干的就是创建一个Looper,  绑定到主线程并存放到集合中,接着根据当前线程取出并把这个Looper赋值给 sMainLooper 。

 

 

  • 继续再看 Handler 是怎样与主线线程的Looper之间进行关联的?

理解了上面所说的Looper是程序一启动就已经在主线程存在后,再来分析下我们上面一开始写的代码中的 new Handler() 对象时,其构造方法都做了什么事。

Android Handler机制,如何学习阅读Handler源码,最简单的方式一步一步开始了解其原理_第3张图片

上面调用链看到Handler构造方法里,调用了 Looper.myLooper() ,  再调用了 sThreadLocal.get() 查找当前线程有没有Looper , 由于当前Handler 是在MainActivity主线程创建的, Looper.myLooper() 拿到的也是主线程的Looper引用,这样就了解了Handler和Looper之间是如何关联的了。

分析到这里后,也大概就知道了为什么子线程不能直接创建Handler了吧

看了上面Looper 和 Handler 的源码后,我们知道Looper中的ThreadLocal集合里只保存了一个主线程的Looper对象;如果Handler是在子线程创建的,它构造方法的Looper.myLooper() 取出的也就是子线程的Looper对象,但子线程默认是没有Looper所以拿到就为null,Handler构造方法里就对取出的mLooper判空操作,如果null就直接丢个异常。这就是为啥不能直接在子线程创建Handler的原因了。

 

 

  • 最后研究 Handler 如何发送消息以及接收消息的

我们都知道,Handler是通过重写它的handleMessage(Message msg)方法接收消息的; 其他线程通过拿到Handler的引用调用它的sendMessage()方法发送消息的。 

从上面第二部分的Handler机制流程图可以看到,Handler 的sendMessage()发送消息是直接把消息发送到Looper内部的消息队列MessageQueue , Looper通过 loop() 方法把队列中的消息取出返回给 Handler 的 handleMessage(Message msg)方法。

下面截取了Handler 的sendMessage() 方法跟踪源码的关键部分,首先研究下Handler 是如何发送消息的:

Android Handler机制,如何学习阅读Handler源码,最简单的方式一步一步开始了解其原理_第4张图片

 

虽然这流程图看上去有点复杂,但认真看下其实是非常简单的。我们从 MainActivity 子线程调用 handler.sendMessage() 方法开始,跟踪进去发现Handler最后调用了是MessageQueue的 enqueueMessage(Message msg, long when)  方法 ,并把 msg 保存到MessageQueue的一个全局变量mMessages中 。

然后继续看Handler重写的handleMessage(Message msg) 是如何接收到消息的

Android Handler机制,如何学习阅读Handler源码,最简单的方式一步一步开始了解其原理_第5张图片

从第二部分的Handler机制图可以知道Looper是通过loop()方法不断的取消息出来,而App启动时则会在ActivityThread中 调用Looper.loop()不断的取出数据。可以跟踪到 loop() 方法可以看到有个死循环一直在调用MessageQueue.next()取出数据,然后将取出的数据通过dispatchMessage(msg) 回调 Handler 的 handleMessage (Message msg) 方法,因此子类通过重写这个方法则能拿到传过来的Message方法。

 

以上就是Handler机制大概的一个流程分析,这里我尽量站在刚接触Handler源码的人员角度来梳理分析源码的顺序,尽量以最简单的方式解释,所以很多深入的部分如Looper那些死循环为啥不会导致主线程卡死以及本地方法的epoll机制那些省略了。

 

你可能感兴趣的:(Android,android)