欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Handler消息传递机制详解

程序员文章站 2022-05-13 22:46:35
...

序言

      随着科技的发展,人们的日常生活习惯也伴随着巨大改变。手机也取代了电脑,成为人们的“贴身小伙伴”,似乎时时刻刻都离不开手机,吃饭走路都要盯着手机,也造成了许多安全事故,希望大家在非必要时还是不要玩手机,利人利己啊。

      说起智能手机系统,安卓系统当然是至今市场占有率最高的咯。谷歌也在不断优化系统,提升用户体验。作为安卓开发对于内部底层代码实现还是要熟知一二的,今天来详细讲解Handler消息传递机制。

      Handler的主要工作就是为了满足UI线程和工作线程间(子线程)的通信,通常通信分为异步和同步

             异步通信:就好比有UI线程和子线程A,两个线程都需要执行任务,两者都准备就绪等待分配到CPU等资源去执行任务,我们无法控制任务完成的先后顺序。

             同步通信:在UI线程和子线程B之间,某个时刻需要等到子线程B的任务执行完后,才可以再去开始UI线程的任务,这就是同步,保证了任务的执行顺序。

     Android的消息处理机制中涉及到几个相关的类,最好能够熟悉其角色功能:

             Handler        Looper         Message         MessageQueue

下图简单的描述了相互间的通信流程:

Handler消息传递机制详解

可以看出Message对象就是数据的载体,装载着所要传递的信息。Handler将Message对象传给MessageQueue消息队列,Looper调用其loop方法将消息队列中的消息对象抽取出来然后给Handler处理。

 

Handler

Handler在整个消息处理流程的担当的职责还是挺多的,Handler就推动着整个流程的执行。首先将消息最终发送到消息队列(MessageQueue),消息队列会调用自己的enqueueMessage将消息添加到自己的队列中,后面由Looper的loop方法触发消息队列调用next方法取出队中的消息对象,然后通过Message的target变量(就是Handler)调用dispatchMessage方法对消息进行分发处理。后面会根据代码详细讲解整体流程。

 

Looper

Looper就相当于一个泵,不断去触发消息队列取出队列中的消息,Looper中的静态成员变量sThreadLocal(ThreadLocal<Looper>)存储着每个线程中的Looper对象,保证每个线程中Looper对象的唯一性,普通成员mQueue(MessageQueue)用来暂存消息对象,在每个线程中Looper和MessageQueue对象实例是唯一的,不可能在单个线程中出现多个Looper对象,否则会出错。

 

Message(消息类)

消息的载体,主要功能是对数据的封装,以便传递数据,下面讲解下其部分关联成员

1)public int what:定义不同的消息类型,可用于区分不同的业务

2)public int arg1:传递整型数据

3)public int arg2:传递整型数据

4)public Object obj:传递其他数据类型使用

5)Handler target:指向发送该消息的Handler对象

6)Runnable callback:用于指向Handler使用postxxx方法传递的Runnable对象,接口回调

 

MessageQueue(消息队列)

消息队列就是一个队列结构,暂存消息,保证消息对象先进先出

 

