Handler的运行机制

概述 Handler机制又称为Android中的消息机制,这是Android中的重点,是最常用的线程间通信的方式。本blog首先介绍android中为什么要提供消息机制,然后以分析原码的形式讲解消息机制中重要的类及类中重要的方法,再讲解各个类之间的调用关系,最后对Handler的执行机制进行总结。


Android中的消息机制 一,Android中为什么要提供消息机制 我们都知道在android中有两个规定: 1. 网络请求操作要放到子线程。 2. 不能在子线程更新UI。所以Android中必须有线程间的通讯机制。那么为什么要有这两个规定呢?
耗时操作为什么要放在子线程中?
答:如果在主线程一直执行代码,那么界面就会卡顿,为了提高用户体验,android增加了ANR机制,即界面长时间没有响应就会出现ANR。我们在开发中既为了避免界面卡顿,又避免出现ANR所以把耗时的操作放在子线程中。
为什么不能在子线程中更新UI?
控件是线程不安全的,如果不同线程同时修改UI控件将产生异常。
为什么不给控件加锁呢?
加锁确实是一个解决的办法,但是加锁会让逻辑变得复杂,降低UI绘制的效率。总体来说不如添加Handler机制处理效率高。


二,Android消息机制概述 Android的消息机制主要是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程,这三者是一个整体。
MessageQueue的中文翻译是消息队列,作用是存储消息,里面是以单链表的结构进行存储。
Looper的中文翻译是循环,这里叫消息循环,MessageQueue的作用是存储消息,looper的作用就是从messageQueue里面取出新消息,然后交由handler进行处理。
此外还有两个重要的类,Message和ThreadLocal。Message用来携带数据。ThreadLocal保证线程内数据安全。
下面开始看各个类的原码,这里仅贴出重要方法的重要方法体,想更详细了解原码的小伙伴可以自行查看原码。


ThreadLocal类 这是一个存储数据的类,提供了set和get方法,一个ThreadLocal对象只能存储一个对象。这个类的最大特点是可以指定线程存取数据,即:在一个线程中使用set方法存储一个数据,只有在该线程中使用get方法才能取出来,在其他线程中使用get方法取不出的,这就保证了线程间数据的安全。
一,set方法 set方法的核心原码如下:

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

分析:在调用ThreadLocal的set方法存储数据时,首先根据当前线程得到一个Values对象,如果得不到就去创建一个Values对象,Values中储存的是键值对,键时当前的ThreadLocal对象,值就是我们存储在ThreadLocal对象中的值。


二,get方法 get方法的核心原码如下:
public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); }return (T) values.getAfterMiss(this); }

分析:在调用ThreadLocal的get方法取出对象时,首先也是得到Values对象,如果是同一个线程,则此时一定可以通过values(currentThread)获取到values对象,此时就可以根据某些算法取出通过set方法存进去的对象。


三,ThreadLocal类在Handler机制中的作用 这里先简单说明一下,如果不懂可以跳过去,等看完整篇之后回头再看。
在Handler机制中,需要保证UI线程中只有一个MessageQueue对象和一个Looper对象,系统在app启动时给我们创建了一个Looper对象,并保存到ThreadLocal中,在创建Handler对象时需要得到Looper对象(这个可以看Handler的构造方法),此时我们要保证得到的是UI线程中的Looper对象,就需要使用ThreadLocal。


Message类 Message作用是封装数据,在Handler的使用中已经用到,下面详细介绍一下它几个重要的字段和方法。
一,Message类中重要的字段 Message类中重要的字段如下:
Arg1和arg2可以直接传递int数据。
callBack:是一个回调方法,这个方法在UI线程被调用。
data:是bundle类型,里面可以封装很多数据。
next:是为了消息池而定义的,执行消息池中的下一个Message对象。
obj:传递数据时可以给Message绑定一个对象,比如String,Bitmap,Student,等等。
sPool:维护的消息池。
target:这个是很重要的字段,一个app只有一个主线程,一个线程中只有一个MessageQueue对象和一个Looper对象,不同Handler发送过来的消息怎么对应给该Handler进行处理,靠的就是这个字段。Handler在发送消息时给Message的target赋值。即可以根据Message找到对应的Handler.
What:同一个handler可以发送很多Message,通过what来区分是哪一条消息。
when:这个是系统使用的字段。我们在得到Message对象后不能发送两次,因为系统处理后Message的when设为0,再次遇到0的Message将不处理。


二,Message类中的对象回收机制 在获取Message对象时使用的方法是:Message.obtain()。下面看下这个方法的原码:
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类中维护了一个消息池,当消息池中有Message对象时就拿过来使用,当没有Message对象时就创建对象。其实Message有一个回收机制,当Message消息被Handler执行过后就会被回收,放到消息池中,等待下一次使用,避免了对象不停的创建和销毁。Message的回收机制主要方法是:recycle()方法和recycleUnchecked()方法,这两个方法只是Message中消息回收机制中的方法,对Handler运行机制没有作用,这儿不做介绍,感兴趣的小伙伴可以自己去看原码。


Handler类 在handler的使用方法中已经接触到了handler类的几个方法,下面对重要方法做具体分析。
一,Handler的构造方法 在创建Handler对象时一般使用的是无参构造,无参构造的原码是:
public Handler() { this(null, false); }

