Android中Handler源码分析

Android开发中,我们经常会遇到这样的一个场景:子线程获取数据,主线程将子线程获取到的数据进行UI更新,而Android本身提供的是Handler来处理,那handler是如何实现的呢?它的原理又是什么呢?
主要分下面几个部分来进行分析:
1.如果使用?
2.子线程发送的消息为什么在主线程收到?
3.简单介绍一下ThreadLocal。
开始进入正题:

1、如何使用

1.1新建一个Handler对象

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

1.2将获取到的数据(暂时定义为null)发送个handler进行UI更新。

 new Thread(new Runnable() {
            @Override
            public void run() {
                handler.sendMessage(null);
            }
        }).start();

第一部分就完了,也是最简单的如何使用。

2、子线程发送的消息为什么在主线程收到?

2.1 子线程发送消息

跟踪handler.sendMessage()方法,调用过程:
sendMessage() --> sendMessageDelayed() --> sendMessageAtTime() --> enqueueMessage

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        ...
        return queue.enqueueMessage(msg, uptimeMillis);
        ...
    }

由上述代码可以看出,发送过程已经结束,即将此过程称之为消息入队

2.2主线程接受消息

我们一定听过这样一句话,主线程有一个默认的Looper实例,用它来轮训消息队列。那它在哪实例化的呢?
在ActivityThread.java中,有一个main方法,它是应用程序的入口。

public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        ...
        Looper.loop();
        ...
    }

在这个方法中,有两条重要的语句
先看第一条,调用过程如下:
Looper.prepareMainLooper() --> prepare()
这个过程很简单,看一下prepare中都做了什么。

private static void prepare(boolean quitAllowed) {
        ...
        sThreadLocal.set(new Looper(quitAllowed));
    }

没错,在这个方法中实例化了Looper,并将其存储到ThreadLocal。
再看第二条,直接看looper的方法:

public static void loop() {
       final Looper me = myLooper();
       msg.target.dispatchMessage(msg);
}

loop()方法有两个作用,第一是从ThreadLocal中获取looper的实例;第二是从消息队列中取到消息之后进行消息分发,看一下dispatchMessage方法:

public void dispatchMessage(Message msg) {
     ...      
     handleMessage(msg);
}

OK,终于看到了Handler的消息回调方法,因为loop方法是在主线程中执行的,所以handleMessage也是在主线程进行回调的。

3、简单介绍一下ThreadLocal。

主要是为了保证java多线程并发中数据的隔离,简而言之,多个线程之间不会有共享的数据,所有数据都是线程私有的。
那ThreadLocal在Handler中是怎么体现的呢?
其中在实例化Looper对象的时候,调用了set方法,而在获取Looper对象的时候,调用了get方法,那我们分别看一下set和get都做了什么?
set

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

在ThreadLocal中set数据的时候,其实是在每个线程的ThreadLocalMap中写值。每个线程都有自己的ThreadLocalMap, 它的key为threadlocal对象,value为Looper实例。注意:网上好多人说key是thread对象,其实看源码不是的。
get

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

了解set的原理之后,get也就很简单了,就是从一个map中取值。
最后用一张很简单的图来总结一下:


handle.png

本人愚见,望起到抛砖引玉作用,欢迎各位指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容