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

Android 消息机制源码解读

作者: 梦飞成2012 | 来源:发表于2017-12-06 21:05 被阅读6次

    Android消息机制,对每个开发来说,都不可避免的和它打交道,比如在子线程中更新UI、倒计时功能等等都需要使用到消息。
    从开发的角度来讲,Handler是Android消息机制的上层接口,我们只需要和Handler打交道就足够了。
    Handler的使用:

    // 处理消息
    Handler handler = new Handler(){
                @overider
                public void handleMessage(Message msg) {
                        // do somethings you want to do!
                        super.handleMessage(msg);
                }
    } 
    
    // 发送消息
    Message msg = Message.obtain();
    msg.seData(bundle);
    msg.setTarget(mHandler);
    msg.sendToTarget();
    // 或者使用mHandler.sendMessage(msg);  
    

    Handler的使用很简单,我们就不在此举例赘述了。但是想更娴熟的使用Handler,驾驭消息机制,我们还需要从源码层面来讲整个机制的运作,读懂Handler和Looper、MessageQueue、Message的关系。
    请看下图:

    消息机制的闭环.png

    下面我们从源码一步步的分析消息运行机制:

    一、Handler:

    1.1Handler的构造方法

    handler的构造方法如下:

     public Handler() {
            this(null, false);
        }
    
      
        public Handler(Callback callback) {
            this(callback, false);
        }
    
        public Handler(Looper looper) {
            this(looper, null, false);
        }
    
    
        public Handler(Looper looper, Callback callback) {
            this(looper, callback, false);
        }
    
    
        public Handler(boolean async) {
            this(null, async);
        }
    
        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 that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
        public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    从Handler的构造方法,我们可能看出,Handler里面保存了Looper、MessageQueue的引用。

    1.2消息的发送

    发送消息有两种方式:

    • 1、msg.sendToTarget();
    • 2、mHandler.sendMessage(msg);

    先看1方式:

    public void sendToTarget() {
        target.sendMessage(this);
    }
    

    最终还是调用的handler的sendMessage方法。
    再看2方式都干了什么事情。

    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }
    
    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);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //将要发送的Message绑上该Handler的标签
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //将消息将入Handler构造函数中初始化的MessageQueue队列
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    可以看到sendMessage就是做了一个入队列的操作(即enqueueMessage)
    消息发送完成 ,接下来我们看看消息是如何取出来的,再看这个之前,我们先看看Looper的相关设置:

    二、Looper:

    Looper,在消息机制中扮演者消息处理角色,即从MessageQueue中不断的取消息,然后分配给对应的Handler去handleMessage。

    2.1 Looper的构造方法
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    

    这是一个私有的构造方法。我们可看到,在构造方法中,每个Looper都和一个MessageQueue绑定。

    Looper里面的主要引用如下:

    • static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    • private static Looper sMainLooper; // guarded by Looper.class
    • final MessageQueue mQueue;
    • final Thread mThread;
      这几个成员变量比较好理解,MessageQueue是Looper操作的消息队列,mThread是当前线程的对象,sMainLooper是主线程的Looper对象引用。
      sThreadLocal 这个对象比较特殊,主要作用将Looper和线程关联,这样外部就可以直接通过Looper拿到当前线程的Looper对象了,这样可以保证Handler创建的时候与当前线程的Looper关联。
      关于 ThreadLocal<T>的简介和用法可以看这篇文章Android 多线程系列----ThreadLocal的使用和原理
    2.2 Looper的获取

    Looper没有public的构造方法,只能使用Looper.myLooper来获取实例,在获取对象前需要调用Looper.prepare()方法。(注:在UI线程,ActivityThread已经帮忙做了这些事情了,所以我们创建Handler不需要操作Looper)
    下面看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");
        }
         //Looper类会创建一个新的Looper对象,并放入全局的sThreadLocal中。
        sThreadLocal.set(new Looper(quitAllowed));
    }
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
    
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }
    

    通过上面代码,我们可以得到以下结论:

    • 1、每个线程中,只能有一个Looper,因为多次调用Prepare会报异常
    • 2、每个线程也只要一个MessageQueue,一个线程中,所有的消息公用一个队里
    • 3、Looper.myLooper提供给外部实例,通过ThreadLocal实现的
    2.3Looper的loop,获取消息

    消息循环的启动,需要调用Looper的loop方法。
    这个方法的作用就是不断的从MessageQueue中获取消息,然后处理,我们先看看源码:

    /**
     * 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();
        //调用loop()方法之前必须在当前线程通过Looper.prepare()方法创建Looper对象实例。
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获取Looper实例的中的消息队列
        final MessageQueue queue = me.mQueue;
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        //开启循环队列-不断
        for (;;) {
            //从队列中取出消息
            Message msg = queue.next(); // might block
            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.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            //将消息分发给注册它的handler
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            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();
        }
    }
    

    通过上面方法,我们看到,loop方法启动了一个死循环,不断通过MessageQueue的next方法(该方法是阻塞的),不断获取消息,然后通过Message的target调用dispatchMessage分发处理消息,然后回收消息对象。

    三、消息的处理:

    Handler的dispatchMessage方法源码:

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    可以看到消息分发处理很简单:

    • 1、如果Message有callback则调用Message的Callback
    • 2、否则,如果Handler创建时候设置了Callback,调用Callback的handleMessage,如果返回true结束
    • 3、否则, 调用Handler的handleMessage方法,完成消息的处理。

    四、消息机制的闭环实现:

    消息机制实现,有一些前提,如下:

    • 1、线程直接内存是共享的(不同线程可以使用Hanler发消息,加入消息队列)

    • 2、每个线程只有一个Looper和MessageQueue

    • 3、Handler和Looper绑定,保证了不论哪个线程,Handler只会向一个指定对了发送消息,在取消息的时候,处理也就只会在当前Looper线程了。

    • 1、创建Looper和Message对象

    • 2、创建Handler对象,通过Looper.loop启动消息循环

    • 3、Handler在子线程发送消息,将消息加入到MessageQueue

    • 4、Looper在loop方法中(主线程),取出消息并处理,完成了消息在不同线程间的切换。

    消息机制的闭环可以如下总结:

    消息机制的闭环.png

    参考资料:

    1、又一年对Android消息机制(Handler&Looper)的思考
    2、Android消息机制的原理剖析—闭环总结
    3、Android消息机制(一):概述设计架构

    相关文章

      网友评论

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

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