美文网首页Android
Handler消息通信机制,原来如此简单

Handler消息通信机制,原来如此简单

作者: Dora_Liang | 来源:发表于2018-11-16 19:18 被阅读171次

    前言


    相信不管是工作还是面试,大家都会跟Handler打交道,而且Android许多地方,或多或少都会看见Handler的身影(AsyncTask,HandlerThread,IntentService等)。所以掌握Handler机制非常重要。该文主要是对Handler机制进行解析以及面试中经常的问题,对Handler的用法就不过多介绍。

    一、主要概念简述

    • 主线程(UI线程 ):应用第一次启动时,Android会在主进程中启动一条主线程(ActivityThread),用于处理UI相关事件。

    • Handler(消息发送者与处理者):负责发送Message到消息队列和处理Looper循环器分发出的消息,每条线程可以有多个Handler。

    • Looper(消息循环器):MessageQueue与Handler之间通信的桥梁,用于取出在Looper 的消息队列中循环的消息,然后将取出的消息分发给Handler。一个线程有且只有一个Looper。

    • ThreadLocal(线程内部数据存储类):用来获取或存储线程自身完全独立的副本(局部变量),进行数据隔离。

    • MessageQueue(消息队列):采用单链表数据结构来存储Handler发送过来消息,按照先进先出顺序执行。

    • Message(消息):可以用来存储与传递相关需要的信息的消息对象。

    具体它们间的通信关系,如图: Handler通信流程图.png

    二、源码解析

    Handler源码分析

    1.构造方法

    虽然Handler的有多个构造方法,但是都离不开

    public Handler(Callback callback, boolean async)
    

    这个构造方法,例如:当我在主线程创建一个空参构造的Handler,里面其实默认传入callback为null,async为fasle。

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

    我们跟着看下这个this到底做了什么?

    public Handler(Callback callback, boolean async) {
            //这里判断Handler是否发生了内存泄漏
            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());
                }
            }
            //获取Looper 
            mLooper = Looper.myLooper();
            //如果为空,抛出异常
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            //获取Looper里面的消息队列
            mQueue = mLooper.mQueue;
            mCallback = callback;
            //这个async是否异步发送消息
            mAsynchronous = async;
      }
    

    2.Handler发送消息

    • 方式一:通过sendMessage(Message msg)
    //通过Handler发送消息,从这里开始
    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) {
            //获取Looper的消息队列
            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) {
            //这个target代表的是发送该消息的Handler
            msg.target = this;
            //判断是否异步发送消息
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            //消息入队,具体实现在MessageQueue
            return queue.enqueueMessage(msg, uptimeMillis);
     }
    

    通过上述的源码分析,这样就完成了一次消息入队的过程。

    • 方式二:通过post(Ruunable r)
    //将Runnable添加到消息队列
    public final boolean post(Runnable r) {
            //getPostMessage(r)返回一个Message
            return sendMessageDelayed(getPostMessage(r), 0);
    }
    
    //将Runnable赋值给Message的callback变量
    private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
    }
    
    /**后面的发送消息就跟前面分析的一样**/
    
    

    通过分析上面两种发送消息的方式,也将导致Handler处理消息也有两种方式。

    3.Handler处理消息

    /**
     *  处理系统消息
     */
    public void dispatchMessage(Message msg) {
            //判断message里面callback是否为空
            if (msg.callback != null) {
                //处理post()发送的消息,内部其实就执行run()方法
                handleCallback(msg);
            } else {
              //处理sendMessage()发送的消息
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                //重写该方法,进行消息处理。该方法是空方法,运行在主线程
                handleMessage(msg);
            }
    }
    

    Handle内部消息处理其实就做了两件事

    • 当使用post(Ruunable r)发送消息,消息通过传进来的的Runnable执行run( )方法。

    • 当使用sendMessage(Message msg)发送消息,通过重写handleMessage( ),自己进行相应消息处理。


    Looper源码分析

    在Looper里面会创建一个ThreadLocal静态变量,并将Looper作为参数(副本)传给它。将ThreadLocal作为Key,Looper作为Value,存在Thread的ThreadLocalMap。这样做起到了线程隔离,每条线程只能获取和存储本身独立的Looper。

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

    1.Looper创建与获取

    调用Looper.prepare( )就可以初始化当前线程的Looper。

    /**
     * 初始化Looper,默认传true,允许退出Looper循环
     */
     public static void prepare() {
            prepare(true);
     }
    
    //往下跟踪
    private static void prepare(boolean quitAllowed) {
            //从ThreadLocalMap里面获取当前线程的Looper
            //如果不为空,抛出异常。(每条线程有且只有一个Looper)
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            //不为空,创建一个Looper给存在ThreadLocalMap
            sThreadLocal.set(new Looper(quitAllowed));
    }
    

    这个是在子线程中,我们手动创建Looper。如果是在主线程中,使用prepareMainLooper( )创建。

     public static void prepareMainLooper() {
            //初始化Looper,不允许退出Looper循环
            prepare(false);
            //对Looper对象加同步锁
            synchronized (Looper.class) {
                //如果不为空,抛出异常
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                //获取Looper
                sMainLooper = myLooper();
            }
      }
    

    我们看下Looper的构造方法,可以知道,当我们初始化一个Looper,内部都会帮我们创建好了一个消息队列MessageQueue

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

    获取Looper:

    /**
     * 获取存储在ThreadLocalMap的Looper
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    

    关于ThreadLocal:在ThreadLocal内部会维护一个ThreadLocalMap静态内部类,用来获取与存储线程的本地副本。

    2.Looper.loop( )消息循环

     public static void loop() {
            //获取Looper
            final Looper me = myLooper();
            //如果为空,抛出异常(需要先初始化Looper)
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            //获取Looper里面的消息队列
            final MessageQueue queue = me.mQueue;
    
            //确认本地身份和跟踪身份令牌
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
    
            //进入for死循环
            for (; ; ) {
                //获取下一个消息
                Message msg = queue.next(); // might block
                //如果为空,消息队列阻塞
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
    
                final long traceTag = me.mTraceTag;
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
                final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                final long end;
                try {
                    //分发消息给Handler(target就是该消息的发送者Handler)
                    //在进行loop前,需要先创建Handler
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                if (slowDispatchThresholdMs > 0) {
                    final long time = end - start;
                    if (time > slowDispatchThresholdMs) {
                        Slog.w(TAG, "Dispatch took " + time + "ms on "
                                + Thread.currentThread().getName() + ", h=" +
                                msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                    }
                }
    
                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();
            }
        }
    

    整个Looper循环的过程分为:

    • 取出Looper和MessageQueen。

    • 进行消息循环,有消息就分发(如果是延时消息,先缓存在消息队列,等时间到了,在按顺序分发);如果消息队列中没有消息,线程阻塞,当又有消息发送过来,会被唤醒。

    • 发送过的消息,会被回收,释放资源。

    3.Looper退出

    分为两种分式:安全退出与非安全退出

    /**
     *  直接退出
     **/ 
    public void quit() {
            mQueue.quit(false);
    }
    
    /**
     *  安全退出(消息队列中的先退出延时消息,再处理完已有的消息后退出Looper)
     **/ 
    public void quitSafely() {
            mQueue.quit(true);
    }
    

    注:

    在主线中创建Handler,主线程会帮我们初始化Looper和进行looper循环,但是在子线程中创建Handler我们需要先Looper.prepare( ),在new Handler(Looper.myLooper()),最后进行Looper.loop( )进行循环。

    public static void main(String[] args) {
            ...
            //初始化Looper
            Looper.prepareMainLooper();
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
            //获取主线程中的Handler
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            //Looper循环
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    

    分析到这里,Handler的核心原理已经差不多,休息一下。

    Message与MessageQueue源码分析

    1.Message

    Message主要用来存储各种信息的序列化对象,里面有几个常用的变量。

     //消息标识 
     public int what;
     public int arg1;
     public int arg2;
     public Object obj;
     //消息延时时间
     long when;
     Runnable callback;
     //发送消息的Handler
     Handler target;
     //下一个消息
     Message next;
    

    2.MessageQueue

    2.1、消息入队

    在分析Handler发送消息过程中,我们会看到queue.enqueueMessage(msg, uptimeMillis)进行消息入队。

      /**
         * 消息入队
         * @param msg 消息
         * @param when 延时时间
         * @return
         */
        boolean enqueueMessage(Message msg, long when) {
            //如果target(Handler)为空,抛出异常
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            //如果消息已经使用过,抛出异常
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                //回收消息
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                //是否需要唤醒
                boolean needWake;
                //判断消息队列里有没有消息,没有的话则将当前插入的消息作为队头,并且这时消息队列如果处于等待状态的话则将其唤醒
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    //否则插入消息队列中间,根据消息创建时间顺序插入
                    //消息是队列中最早的异步消息
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    //进入for死循环
                    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;
                }
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    
    2.2、消息出队

    消息在Looper里循环中取出,消息怎么出队,我们看下源码。

     Message next() {
            final long ptr = mPtr;
            if (ptr == 0) {
                return null;
            }
            //如果nextPollTimeoutMillis为-1,这时候消息队列处于等待状态。   
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
                nativePollOnce(ptr, nextPollTimeoutMillis);
                //尝试检索下一条消息。如果找到则返回。
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {
                      
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                            //消息不为空且不是异步,继续循环取出下一个消息
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        //下一条消息尚未准备好。设置一个超时,以便在准备就绪时唤醒
                        if (now < msg.when) {
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            //消息标记为已使用
                            msg.markInUse();
                            //返回消息
                            return msg;
                        }
                    } else {   
                        //如果没有消息,nextPollTimeoutMillis = -1,下次循环消息队列则处于等待状态
                        nextPollTimeoutMillis = -1;
                    }
                    //退出Looper循环
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
    
                  ...
    
                // 计数重置为0
                pendingIdleHandlerCount = 0;
                
                nextPollTimeoutMillis = 0;
            }
        }
    

    消息队列中消息由Handler存入,由Looper取出。串行方式。


    分析到这里,Handler的消息通信机制已经分析完了,回头看下原来如此简单。最后我们总结下

    三、总结

    • 首先,我们创建Handler对象发送消息到MessageQueen。(如果是在子线程中创建,需要先Looper.prepare( )初始化Looper,再new Handler(Looper.myLooper()),最后调用Looper.loop( )开启消息循环)。

    • 然后,发送的消息在Looper循环器的消息队列中循环,先进入的消息,先出去。

    • 最后,调用msg.target.dispatchMessage(msg)取出消息,在Handler的handleMessage(msg)中进行消息处理。

    四、参考文章

    Android进阶——Android消息机制之Looper、Handler、MessageQueen

    相关文章

      网友评论

        本文标题:Handler消息通信机制,原来如此简单

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