下面通过SDK-27的源码来详细讲解下整个流程:

        既然Handler推动着整个流程的运转,那我们先从创建Handler对象切入:

    //可以看到Handler对构造方法进行了多个重载

    //默认与当前线程中的Looper关联,如没有Looper对象则抛出异常
    public Handler() {
        this(null, false);
    }

    //设置接口回调对象(执行的优先级低于message的callback,重载的handleMessage)
    public Handler(Callback callback) {
        this(callback, false);
    }

    //指定一个Looper对象与此关联
    public Handler(Looper looper) {
        this(looper, null, false);
    }

    //指定一个Looper对象与此关联,设置接口回调对象
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    //设置Handler是否异步
    public Handler(boolean async) {
        this(null, async);
    }
    
    //设置接口回调的Callback对象,并设置是否异步
    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());
            }
        }
        //首先通过Looper的mylooper方法获取到当前线程中的Looper对象,如果不存在则抛出异常
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //将当前线程中的消息队列对象赋值给自己的成员变量mQueue
        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的构造方法主要就最后两种,一种是自己获取当前线程中的Looper对象,另一种是传递进来的Looper对象。先来简述下第一种:如果不传Looper对象的话,通过Looper的myLooper方法获取当前线程中的Looper对象,会先做个空判断,如果为空就抛异常,大家想想是不是自己有过在子线程中一开始就直接new一个Handler对象,结果程序跑起来就崩溃了,如果需要在子线程中正常使用的话应该先调用Looper的prepare方法,保证当前线程中存在Looper对象实例,注意如果当前线程存在Looper对象实例的话,再去调用其prepare方法,也会抛出异常(new RuntimeException("Only one Looper may be created per thread"))。可能大家会想到为什么在UI线程中直接创建Handler对象没有出现异常,那是因为在App 启动的时候系统已经在ActivityThread类的main方法中初始化了主线程对应的Looper对象(prepareMainLooper方法创建)

   public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        //创建主线程对应的Looper对象
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

顺便来看下Looper的几个方法:

    //创建子线程中的Looper对象
    public static void prepare() {
        prepare(true);
    }

    //创建子线程中的Looper对象
    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));
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    //创建主线程中的Looper对象
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been     
                  prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    /**
     * Returns the application's main looper, which lives in the main thread of 
          the application.
     */
    //获取主线程中的Looper对象
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }


    //获取当前线程中的Looper对象
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    /**
     * Return the {@link MessageQueue} object associated with the current
     * thread.  This must be called from a thread running a Looper, or a
     * NullPointerException will be thrown.
     */
    //获取当前线程中的消息队列对象
    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

    //私有构造方法,创建Looper对象,同时创建当前消息队列
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

从上面的方法中可以看到sThreadLocal类变量,这个变量前面讲到过,就是用来存储当前线程中的Looper对象,我们来大致的看下ThreadLocal这个类:

    //ThreadLocal内部数据存储实际上是其内部类ThreadLocalMap实现的
    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    //获取当前线程所对应的对象
    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();
    }

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    //将要存储的对象与当前线程关联起来
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    /**
     * Removes the current thread's value for this thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the current thread, its value will be
     * reinitialized by invoking its {@link #initialValue} method,
     * unless its value is {@linkplain #set set} by the current thread
     * in the interim.  This may result in multiple invocations of the
     * {@code initialValue} method in the current thread.
     *
     * @since 1.5
     */
    //移除当前线程所对应的对象
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

通过上面的代码解释可以大致了解Looper通过ThreadLocal存储的操作,其实最终是通过ThreadLocalMap内的Entry类实现的,可以抽象的理解内部就像key-value结构

上面讲解了创建Handler对象的过程,接下来讲讲发送消息的过程:

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

    public final boolean postAtFrontOfQueue(Runnable r){
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    /**
     * Sends a Message containing only the what value.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

    /**
     * Sends a Message containing only the what value, to be delivered
     * after the specified amount of time elapses.
     * @see #sendMessageDelayed(android.os.Message, long) 
     * 
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

    /**
     * Sends a Message containing only the what value, to be delivered 
     * at a specific time.
     * @see #sendMessageAtTime(android.os.Message, long)
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

    /**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    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);
    }

    /**
     * Enqueue a message at the front of the message queue, to be processed on
     * the next iteration of the message loop.  You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     * <b>This method is only for use in very special circumstances -- it
     * can easily starve the message queue, cause ordering problems, or have
     * other unexpected side-effects.</b>
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }

    //将消息添加到消息队列
    private boolean enqueueMessage(MessageQueue queue, Message msg, long             
          uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

上面的postxxx, sendxxx方法最终都是调用enqueueMessage方法将消息对象加入到消息队列.通常我们使用postxxx方法的时候,都是要传一个Runnable对象,这个对象最终赋值给了Message对象的callback成员:

   private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
   }

   private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r;
        return m;
   }

这个回调的操作会关联到执行的先后顺序,因为最终在消息分发处理时,关联到三个不同的执行操作