Android之异步消息处理机制

《Android之异步消息处理机制》 异步消息处理.png

前言

在看过了网上那么多的Android的异步消息处理机制的文章之后,总是觉得不够系统,要么是copy来copy去的代码,要么是凌乱的结构,让人看的云里雾里的,也可能是没有看懂并转化成自己的东西。官方文档也没有详细的去解释其之间的关系,只有的类注释,然后我们就只能Read the Fucking Source Code !其实我们只要把官方文档的解释加上源码就能理清这个机制的原理。

PS:在此强调一点就是,去学习android的东西,一定要看官方文档,官方文档才是最原汁原味的,网上的文章都是别人消化过的,不是你的理解,所以一定要去官网都挨个撸一遍,做到心中有数,这样以后查起文档就有的放矢了。

链接:GoogleAndroid 开发者官方网站

1. Android的线程

在说异步消息处理机制之前,一定要先去看一下这篇文章《Android的单线程》,只有看了这篇文章你才知道为什么会有异步消息处理机制。

2. 异步消息处理机制类

首先我们要了了解一下这几个类:

  • Handler
  • Looper
  • Message
  • MessageQueue

在介绍之前,我想大家最一开始使用的都是从Handler,即使没看过源码,也知道大概的使用方式了,所以使用方法这里我不做赘述,下面一一介绍:

Handler

我按照官方文档英文翻译一下,如果大家英语好的话也可以去原文地址去看:

一个Handler允许你发送或处理Message和Runnable对象到线程的消息队。每一个Handler实例都和一个单一的线程相关联,就是消息队列所在的线程。当你创建一个新的Handler对象,从那时起,它就会绑定到线程所创建的消息队列中,它会发送消息或者Runnable接口到消息队列,当他们从消息队列出来之后便依次执行。

然后Handler有2种主要的使用:

  1. 执行Message对象和在未来特定时刻执行Runnable接口;
  • sendMessage(Message)
  • sendEmptyMessage(int)
  • sendEmptyMessageDelayed(int,long)
  • sendEmptyMessageAtTime(int)
  • sendMessageDelayed(int,long)
  • sendMessageAtTime(int,long)
  • sendMessageAtFrontOfQueue(Message)
  1. 在一个不同的线程加入一个动作使之执行而不是你自己去实现。
  • post(Runnable)
  • postAtTime(Runnable,long)
  • postAtTime(Runnable,object,long)
  • postDelayed(Runnable,long)
  • postAtFrontOfQueue(Runnable)

然后我们在Handler.handleMessage Callback中处理回传回来的消息。

Looper

Looper这个类大家肯定也很熟悉,还记得你第一次使用Looper,抛出RuntimeException :”Only one Looper may be created per thread”吗?没错就是你在某一个已经绑定了一个Looper对象的线程中调用Looper.prepare()时产生的。
下面看一段典型的代码:

class LooperThread extends Thread {
  public Handler mHandler;

  public void run() {
      Looper.prepare();

      mHandler = new Handler() {
          public void handleMessage(Message msg) {
              // process incoming messages here
          }
      };

      Looper.loop();
  }}

Looper的作用其实就是一个消息循环器,它不停的从消息池中取出消息,然后在当前线程中分发消息。但是使用的时候一定要注意,要绑定到一个线程,先调用prepare再调用loop。

那么Looper.prepare()究竟做了什么,我们看源码:

/** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
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()的注释:

注释:Initialize the current thread as a looper.This gives you a chance to create handlers that then reference
this looper, before actually starting the loop. Be sure to call loop after calling this method, and end it by
calling quit

翻译:初始化当前的线程作为一个looper对象。在真正的开始消息循环之前,给你机会去创建一个或多个handler来指向到这个looper对象。但是要确保当调用完这个方法之后一定要调用Loop方法,然后调用quit来结束它。

然后我们再看第二个函数的具体实现,我们看到了有一个类ThreadLocal貌似我们从未见过,也没有在实际项目当中使用过,但是如果有的同学如果看过《JAVA并发编程实战》第三章节中的对象的共享,应该会有印象。
不过没关系,我们还是用过类注释来了解它的作用:

注释:Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same ThreadLocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports null values.

翻译:实现了一个线程局部存储,就是每一个线程都有自己对应的值。所有的线程共享ThreadLocal对象,但
是当访问的时候,每一个线程会看到不同的值。一个线程的改变不会影响其他线程。这种实现还支持存储null值。

然后我们在看ThreadLocal所包含的方法:

《Android之异步消息处理机制》

发现和WeakReference的结构很像!然后我们继续看下源码:

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

定位到ThreadLocalMap,在看下源码:

static class ThreadLocalMap {
    /**
     * The entries in this hash map extend WeakReference, using
     * its main ref field as the key (which is always a
     * ThreadLocal object).  Note that null keys (i.e. entry.get()
     * == null) mean that the key is no longer referenced, so the
     * entry can be expunged from table.  Such entries are referred to
     * as "stale entries" in the code that follows.
     */
    static class Entry extends WeakReference<ThreadLocal> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal k, Object v) {
            super(k);
            value = v;
        }
       ...下面代码省略
    }}

