android 的消息机制主要指Handler的运行机制,也就是消息的分发和处理机制。基于这一点认知我们可以认为在android中,是有两套消息机制,分为java层和native层。它们对事件有着各自的处理机制。下面我们由浅入深,来了解一下android的消息机制。


  1. 基本概念
  2. 如何使一个线程变成Looper线程?
  3. Handler是怎样接收和处理消息的(事件的处理逻辑)
  4. 系统如何维护Message对象的回收与获取的?(消息池)
  5. java层和native层的消息机制(待补充)

1. 基本概念

首先我们需要理解的是Android应用是事件驱动的(尽管Android开发中包含一些事件驱动的特性,但是离纯事件驱动架构还很远。具体描述可以看这篇文章: Android事件驱动编程 ),每个事件都会转化为一个系统消息,也就是Message。消息中包含了事件相关的信息和消息的处理人--Handler。每个线程都有一个默认的消息队列,也就是MessageQueue,作用类似于工厂的产品线,动态的运转,而消息循环不断的从这个队列中取出消息,处理消息。而默认情况下,普通线程的消息队列并不是运转的,我们需要开启消息队列的运转,也就是将线程变成Looper线程。

盗了两张图,第一张表明了Looper线程中,消息队列和消息的结构。第二张图加入了消息处理者Handler。

401381542845.png 47461381542848.png

我们对上述概念做一个规范性的描述:

Message:消息,又叫task,封装了任务携带的信息和处理该任务的handler。

MessageQueue: 消息队列,内部存储一组消息,以队列的形式对外提供插入和删除工作。可以理解为消息的传送带,将消息运转起来。

ThreadLocal:线程本地储存对象。通过它可以在指定的线程中存储数据,存储后只能在指定线程中获取该数据

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本

给一个链接: 深入研究java.lang.ThreadLocal类

Looper: 循环者,用来使一个普通线程变成Looper线程。 Looper线程一直循环等待,一旦有新任务,执行完然后继续循环等待下一个任务。它的内部维护了一个消息队列MQ。Android的主线程就是一个Looper线程。

Handler: 处理者。扮演了往MQ上添加消息和处理消息的角色。通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。


一些问题的讨论


问题1. 如何使一个线程变成Looper线程?

前面我们说到,

......而默认情况下,普通的线程的消息队列并不是运转的,我们需要开启消息队列的运转,也就是将线程变成Looper线程......

