Handler源码分析原理

来源:互联网 发布:沈阳数据恢复 编辑:程序博客网 时间:2024/05/16 14:55

Handler产生的原因:
异步通信
因为Android只有在主线程才能进行UI操作(一般情况),而有很多情况下需要子线程去获取数据。由于俩个线程之间的不同步,所以子线程在获取数据后需要通知给主线程去改变UI;
那么怎么通知呢?即如何让主线程去调起改变UI的方法,而不是让子线程去调呢?(线程是抽象的概念,方法被哪个线程调起,就属于哪个线程,和代码中方法所写的位置无关,但是变量却是线程间共享的)

Handler从自我实现分析:

如下假设:A线程开启一个死循环,判断Boolean变量key是否为true,如果是true,执行某一方法;B线程可以在耗时操作结束后置key值为true;此时A检测到key为true,执行方法。

这样就实现了线程间的一个简单同步。

这里写图片描述

从Handler的使用中可以看出,Handler不仅仅是一个简单的通知,他还涉及到数据的传递。所以不能简单的用上述一个boolean判断来实现。但是思想是却是一致的。

Android中采用了MessageQueue作为这一Flag,即一旦消息队列中的消息不为空,主线程中已开启的死循环就取出消息,然后执行。这样就实现了从子线程到主线程的跨越。

这里写图片描述

但是很快问题就出现了,MessageQueue作为一个变量,是所有线程都能访问的。假如我们开启了另一个子线程,像主线程一样开启相同的死循环获取message,MessageQueue就会失去效果:

这里写图片描述

所以为了通用,我们必须实现一个线程一个MessageQueue副本,这样各自线程开启的死循环只会从各自的MessageQueue副本里拿,不会产生混乱,同时往MessageQueue里添加消息时,也要根据线程有选择的添加。ThreadLocal正好可以简单的实现不同线程不同数据副本的效果。(这里不深入ThreadLocal的实现)
最终的模型如下:

这里写图片描述
这样就实现了真正意义上的跨线程访问,而不局限与子线程与主线程。

Handler原理源码分析:

分析handler源码之前,离不开先分析Looper,所以先从looper的分析入手:

App主线程在启动时,首先会执行looper.parpare,源码如下:

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));}private Looper(boolean quitAllowed) {    mQueue = new MessageQueue(quitAllowed);    mThread = Thread.currentThread();}

可以看到,Looper初始化时新建了一个成员变量MessageQueue,然后连同messageQueue绑定给了ThreadLocal。下面看看ThreadLocal.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把当前线程与该Looper对象关联在了一起,即,ThreadLocal把线程与其对应的MessageQueue做了关联,上述模型实现了第一步。

主线程在执行完Looper.parpare后,会执行Looper.loop,源码如下,过滤了部分代码:

    public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final MessageQueue queue = me.mQueue;        ...        for (;;) {            Message msg = queue.next();             if (msg == null) {                return;            }            ...            }            ...                   msg.target.dispatchMessage(msg);            ...            msg.recycleUnchecked();        }    }

Looper.loop就是把之前parpare的MessageQueue进行了死循环,不断取出其消息,执行message.target.dispatchMessage(msg)方法!

至此,模型的后半部分(线程拥有各自的MessageQueue,并死循环取出执行)就完成了。现在只有俩个疑问,sendMessage是谁完成的?以及message.tartget又是谁?

这个时候,Handler终于登场了。首先看一下Handler初始化的源码:

    public Handler(Callback callback, boolean async) {        ...        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

可以看到,Handler拿到了一个Looper,换句话说,Handler拿到了一个MessageQueue,离sendMessage只差一步了!

但是根据模型,这个MessageQueue属于哪个线程的呢?换句话说,我们是往哪个线程sendMessage呢?

源码中Looper是通过myLooper得到的,看一下他的源码:

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

ThreadLocal又出现了,它的作用是取出对应线程的数据副本,这里就是取出对应线程的Looper(MessageQueue),分析一下它get()方法的源码:

    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();    }

到这里一目了然,ThreadLocal取的就是当前线程之前所关联的Looper,也就是Handler初始化时所在线程的Looper!

所以Handler在初始化时,会获取Handler所在线程的Looper,同时获取该线程的MessageQueue。

最后一步,Handler.sendMessage(),sendMessage()最终会走到enqueueMessage()方法上:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

这里,msg.target终于有了赋值,就是Handler自己!

接着往下看,同样只保留了关键代码:

    boolean enqueueMessage(Message msg, long when) {        ...        synchronized (this) {            ...            msg.markInUse();            msg.when = when;            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                            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;            }            ...        }        return true;    }

这部分仍然运行在子线程,通过sendMessage(msg)方法,给msg指定了其target为Handler本身,并将此msg加入到了messageQueue里。

所以:

  1. handler.sendMessage()可以简单理解为 为Handler所在线程的MessageQueue添加Msg。

  2. handler.sendMessage()运行在线程B,只是添加msg数据。真正操作msg数据的方法,运行在Handler所在线程A里,以此实现跨线程。

Handler原理简化模型:

这里写图片描述

最后需要说明一下,只有主线程默认执行了Loop.parpare()和Looper.loop(),其它线程想要跨线程需要手动执行。

原创粉丝点击