美文网首页
Android消息机制源码解析

Android消息机制源码解析

作者: 蜗牛的独奏1 | 来源:发表于2018-03-09 17:02 被阅读0次

    概述

    Android 的消息机制主要是指 Handler 的运行机制。Android 规定只有主线程可以访问 UI ,子线程中无法访问 UI。但是主线程中不建议进行耗时操作,因为这会引起 ANR。
    系统为什么不允许子线程中访问 UI?
    如果多线程并发访问,UI 控件处于不可控制的状态。如果对 UI 控件的访问上锁,首先上锁机制会让 UI 访问的逻辑变得复杂;其次会降低 UI 的访问效率,因为锁机制会阻塞某些线程的执行。所以,采用单线程模型来处理 UI 操作简单高效。

    消息机制解决了我们需要从服务器获取数据后操作 UI 的矛盾。但是更新 UI 仅仅是 Handler 的一个使用场景。本质上来说,Handler 并不是专门用于更新 UI 的,它只是在更新 UI 的场景中用的比较多。Handler 的使用过程很简单,通过它可以轻松的将一个任务切换到 Handler 所在的线程中去执行。Handler 的运行需要底层的 MessageQueue 和 Looper 的支撑。

    Android消息机制有哪些核心成员组成的呢?

    • MessageQueue:
      消息队列。 它的内部存储了一组消息,以队列的形式对外提供插入和删除的操作。但是内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。
    • Looper:
      消息循环。以无限循环的形式去查询是否有新消息,如果有的话就处理消息,没有就一直等待。
    • Handler:
      消息传递的主要操作者。将消息从一个线程传递到另外一个线程。
    • ThreadLocal:
      不是线程,它的作用是可以在每个线程中存储数据。
      Handler 创建的时候会采用当前线程的 Looper 来构造消息循环系统,Handler 内部要使用 ThreadLocal 获取当前线程的 Looper。ThreadLocal 可以在不同的线程中互不干扰的存储并提供数据,通过 ThreadLocal 可以轻松获取每个线程的 Looper。
      注意:线程默认是没有 Looper 的,如果需要使用 Handler 必须为线程创建 Looper。主线程(UI线程 ActivityThread)被创建时就会初始化 Looper,这也是主线程中默认可以使用 Handler 的原因。

    Handler分析

    Handler 是Android消息机制的上层类,也是Android消息机制中我们接触最多的类。使用消息机制的时候,我们都是通过 Handler 的 post 和 send 系列方法开始的,我们从这里开始分析,借助源码,探索Android消息机制的底层原理。我们先看 post 系列方法源码:

    public final boolean post(Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    public final boolean postAtTime(Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }
    
    public final boolean postDelayed(Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    

    我们再看 send 系列方法的源码:

    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean sendEmptyMessage(int what){
        return sendEmptyMessageDelayed(what, 0);
    }
    
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    
    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    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);
    }
    

    画个简易的图,理清这些方法之间的关系。

    image

    图中可以发现:post 系列和 send 系列的方法最终都指向了 sendMessageAtTime()方法。也就是说,send 系列的方法最终都是靠 sendMessageAtTime()方法实现的。我们看下 sendMessageAtTime 方法的实现:首先获取一个 MessageQueue,然后传给 enqueueMessage 方法,调用此方法。下面是 enqueueMessage 方法源码:

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

    可以看到,最后执行的是 MessageQueue 类的 enqueueMessage 方法。我们知道 MessageQueue有两个很重要的方法:enqueueMessage 和 next。enqueueMessage 负责往消息队列里添加数据,next 负责从消息队列里取出数据。所以,handler post 和send 系列的方法做的事情就是往 MessageQueue 里面添加数据。

    现在我们知道数据是怎么添加到消息队列里面去的了,这是消息传递的第一步,那第二步就是把消息取出来进行处理,虽然处理消息依然是 handler 的事,但把消息取出来却是 Looper 默默一直干的事。我们分析下 Looper。

    Looper

    Looper 是让我们整个消息机制循环起来的核心类。普通的线程是没有消息队列的,也是无法使用 Handler 的(主线程:ActivityThread 被创建的时候默认初始化 Looper ,这也是我们可以直接在主线程使用 Handler 的原因)。正是借助 Looper 让线程成为 Looper 线程,线程和 Looper 绑定后,也就有了消息队列,因为消息队列 (MessageQueue) 是放在 Looper 类里面的。那么我们怎么做可以让普通线程变成可以使用消息机制的线程呢?很简单,使用 Looper 类的两个静态方法:

    • Looper.prepare()
    • Looper.loop()

    这两个方法具体做了什么呢?我们看下源码:

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

    prepare 方法很简单,里面就做了一件事,就是给变量 sThreadLocal 设置值。sThreadLocal 变量是一个 ThreadLocal 类型的变量:

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

    如果对 ThreadLocal 不太了解可以先看下这篇文章 Android消息机制-ThreadLocal。我们知道,ThreadLocal 的作用就是保证使用 ThreadLocal 存储的数据只属于当前线程,其他线程无法获取。sThreadLocal 泛型是 Looper,所以这个变量会存储一个 Looper 对象。prepare() 方法是调用了 sThreadLocal 的 set 方法存储了一个新建的 Looper 对象。存储之前会判断是否已经有了 Looper 对象,如果已经有了,会抛异常。所以,一个线程中只能有一个 Looper 对象。这样就保证了这个 Looper 对象只属于当前线程,而且只有一个 Looper 对象。我们看看 new Looper(quitAllowed)这步都干了什么:

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

    可以看到,首先,构造方法是私有的,就是在别的类中不能实例化 Looper 对象。方法中就是给两个变量赋值,一个是我们的消息队列 mQueue,一个是线程对象 mThread。此时,我们的 Looper 对象有了消息队列,而且获取到了当前线程。然后看下 loop() 方法:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();//1、获取looper对象
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;//2、获取消息队列
    
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
    
        for (;;) {
            Message msg = queue.next(); // 取出队列中的消息
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
    
            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
    
            final long traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);//消息不为空执行此处
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
    
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
    
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }
    
            msg.recycleUnchecked();
        }
    }
    

    这里就出现了我们一直想寻找的东西啦。首先 1 处通过 myLooper()方法获取到存储的 looper 对象:

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

    然后 2 处通过获取到的 Looper 对象获取到对象中的消息队列。获取到消息队列后,通过一个不设参数的 for 循环方法不断取出消息,如果消息不为空,就执行:

    msg.target.dispatchMessage(msg);
    

    msg我们都知道是 Message,那么 msg.target 是什么呢?哈哈,还记得我们前面分析的 Handler 把消息存入消息队列的过程吗,Handler 的存储方法:enqueueMessage 方法:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//这里把handler对象赋给了target
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    所以,这个 target 就是我们的 handler 对象,next 方法取出消息后就调用了我们 handler 对象的 dispatchMessage() 方法。这里就是我们能不断处理消息的关键: Looper 对象一直在幕后不断的取出消息给我们的 handler 对象,然后由 handler 对象去处理消息。

    现在我们已经清楚了消息队列是什么时候构造的,消息是什么时候存入队列的,消息是怎么取出的,还差一步,我们看看 handler 是怎么处理消息的,就是我们上面取出消息后执行的方法:dispatchMessage

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    首先判断 msg 的 callback 是否为空,callback 是什么呢?是我们调用 post(Runnable r) 方法传入的 runnable 对象:

    public final boolean post(Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    

    不为空的时候用的是 post 系列方法,如果为空则用的是 send 系列方法。第二步,判断 mCallback 是否为空,mCallback 又是什么呢?

    final Callback mCallback;
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    

    看到这里,大家应该明白了吧,终于看到我们熟悉的处理消息的方法了:handleMessage。mCallback 是否为空对应的是 Handler 类的不同构造方法。

    public Handler() {
        this(null, false);
    }
    public Handler(Callback callback) {
        this(callback, false);
    }
    

    我们在创建 handler 对象的时候,可以直接传入一个匿名内部类去实现 handleMessage 方法;也可以构造方法不传参,然后去实现 handlerMessage 方法。所以,在 dispatchMessage 方法中,判断如果 mCallback 为空的话,执行 handleMessage 方法。这样,我们就走到了 handleMessage 方法,就可以按照我们的业务逻辑去处理消息了。

    最后画一张图总结一下整个流程:


    image

    相关文章

      网友评论

          本文标题:Android消息机制源码解析

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