Android的消息机制

Android中的消息机制其实也就是Handler的运行机制。Android中通过使用Handler来进行更新UI的操作。因为Android的UI更新是单线程模型,UI控件也都是非线程安全的。其原因是如果给UI控件加锁,那么效率将会变得底下的同时还会将UI访问的逻辑变得复杂。

Handler的运行基于MessageQueueLooper的支撑。MessageQueue顾名思义是消息队列。但其实是一个单项链表结构来存储信息Message的。而Looper则是不断去读取消息队列MessageQueue中的信息,将其交与发送该消息的HandlerdispatchMessage进行处理。

下面我们就HandlerMessageQueueLooper分别分析一下其各自的工作原理。


MessageQueue

消息队列中维护了一个Message类型的变量mMessagesMessage是链表结构,所以之前所说消息队列是通过单项链表结构来存储消息。
当消息队列收到来自Handler的消息时,消息队列需要将此条消息插入队列中也即mMessages中。消息插入顺序和它的执行之间相关。

boolean enqueueMessage(Message msg, long 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{       // Inserted within the middle of the queue.  Usually we don't have to wake       // up the event queue unless there is a barrier at the head of the queue       // and the message is the earliest asynchronous message in the queue.       needWeke = mBlocked && p.target == null && msg.isAsynchronous();       Message prev;       for(;;){         prev = p;         p = p.next;         if(p == null || when < p.when){           break;   // 如果除第一条消息外之后无消息或者之后的执行时间都大于该消息执行时间就跳出去插入信息。         }         if(needWake && p.isAsynchronous()){           neesWake = false;         }       }       msg.next = p;  ///把消息插入到消息队列的头部       prev.next = msg;     }     if(neesWake){       nativeWake(mPtr);     }   }   return true; }

主要是进行了消息的单链表插入,现在我们再来看下新的函数读取函数`next()。

...for(;;){  ...  Message prevMsg = null;  Message msg = mMessages;  if (msg != null && msg.target == null) {  // Stalled by a barrier.  Find the next asynchronous message in the queue.  do {     prevMsg = msg;     msg = msg.next;   } while (msg != null && !msg.isAsynchronous());  }  if (msg != null) {     if (now < msg.when) {        // Next message is not ready.  Set a timeout to wake up when it is ready.        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);      } else {        // Got a message.        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 {    // No more messages.    nextPollTimeoutMillis = -1;  }}

next()方法中有一个无线循环,如果没有消息,next()会一直阻塞。知道有消息来,会返回这条消息并从消息队列中移除。


在分析完了消息队列的插入和读取消息之后,我们应该再看下Looper,但是在分析Looper之前,我们需要先对ThreadLocal有所了解。

ThreadLocal

ThreadLocal是一个线程内部的数据存储类。通过它可以在指定的线程内存储数据,其他线程无法访问到该线程的数据。当作用域是线程的时候可以使用ThreadLocal。作用有两个:1.在指定线程存储数据。2.复杂逻辑下的对象传递。

ThreadLocal会从各自线程内部取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找出对应的Value的值。

public void set(T value){    Thread currentThread = Thread.currentThread();    Values valuse = Values(currentThread);    if(valuse == null){      initializeValues(currentThread);    }    values.put(this, value);}

可以看到,通过当前线程拿到数组Values,如果没有,则传入当前线程初始化一个数组。然后将值放到数组中。我们再看下是如何将值放到数组中的。

private void set(ThrealLocal<?> key, Object value){... Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i];      e != null;      e = tab[i = nextIndex(i, len)]) {     ThreadLocal<?> k = e.get();     if (k == key) {         e.value = value;         return;     }     if (k == null) {         replaceStaleEntry(key, value, i);         return;     } }

可以看到,ThreadLocal的值存储位置永远都是ThreadLocal的字段i(值的hashCode和长度减1的值做位运算)的下一个位置。

get方法则是找出字段i的值,然后从数组的i的下一位获取到ThreadLocal的值。

好了,讲解完了ThreadLocal之后,我们需要分析一下Looper的原理了。Looper在消息机制中扮演消息循环的角色。通过不断的Loop()去处理消息队列中的消息。我们先来分析Looper的构造函数。

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

可以看到,初始化Looper的时候,会内部构建一个消息队列。我们可以看到这个构造函数是一个私有函数,那我们应该如何为一个线程创建一个Looper呢。我们可以通过Looper.prepare()为线程创建一个Looper。然后再通过Looper.loop()来开启消息循环。我们来看下looper()函数是如何实现的。

    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;      // 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 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 {              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);          }          // 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()中是一个死循环,只有当消息队列的next()方法返回为null的时候才会退出。当Looperquit方法调用的时候,MessageQueuequitquitSafely方法会通知消息队列退出,这个时候消息队列的next()就会返回为null。而没有消息的时候next()会一直阻塞,这样导致了loop也一直阻塞。当有消息的时候,Looper则会通过消息的target找到发送消息的handler然后调用其dispatchMessage(Message msg)去将消息给Handler处理。而dispatchMessage是在创建Handler时所使用的Looper中执行的。所以就将逻辑代码转移到了指定的线程上执行了。

好了,大致分析了Looper之后,我们再接着对Handler做一个简单的了解。


Handler

Handler的工作主要是发送和接收消息,也即sendMessagedispatchMessage两个函数。
sendMessage是将消息队列中插入一条消息,即调用enqueMessageMessageQueue收到消息之后调用next()将消息传给Looper处理,然后Looper又将消息丢给该Handler来处理。

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

其中callback是一个接口,是用来实例化Handler时候的一个Runnable对象。

/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */public interface Callback {    /**     * @param msg A {@link android.os.Message Message} object     * @return True if no further handling is desired     */    public boolean handleMessage(Message msg);}

主线程的消息循环

这里稍微再讲解一下主线程的消息循环。Android的主线程UI线程也就是ActivityThread。其main()方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue。通过loop()来开启主线程的消息循环。ActivityThread中有一个Handler来进行消息循环,这个Handler就是AcitivytThread内的ActivityThread.H
ActivityThread通过ApplicationThreadAMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThreadBinder方法,然后ApplicationThread会向H发送消息,H接收到消息后会将AplicationThread中的逻辑切换到AcitivyThread中执行,也就是切换到主线程执行,这个过程就是主线程的消息循环模型。


以上摘选自任玉刚的《Android开发艺术探索》加上自己的一丢丢记忆理解。

更多相关文章

  1. Android应用程序键盘(Keyboard)消息处理机制分析(1)
  2. Android - Android 的消息机制
  3. Android之进程与线程
  4. Android学习笔记:Android消息处理机制之Handler介绍
  5. Android之消息推送实现
  6. Android Handler机制 - handleMessage究竟在哪个线程执行

随机推荐

  1. android ViewPager 使用方法
  2. Android(安卓)on OMAP
  3. 智能手机软件平台 Android(安卓)VS iPhon
  4. 找回Kitkat的AppOps
  5. Android(安卓)封装http请求的工具类
  6. Android通过TCPIP协议实现断点续传上传实
  7. Android简单实现猜拳游戏
  8. Android(安卓)SDK Tutorial: Button onCl
  9. Android开机自启
  10. Android: PLEASE DO NOT USE A WAKE LOCK