美文网首页
15.源码阅读(安卓消息机制)

15.源码阅读(安卓消息机制)

作者: 任振铭 | 来源:发表于2018-04-30 08:34 被阅读5次

根据消息机制,手写了一个简单的handler模型,便于理解消息机制的原理,源码在GitHub:https://github.com/renzhenming/MyHandler.git

Handler用于处理线程间通信,当我们需要在子线程更新UI时,通常是通过handler发送一个消息到主线程中,在主线程更新,代码如下:

//主线程创建Handler
private Handler mHander = new Handler(){
    public void handleMessage(Message msg) {
        mTextView.setText((String) msg.obj);
    };
};

//子线程发送消息
new Thread(){
    public void run() {
        Message message = new Message();
        message.obj = "哈哈哈";
        mHander.sendMessage(message);
    };  
}.start();

那么这么一个过程是如何从sendMessage执行到handleMessage的,在看源码过程中,我们带着一个问题:
1)为什么Can't create handler inside thread that has not called Looper.prepare()这个异常会发生

从sendMessage开始看源码,有这么一个方法,uptimeMillis是将要发送消息的时间,这个时间是当前时间加上延迟的时间,这里可以看到获取了一个已经存在的MessageQueue,然后执行enqueueMessage把message加入队列中,我们看看这个MessageQueue是何时被创建的

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        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);
    }

在发送消息之前,会首先创建Handler,那么我们看看Handler创建的额时候做了什么,找到Handler的构造方法

public Handler(Callback callback, boolean async) {
        //针对handler如果没有设置为静态容易发生的内存泄漏进行log提醒
        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) {
            //这里找到了我们最初提出的那个问题,为什么会报
            //这个错误,可以看出来,答案会在获取不到looper的情况下,
            //所以我们需要重点看看myLooper做了什么
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

从ThreadLocal中获取looper ,
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

 /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

从当前线程中获取到ThreadLocalMap 对象,然后从这个map中得到Looper对象,既然如此,如果可以从map中取到looper,那么之前可定有个将looper存入这个集合的过程,从上边的分析可以看出,如果取出的looper为null,也就是并没有将这个looper存入的话,就会抛出Can't create handler inside thread that has not called Looper.prepare()这个异常,并且提到了Looper.prepare方法,那么是不是在这个方法调用的时候,looper被保存的呢,这是一个猜想,我们可以看看源码

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

看到这个prepare方法,方法执行获取到当前的线程对象,从线程对象获取到ThreadLocalMap对象,然后把Looper加入了这个线程中

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

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

我们知道,一个线程中只有一个Looper,并且,上边说的这个异常也只有在子线程中创建Handler会发生,那么为什么在主线程创建可以不用调用Looper.prepare方法呢,这个要从应用启动说起,我们找到app启动的入口,ActivityThread类中的main方法,这两行代码是不是比较熟悉

public static void main(String[] args) {
        ......
        Looper.prepareMainLooper();
        ......
        Looper.loop();
        ......
    }
public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
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));
    }

可以看到,其实在程序启动的时候,在主线程中就已经实例化了一个Looper了,这也就是为什么我们在主线程中创建handler不需要调用Looper.prepare的原因

然后我们再回到sendMessage的时候已经存在的那个MessageQueue的问题,它是何时创建的,刚才看到,当应用程序启动,会在主线程中实例化一个looper对象,而当Looper被实例化的时候,消息队列其实已经被同时实例化了

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

也就是说,程序启动后已经完成了这两步,第一,在主线程实例化了一个Looper对象,并且只有这一个Looper对象,looper实例化,主线程中唯一的消息队列也同时实例化了,这个MessageQueue也就是启动后就已经存在了
继续从这个方法往下看

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        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(MessageQueue queue, Message msg, long uptimeMillis) {
        //这个消息即将被加入消息队列,这个消息同时持有了
        //发送这个消息的handler的引用,谁发送它,它就持有谁,
        //target就是handler对象
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
boolean enqueueMessage(Message msg, long when) {
        ......
        synchronized (this) {
            ......
            msg.markInUse();
            //message将要被发送的时间点
            msg.when = when;
            //第一次过来,mMessages是null
            Message p = mMessages;
            boolean needWake;
            //这里会进行一些判断p=null表示是发送的第一条消息,
            //when=0表示是这条消息不要延迟,
            //when<p.when表示当第不是第一条消息时,比较这条消息的发送时间
            //和上一条消息的发送时间,比上一条时间早
            //满足上边三个条件,就走入下边的代码
            if (p == null || when == 0 || when < p.when) {
                //当地一条消息进来时,由于满足条件p=null,所以就设置
                //这条消息的next,就是链表中下一条message为null
                //假设没有第二条消息发送过来的话,这就是消息队列
                //结束的标志,然后将第一条消息赋值给mMessages 
                //如果有下一条消息过来,这个mMessage就拿去和它做比较
                //这行代码的意思就是如果下一条消息的时间早于上
                //一条消息,就把上一条消息作为下一条消息的next存储
                //把时间早的放在链表的前边
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

经过上边一系列的条件操作之后,会把所有发送的消息存放再链表中,并且按发送时间来排序,发送时间早的排在前边,发送晚的排在后边,到了这里,sendMessage的逻辑也就进行完了,它的作用只是将消息按照顺序放入队列而已,那么是什么时候从队列中取出消息回调到handleMessage中的呢?在子线程创建handler Looper.prepare和Looper.loop都是一块出现的,那么答案可能是在Looper.loop方法中

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        //获取到 当前线程的looper对象
        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 (;;) {
            //通过一个死循环不断的从消息队列中取出下一个消息,然后执行
            //发送这个消息的handler的handleMessage方法回调过去,到这里终于
            //消息被收到了
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ......

            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
           .......

            msg.recycleUnchecked();
        }
    }

主线程中同样是有loop方法的执行的,可以看到,在程序启动后,就有loop方法的调用,开启了一个无限循环不断的读取消息队列中的消息

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

总结一下消息机制的几点关键:
1)在一个线程(主线程或子线程)中创建Handler,第一步需要执行Looper.prepare,目的是初始化当前线程唯一的Looper对象和消息队列MessageQueue
2)当从当前线程send Message发送消息时,是将这个消息按照时间顺序加入消息队列
3)消息进入消息队列,要从队列中取出,需要执行Looper.loop方法,这是一个死循环,会一直执行,只要消息队列中存在消息,就将消息取出,发送到当前handler的handleMessage方法中

相关文章

网友评论

      本文标题:15.源码阅读(安卓消息机制)

      本文链接:https://www.haomeiwen.com/subject/vnualftx.html