看到没,原来是ThreadLocal内部使用弱引用WeakReference来存储当前线程对象,以ThreadLocal为Key,value为Object。另外值得注意的一点是,上面的Entry继承自WeakReference,这样的好处就是能够及时回收Looper中创建的ThreadLocal对象。

接下来是Looper.Loop,我们还是看源码:

public static void loop() {
    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 (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
       //...中间省略代码
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
       //...中间省略代码
        msg.recycleUnchecked();
    }
}

首先第一行,Looper.myLooper():

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

也就是从sThreadLocal静态对象中取出在调用Looper.prepare函数时存放的Looper对象。取出之后,调用Looper中的已经初始化好的对象MessageQueue取出消息池中的消息Message进行操作,取出之后就要将消息进行分发了,此时调用Message.target.dispatchMessage(msg),追溯到源码:

public final class Message implements Parcelable {
       Handler target;
}

原来Message内部持有一个Handler对象,那么这个对象是在什么时候进行赋值的呢,继续看Handler源码:

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

msg.target = this 就是这句!
我们搞明白了Handler在什么时候传递给Message之后,我们就明白消息是怎么回调的了,下面看dispatchMessage(Message msg)函数的实现:

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

这里有if else判断是如果回调函数不为空,则处理回调函数,否则处理消息,很简单,这里其实再次强调了Handler两种主要用法,sendMessage & post Runnable接口。

Message & MessageQueue

由于MessageQueue实际就是操作的Message对象,所以把他们结合在一起来说。
刚才讲到Handler有sendMessage(message)功能,看下源码,原来是Handler把Message对象发送到MessageQueue中,下面看个流程图:

《Android之异步消息处理机制》

最终定位到了下面的代码:

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

queue.enqueueMessage(msg, uptimeMillis),继续看MessageQueue里面的这个函数

boolean enqueueMessage(Message msg, long when) {
    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.");
    }
   
   //此处省略若干代码...
    Message p = mMessages;
    boolean needWake;
    synchronized (this) {
       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;
    }
        if (needWake) {
            nativeWake(mPtr);
        }
    return true;
}

我们不需完全看懂完整的代码,我们只看核心的部分,首先for循环部分是在不停的取出消息,然后取出消息调用了nativeWake函数,查看源码后发现:

 private native static void nativeWake(long ptr);

是本地的实现,我们也不需要care(当然如果你有兴趣可以去查看源码),只需要知道这个函数是唤醒消息队列再次处理消息即可,而整个的消息的循环是采用Sleep-Wakeup 机制。也就是说当队列中没有消息的时候,并不会不停的去取消息,而是进行休眠,休眠的意思大家想想自己的电脑就明白了,当再次点击电源键时,能够立刻唤醒进行工作,此处消息队列的处理也是一样,当消息队列中有消息的时候调用nativeWake来唤醒Looper线程。

另外值得注意的是,Message内部是采用消息池的机制,这样在获取的消息的时候回优先从消息池中取出可用的消息对象,如果没有再进行初始化,这样的好处就避免了不必要的内存开销。另外,Message类也提供了上述代码静态工厂方法Message.obtain()。

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

所以大家在使用的时候最好使用Message.obtain()来构造Message对象。

3. 异步消息处理机制

说到这里,可以用一张图来总结整个的流程:

《Android之异步消息处理机制》

经过以上的几个关键的类的介绍,原理也大概明白了,其中间过程发生了什么,在什么时候发生,大家可以在源码中仔细查看。最后总结一下:

  • Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的;
  • Android应用程序的消息处理内部是使用ThreadLocal来将一个Looper对象绑定到一个线程上;
  • Android应用程序的主线程进入空闲等待状态时不处理消息,来了消息之后利用本地nativeWake函数来进行唤醒;
  • Android应用程序的消息采用消息池机制来存取消息。
    原文作者:刘战塔_Terry
    原文地址: https://www.jianshu.com/p/0d483c6cca60
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