继续往下看
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class 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; //得到MessageQueue对象 mCallback = callback; mAsynchronous = async; }

这段代码中有两个重要之处,就是得到了Looper对象和MessageQueue对象,这两个对象就是主线程中唯一的Looper对象和MessageQueue对象。


二,enqueueMessage方法 在发送消息时使用的是sendMessage方法,但这个方法没有具体的逻辑代码,它和其他发送消息的方法一样,最终调用的是:enqueueMessage方法,下面看enqueueMessage方法的原码。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }

这个方法其实也特别简单,就两个操作,一个是将this赋值给Message的target字段。另外一个就是调用MessageQueue的enqueueMessage方法,将Message放入到消息队列中。


三,dispatchMessage方法 这个方法的作用是分发消息,原码如下:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }

注:这个方法在Looper的loop方法中被调用,因此执行了我们重写了handleMessage方法。


MessageQueue类 MessageQueue的中文翻译是消息队列,作用是存储消息对象,里面是以单链表的结构进行存储。
MessageQueue的作用有两个,插入消息,取出并删除消息。
插入消息对应的方法是:enqueueMessage
取出并删除消息对应的方法是:next


一,enqueueMessage方法 enqueueMessage的核心代码如下:
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."); }synchronized (this) { 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; msg.next = p; // invariant: p == prev.next prev.next = msg; } } return true; }

分析:这个方法的作用是存储Message对象,方法中首先做了一个判断:这个Message对象必须有target字段,即必须带有handler对象。剩下的操作就是根据时间对Message进行排序,设置message的next指向。
注:这个方法是在handler中的sendMessage方法中被调用的。


二,next方法 next方法的核心代码是:
Message next() {
for (; ; ) {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) { 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; }}

}
分析:这个方法的原码比较多,作用就是返回一个Message对象,并把该Message对象从消息队列中清除。
这个方法中维护了一个死循环,只有当有msg对象不等于null时才有返回值,否则会一直循环,UI线程的代码就停留在了这儿。


Looper类 Looper的中文翻译是循环,这里叫消息循环。MessageQueue的作用是存储消息,looper的作用就是从messageQueue里面取出新消息,然后交由handler进行处理。
一,prepareMainLooper()方法 这个方法的作用是创建UI线程中的Looper对象。原码如下:
public static void prepareMainLooper() { prepare(false); //创建Looper对象。 synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); //得到Looper对象。 } }

这段代码中调用了两个非常重要的方法,一个是prepare,这个方法中创建了Looper对象,并把Looper对象保存到ThreadLocal中。另外一个是myLooper方法,这个方法的作用是取出保存在ThreadLocal中的Looper对象。


二,Looper.loop()方法 这个方法是重重之重,它好比司令部,对整个消息机制的运行起支配作用。
方法的原码:
public static void loop() { final Looper me = myLooper(); //1,获取Looper对象final MessageQueue queue = me.mQueue; for (; ; ) {//开启一个死循环 Message msg = queue.next(); // 从消息队列中取出消息 if (msg == null) {return; }Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); }msg.target.dispatchMessage(msg); //调用handler的dispatchMessage方法if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); msg.recycleUnchecked(); //回收Message对象 } }

总结来说,这个方法中做了四件大事:
  1. 得到UI线程内的looper对象和MessageQueue对象。
  2. 开启一个死循环,在死循环中不断的从MessageQueue中取消息。
  3. 取出消息后调用handler的dispatchMessage方法分发消息。
  4. 回收Message对象。


Handler机制中各方法的调用关系 Handler机制中的方法按执行的时机不同可以分为三块,一是app启动时的准备工作,二是当我们调用handler的sendMessage时发送消息,三是处理消息。


一,app启动时的准备工作 一个app启动时会调用ActivityThread类中的main方法。这个是非常重要的方法,在这里面做了很多事情,下面只看与Handler机制有关的代码:
public static void main(String[] args) { SamplingProfilerIntegration.start(); //。。。 Looper.prepareMainLooper(); // 创建ActivityThread实例 ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); }AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); }Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }

这个方法中调用了两个重要的 方法:
Looper. prepareMainLooper()和Looper.loop();
Looper. prepareMainLooper()的作用是:创建Looper对象和MessageQueue对象。
Looper.loop()的方法是开启死循环。代码会停留在儿,等待消息队列中的消息。


二,发送消息 发送消息使用的是Handler的sendMessage方法,最终调用的是MessageQueue的enqueueMessage方法,把消息放入消息队列。
三,处理消息 当消息队列有有消息时,Looper的loop方法就会执行,调用queue.next()得到Message对象,然后再调用handler的HandleMessage方法处理消息。


总结 【Handler的运行机制】看到这儿应该对Handler的执行机制都明白了,这儿再做一个总结:
在app启动时首先执行的是ActivityThread类中的main方法,这个方法中主要做两件事情,一是调用looper的prepare方法创建looper对象和Messagequeue对象。二是调用looper的loop方法,开启一个死循环不断的从消息队列中获取消息。我们使用Handler首先在主线程创建一个handler对象,并重写handlemessage方法。在子线程中使用sendMessage将消息发送到消息队列中,此时looper的loop方法就可以从消息队列中拿出消息,然后交由handler对象进行处理,然后就执行了我们重写的handlemessage方法。

    推荐阅读