现在我们来看一下如何使一个线程变成Looper线程,其实很简单:

        public class LooperThread extends Thread {              @Override              public void run() {                    // 将当前线程初始化为Looper线程                     Looper.prepare();                    // ...其他处理,如实例化handler                    // 开始循环处理消息队列                     Looper.loop();              }        }

我们来看一下Looper类到底是个什么东西,Looper类代码刨去注释也就100行左右:

    public final class Looper {               private static final String TAG = "Looper";               static final ThreadLocal sThreadLocal = new ThreadLocal();               private static Looper sMainLooper;             //内部维护一个消息队列           final MessageQueue mQueue;               //持有当前线程的引用           final Thread mThread;               private Printer mLogging;                public static void prepare() {                         prepare(true);               }               private static void prepare(boolean quitAllowed) {                // 试图在有Looper的线程中再次创建Looper将抛出异常                        if (sThreadLocal.get() != null) {                                  throw new RuntimeException("Only one Looper may be created per thread");                        }                 //其实Looper对象本质是一个线程局部变量                       sThreadLocal.set(new Looper(quitAllowed));               }          //省略若干方法           private Looper(boolean quitAllowed) {                  //创建消息队列                  mQueue = new MessageQueue(quitAllowed);                  //默认持有的为当前线程                  mThread = Thread.currentThread();           }                     //省略其他方法    }

我们可以很清晰的看到,Looper对象会持有当前线程的引用,同时内部维护了一个消息队列,并且本质就是一个线程局部变量。这样Looper的准备工作就做好了,接下来我们来看Looper线程是如何Looper消息队列的

    public static void loop() {            final Looper me = myLooper();   //从Looper局部变量那里拿到looper对象          if (me == null) {                      throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");            }        final MessageQueue queue = me.mQueue;     //拿到消息队列        //clearCallingIdentity这个可以看成是安全性代码,也可以看成是调试代码作用是确定当前这个looper所在的“进程”        //是否一直在同一个“进程”里,如果进程变多半是说明这个线程运行在某种跨进程代码里。        //比如说你通过AIDL调用stub,远程那边接到之后启动一个线程,就有可能触发ident != newIdent了        Binder.clearCallingIdentity();            final long ident = Binder.clearCallingIdentity();            //开始循环了        for (;;) {                    //消息队列的next()方法具体是怎么执行的?先记住有这个点,后面再深入讨论            Message msg = queue.next();             //早先的代码,会检查msg.target是否为null, 为null的话表示“message没有target为结束信号,退出循环”,            //所以早期Looper的quit()方法通过创建一个空的message,它的target为NULL,即可结束循环消息,            //现在quit()方法会调用MQ的void quit(boolean safe)方法来结束消息循环            if (msg == null) {                             return;                    }                            Printer logging = me.mLogging;                   if (logging != null) {                            logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);                   }            //将真正的处理工作交给message的target,即handler            msg.target.dispatchMessage(msg);                   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后,在线程中通过开启一个循环,不断地取出Message并调用Message的Handler进行处理。

到此为止,你应该对Looper有了基本的了解,总结几点:

1.每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal2.Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行3.Looper使一个线程变成Looper线程。

这里先埋了一个点,消息队列到底是怎么获取下一条要处理的消息的呢(MessageQueue的next()方法到底是怎么执行的)?先别急,我们先来看Handler

问题2. Handler是怎样接收和处理消息的

接上文,Looper将消息的分发处理交给了Handler,那这个消息的“处理者”到底是怎么去处理消息的呢?同样的,我们先来看看Handler的构成:

public class Handler {    final MessageQueue mQueue;  //关联的消息队列    final Looper mLooper;  //关联的Looper    final Callback mCallback;    //...省略代码    //默认关联当前线程的Looper    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();    //获取关联的Looper          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;    }        //指定Looper    public Handler(Looper looper, Callback callback, boolean async) {           mLooper = looper;           mQueue = looper.mQueue;            mCallback = callback;            mAsynchronous = async;    }    //...省略代码

}

Handler关联了Looper对象和消息队列,我们可以为Handler设置回调,接下来看看Handler如何发送消息的

Handler有很多发送消息的方法,包括:
post(Runnable)
[postAtTime(Runnable, long)](http://developer.android.com/reference/android/os/Handler.html#postAtTime(java.lang.Runnable, long))
[postDelayed(Runnable, long)](http://developer.android.com/reference/android/os/Handler.html#postDelayed(java.lang.Runnable, long))
sendEmptyMessage(int)
sendMessage(Message)
[sendMessageAtTime(Message, long)](http://developer.android.com/reference/android/os/Handler.html#sendMessageAtTime(android.os.Message, long))
[sendMessageDelayed(Message, long)](http://developer.android.com/reference/android/os/Handler.html#sendMessageDelayed(android.os.Message, long))
光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,见源码:

//用于向Handler关联的MQ上post一个Runnable对象,它的run方法将在Handler关联的Looper线程上执行public final boolean post(Runnable r) {         return  sendMessageDelayed(getPostMessage(r), 0);}private static Message getPostMessage(Runnable r) {        //这里注意,消息并不是直接new的对象,而是调用了obtain()方法,先记住这一点,待会我们再深入    Message m = Message.obtain();        m.callback = r;        return m;}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);}

了解了Handler是如何发送消息的,接下来我们看一下Handler是如何处理消息的,
消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,见源码:

public void dispatchMessage(Message msg) {        //如果message设置了callback,即runnable消息,处理消息本身的callback    if (msg.callback != null) {                handleCallback(msg);        } else {        //Handler构造函数传入的mCallback,这种写法可以通过实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法        if (mCallback != null) {                        if (mCallback.handleMessage(msg)) {                                return;                        }                }        //最后才是留给子类的钩子方法                handleMessage(msg);        }}

Handler的内部的消息处理机制对开发者而言是完全透明的,优先级比较:消息自带的回调 > Handler构造函数设置的参数回调 > 子类的钩子方法。

问题4:系统如何维护Message对象的回收与获取的?

仔细看前面的代码注释的读者应该发现,在Handler部分,我们说到Handler将Runnable对象转成Message对象,这里并不是直接new了一个消息对象,而是调用了Message.obtain()方法,我们来看一看这个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();}

注释说,我们从全局的消息池当中获得了新的消息对象,我们来看看,这个sPool、sPoolSync到底是个什么?

public final class Message implements Parcelable {     ...    / sometimes we store linked lists of these things    /*package*/ Message next;    ...    private static final Object sPoolSync = new Object();    private static Message sPool;    private static int sPoolSize = 0;    ...}

看到这里有疑问,所谓的pool怎么其实也是个Message对象?其实消息池的维护并没有使用容器,而是链表。next指向的就是链表中下一个消息对象。所有可用的消息通过next串联成消息池。那消息是什么时候被放入链表中的呢?经过查找我们发现,Message中也有个recycler()方法,正是消息被回收的时刻,消息对象才被添加到消息池中:

public void recycle() {    //判断消息是否在使用        if (isInUse()) {                if (gCheckRecycle) {                        throw new IllegalStateException("This message cannot be recycled because it "                    + "is still in use.");               }                return;        }        //清空状态,并将消息添加到消息池中    recycleUnchecked();}

总结一下,Message通过在内部构建一个链表来维护被回收的Message对象,当用户obtain()时会优先从消息池中取,其次才是创建新的消息对象。这么做的原因很简单,避免大量创建对象,防止内存占用过高,频繁GC。

更多相关文章

  1. 浅析Android中的消息机制-解决:Only the original thread that cr
  2. Android异步消息机制之Handler
  3. Android的Handler机制详解3_Looper.looper()不会卡死主线程
  4. Android之Handler用法总结
  5. Android开发之消息处理机制(一)——Handler
  6. Android异步加载图像小结 (含线程池,缓存方法)
  7. android 面试题集
  8. Titanium 使用刘明星的Jpush module做android端的消息推送
  9. android 电容屏(二):驱动调试之基本概念篇

随机推荐

  1. SQL Server误区30日谈 第30天 有关备份的
  2. SQL Server误区30日谈 第29天 有关堆碎片
  3. SQL Server误区30日谈 第28天 有关大容量
  4. SQL Server误区30日谈 第27天 使用BACKUP
  5. SQL Server误区30日谈 第25天 有关填充因
  6. SQL Server误区30日谈 第26天 SQL Server
  7. SQL Server误区30日谈 第24天 26个有关还
  8. SQL Server误区30日谈 第23天 有关锁升级
  9. SQL Server误区30日谈 第22天 资源调控器
  10. SQL Server误区30日谈 第21天 数据损坏可