Android Handler 源码及原理分析

参考资料:

ThreadLocal:https://www.cnblogs.com/coshaho/p/5127135.html

Handler:https://www.jianshu.com/p/3d8f7ec1017a


0.前绪

通过Handler发送的消息,会先存到一个消息列表中,就是MessageQueue这个消息列表里。Looper这个类会循环不停地从这个消息列表中按顺序取消息,取到消息后,进行消息处理(使用handleMessage方法)。很简单一个原理,实现了消息的顺序处理。

Handler涉及类包括:Handler.java、Looper.java、MessageQueue.java、ThreadLocal.java、ThreadLocalMap.java

1.源码分析

1.创建Handler

handler翻译中文是操作者,顾名思义,Handler的作用就是用来处理消息的。

Handler mHandler = new Handler();

Handler mHandler = new handler() {

    public void handleMessage(Message msg){
        // handle message
    }

};

最常见的两种创建方式,我们常会在Activity中这样创建使用,一般用于在主线程处理任务。

看构造方法的源码,最后会跑这个方法:

Handler.java

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        // ------注意此行------
        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;
    }

第11行,通过 Looper.myLooper() 获取到了Looper。

注意,此代码高能,信息量巨大,下面,进入这行代码!:

Looper.java

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

就一行代码,你以为我骗人?no,no,no。

扫描二维码关注公众号,回复: 17283469 查看本文章

这个方法是Looper类中的一个静态方法,这行代码中包含了重要角色: ThredLocal

这可是Looper线程绑定的精髓(严肃脸?)。

啥是线程绑定???

就是一个线程只有一个Looper对象

而用的消息队列呢,是Looper的一个内部变量,所以,一个线程也只有一个消息队列。

2.引入Looper

怎么突然从Handler的话题转移到了Looper?

我们之前说过,Looper会循环不停地从消息队列里按顺序取消息。而这个消息队列就是Looper的小弟(一个内部变量),拿到这个Looper,我们就可以往它的消息队列里放消息啦。

看看我们平时是怎么用Handler发消息的:

Message msg = Message.obtain();
msg.what = args1;
msg.obj = args2;
mHander.sendMessage(msg);

其实这个sendMessage方法就是起到把消息扔给消息队列的作用。这个Looper很重要吧。

先看看Looper代码中的 sThreadLocal在哪定义的

Looper.java
 
   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

泛型参数是Looper的一个ThreadLocal静态对象。

回去看 sThreadLocal.get() 方法,这是ThreadLocal的一个方法

ThreadLocal.java
    
public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

分析一波上面代码:

先获取到了当前运行的线程

Thread t = Thread.currentThread();

通过getMap传入当前线程,获取到一个ThreadLocalMap类型的Map。

ThreadLocalMap又是啥?

从命名来理解,就是一个存ThreadLocal的Map啦,Map就是使用key-value存储形式的数据结构啦。

通过map.getEntry传入这个ThreadLocal对象获取到Entry(保存一对key-value的一个实体类),通过这个Entry的value拿到了Looper。

ThreadLocal.java

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

getMap 也是一行代码,够简单的。

它返回了线程的一个变量??

没错,我们平时创建线程用的Thread类,它就是有这么个变量。

3.引入ThreadLocalMap

Thread.java

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

注释说(个人翻译的,不知道准不准确,我就假设准备?):ThreadLocal的values值与此线程有关,这个Map由ThreadLocal来维护。

从翻译知道:Thread告诉我们,这个Map是跟我有关的,但是我不管了!让ThreadLocal自己维护!

what?搞什么飞机,ThreadLocal怎么维护???这可是你自己的东西。

好吧,我们看看ThreadLocal怎么维护。先看看ThreadLocal里的set方法

ThreadLocal.java

    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对象拿到当前线程,然后,

还记得getMap方法吗

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}

通过getMap方法拿到这个线程的 ThreadLocalMap ,如果map不是null,就把自己(ThreadLocal对象)作为key, 传入的value作为value,这对key-value放到map里。如果map是null,就创建一个新的Map,然后把key-value放进去。

对我们Looper来说,value值就是Looper对象。

Thread真够懒的,自己的变量自己不维护,让ThreadLocal自己用set、get方法搞定了。

当然了,ThreadLocal处理的线程那也是有要求的:当前运行的线程。即,谁搞我(调用我的方法),我搞谁。

通过上面一系列麻烦的操作,Looper成功地让和当前线程进行了绑定处理:让当前线程的ThreadLocalMap中含有了键值对:

------------------------------------------------

key                                                       -    value      |

ThreadLocal(Looper的内部变量) -   Looper    |

------------------------------------------------

等等,好像哪里不对,创建Handler的时候,Looper根本没有让ThreadLocal运行过set方法啊?

没有绑定过线程啊,Looper.myLooper() 不就拿不到Looper吗?

其实,主线程的Looper,在Android环境初始化的时候已经创建好咯。

Looper.java

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

请看prepareMainLooper方法的注释:

应用程序的Looper是由Android环境创建的,因此您无需自己调用此函数。

至此就分析完辽。

划重点总结:

Thread有一个自己不想管的Map叫ThreadLocalMap,它的这个ThreadLocalMap是让ThreadLocal来管(Thread在放权啊?)。

ThreadLocal他就把自己作为键,把想保存的东西作为值,保存了到了调用他的线程的Map中。这样,键和值就和当前线程建立了关系。

课后习题:

同一个Activity主线程中可以创建多个Handler吗?

答案:当然可以,同一个Activity中的Handler们,拿到的都是主线程的Looper喔,用的同一个消息队列。

附(旧的总结):

总结0.ThreadLocal和Thread的关系

每个Thread中都有一个存储ThreadLocal的Map对象,这个Map不是使用的集合类中的Map,用的是一个叫做“ThreadLocalMap”(从名字可以明显看出来它的作用了)的类来存储。ThreadLocalMap是一个可以存储key-value的map,key就是ThreadLocal,value就是要存储的跟ThreadLocal对应的值。

这里的ThreadLocalMap是ThreadLocal的“基友类”。

下图看下两者关系

总结1.线程绑定

问起Handler的原理,会提起Looper和MessageQueue跟线程绑定,balabala。

这里实现线程绑定就是使用的Thread中的ThreadLocalMap来实现。

如何使用ThreadLocal呢。首先需要一个Manager类来持有一个ThreadLocal<T>的实例。不同的Thread将这个实例和想存储的值保存起来就好咯。ThreadLocal提供了set、get等等方法来方便获取或者保存当前运行线程(Thread.currentThread()可以获取当前线程)对应的存储值。这样为了不同的功能,我们可以定义不同的ThreadLocal,而且互不干扰。

线程绑定,直接写成Map<Thread, value>不就可以实现吗,为什么要写得如此复杂。

这种存储结构的好处:

1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。

2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。

摘自:https://www.cnblogs.com/coshaho/p/5127135.html

2.Handler

 Hander有一个相关类为Looper,这个就是上面所说的Manager类。

 Looper有几个属性值:

// 存储Looper的ThreadLocal,可以看出Looper不仅充当Manager,还作为基础数据类(存储MessageQueue等信息)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

// 主线程的Looper
private static Looper sMainLooper;

// Looper对应的MessageQueue
final MessageQueue mQueue;

可以看出Looper不仅充当Manager,还作为基础数据类(存储MessageQueue等信息),通过sThreadLocal,实现了线程和Looper的绑定。

Looper有个重要方法:

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

由此,大部分Looper的代码基本上可以知道是什么意思了。

猜你喜欢

转载自blog.csdn.net/u012218652/article/details/90903606