Android中Handler的消息处理机制以及源码分析

在实际项目当中,一个很常见的需求场景就是在根据子线程当中的数据去更新ui。我们知道,android中ui是单线程模型的,就是只能在UI线程(也称为主线程)中更新ui。而一些耗时操作,比如数据库,网络请求,I/O等都是在其他线程中进行的,那么此时就需要在不同线程中进行通信了,而我们最常用的方式就是Handler。

常见的使用方式如下:

 public class HandlerTestActivity extends AppCompatActivity {
public static final String TAG = "HandlerTestActivity";
private TextView displayTv; private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == 42){
displayTv.setText("" + msg.arg1);
}
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
displayTv = (TextView)findViewById(R.id.handlerDisplayTv); new Thread(new Runnable() {
@Override
public void run() {
int count = 0; while (true) { try {
//模拟耗时操作
TimeUnit.MILLISECONDS.sleep(1000);
}catch (InterruptedException e){
} Message msg = new Message();
msg.what = 42;
msg.arg1 = count++;
mHandler.sendMessage(msg); }
}
}).start();
}
}

实际的效果图:

Android中Handler的消息处理机制以及源码分析

其实也很简单,就是 每间隔1秒钟,屏幕上的textview显示的数字增加1。

总结一下,Handler,Looper,MessageQueue,Message这些组成了android当中的异步消息处理机制。主要用于不同的线程之间通讯。(虽然我们最常用到的场景就是 发送消息来更新ui)。

而在android的官方sdk文档中,给出的官方demo其实是这样的。

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

它的基本原理如下。
1,在某个线程中。首先调用Looper.prepare()方法,来准备一个Looper实例,绑定到了该线程,并维护了一个消息队列(MessageQueue),MessageQueue它里面装的内容就是Message,然后调用 Looper.loop(),这是一个无限循环,它来取出消息队列当中的Message送到Handler进行处理。
2,我们new Handler()过程中,来获得到该线程的Looper。然后在其他线程当中,可以调用Handler实例来发送消息(sendMessage方法等),发送的message被放到了MessageQueue中,然后在Handler所在的线程中,Handler实例再通过handleMessage()等方法来处理 这个message,这样就实现了不同线程当中的通信。

所以问题的关键是 一个Handler实例所在的线程只有唯一的一个Looper来维持无限循环,并且它也维护了唯一的一个消息队列(MessageQueue)来传送Message(导入和取出)。

其实这个原理还是不复杂的,随便一本android入门书上也都会讲到这个问题,(并且肯定比我总结的要通俗易懂和严谨)。不过作为一个程序猿,不仅要知其然,更要知其所以然。深层次的理解了它的原理,不仅便于我们的记忆,也可以让我们使用起来更加得心应手。

所以下面,我就从源码的角度来分析这一过程,从子线程当中的sendMessage()开始,然后又怎么在主线程当中handleMessage()进行接收处理的。

首先来看 Handler.java

 /**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
} 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();
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;
}

当然,Handler的构造方法有好几个。不过我们先沿着主线去分析。
在这个构造方法中,最重要的是这两句。

  mLooper = Looper.myLooper();
mQueue = mLooper.mQueue; 

这两句话,说明我们在new Handler时,该线程必须准备好了一个 Looper以及MessageQueue。
然后我们再来看一下Looper.java当中的代码。

 // sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); final MessageQueue mQueue;
final Thread mThread; private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
} public static @Nullable Looper myLooper() {
return sThreadLocal.get();
} 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));
}

首先代码中可以得知 Looper的唯一一个构造方法是private的,所以我们要想得到Looper,那就只能通过prepare()方法了,而在prepare()方法中,注意那句“throw new RuntimeException("Only one Looper may be created per thread")”,这就保证了我们的Looper是唯一的,而通过Handler的构造方法的 “throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()")”。我们也能保证在使用 Handler之前,必须已经调用了 Looper.prepare()来初始化这个唯一的Looper。

而关于这个存储Looper的ThreadLocal对象,其实是一个线程内部的数据存储类,它最大的特点是作用域是限定在该线程内的,也就是说,当它存储数据后(set方法)。只能在该线程内才能获取到数据。 其他线程调用get方法得到的其实会是null(也就是无法得到对应数据)。当然,它内部有一套相应的机制和算法来保证它的作用域是线程内的。

那么从这些代码当中,我们已经明白了,当我们在new Handler()时,必须已经 初始化了唯一的一个Looper和MessageQueue了。

这里插一个问题。我们一直在口口声声的说,在new Handler之前,我们要调用 Looper.prepare()来执行必要的初始化,可是在我们的第一个HandlerTestActivity demo中,根本没调用 这个方法啊。这个原因是因为在UI线程启动时,在ActivityThread中已经帮我们调用过 Looper.prepareMainLooper()方法了(ActivityThread也调用了Looper.loop())。

  /**
* 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()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

所以我们在 HandlerTestActivity中,不需要调用Looper.prepare()方法了。而在官方的LooperThread demo中,因为不是在ui线程。那就只要你自己来调用 Looper.prepare()和Looper.loop()了.

既然Handler,Looper和MessageQueue我们已经都得到实例对象了,那么下面看看它的发送消息的过程。

 /**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
} /**
* 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.
*/
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.
* */
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);
}

通过代码可以看出,几经周折,其实调用的是 enqueueMessage(queue, msg, uptimeMillis)方法,这个方法就是重中之重了。(Handler的sendMessage的方法有几个,但是归根到底,最后还是 调用enqueueMessage方法来进行入队操作)。

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

通过代码可以看出来,Looper的enqueueMessage方法最后调用的还是 queue.enqueueMessage方法,也就是MessageQueue本身来进行入队操作。不过在这个方法中,我们还有一句要注意的代码,那就是
msg.target = this;通过这句代码,来把message的 target对象来变成 Handler本身了。这句代码在后面的处理流程中,是有重要作用的。

那么现在,我们就看看 MessageQueue的 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) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
} 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 {
// 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.
needWake = 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()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
} // We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

这个方法内容比较多。但是主要作用就是一个,把message按照时间戳的顺序,插入到这个队列中,值得注意的是,MessageQueue队列的方式是通过链表来实现的。
至于为什么插入的顺序是按照时间来的,因为我们在使用Handler来发送message时,有几个方法本来就是有个延迟时间的。(比如sendMessageDelayed(Message, long)等,默认的发送时间就是当前时间),并且在Message对象中,也有when属性来保存这个时间的。
不过这里的这个时间戳取的是 手机自开机之后的时间,而不是我们经常说的linux那个源自1970年的那个时间。

我们的代码看到这里,其实就应该告一段落了,那就是 我们通过Handler来发送的Message,已经被送入到MessageQuequ了。消息已经在消息队列了。那么怎么取出来进行处理呢。

我们在最初分析原理的时候就说过,Looper.loop()中维持了一个无限循环,
看一下代码:

 /**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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; // 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;
} //省略一些其他代码
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
} if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
} //省略一些其他代码 msg.recycleUnchecked();
}
}

在这个代码当中,可以看到的确是维持了一个死循环,而这个死循环当中, 就是通过 queue.next() 从MessageQueue中取出message,并且如果没有数据的话,就会被阻塞。MessageQueue对象的next()方法这里就不贴出来了,就是一个出队操作(根据message的when属性,当时间到了,就返回对象,并从队列中删除),。

可是取出来的message数据,这里怎么做处理呢。关键是这一句 msg.target.dispatchMessage(msg),
别忘记了我们前面分析时,在message入队之前的一句代码 msg.target = this。所以 这里的 msg.target.dispatchMessage(msg)代码说白了还是在这个Handler的dispatchMessage()来分发处理的message。

 /**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
} /**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

在dispatchMessage方法中,经过两个if判断,最终message的处理,落到了 handleMessage()方法,至于handleMessage()方法,是个空方法,看看方法前面的注释,这个时候就应该恍然大悟了。
我们在 最初的 demo中的处理方式,不就是重写的 handleMessage方法嘛。。。

所以在这里,这个过程算是比较清晰了,Handler对象(在其他线程中)发送的message,被放入了 MessageQueue中,然后通过Looper的无限循环,最后又被取出到Handler所处的线程中进行了处理。

另外,除了我们传统Handler的sendMessage()方法外,还有一种方法来使用Handler。

public class HandlerTest_2_Activity extends AppCompatActivity {
public static final String TAG = "HandlerTest_2_Activity";
private TextView displayTv; private Handler mHandler; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
displayTv = (TextView) findViewById(R.id.handlerDisplayTv); mHandler = new Handler(); new MyThread("子线程").start(); } class MyThread extends Thread{ public MyThread(String name) {
super(name);
} @Override
public void run() {
super.run();
mHandler.post(new Runnable() {
@Override
public void run() {
//操作UI
displayTv.setText("operation from " + Thread.currentThread().getName()); }
});
}
}
}

我们在 MyThread子线程当中,可以直接使用 mHandler.post方法来传递任务到ui线程中进行相关操作。(Thread才是线程,Runnable只是任务,如果不明白这一点的,可以看一下我前面写的几篇多线程的文章)。

我们也分析一下相关的代码:

 /**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
} private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

看到 sendMessageDelayed()方法,我们就很熟悉了,其实 还是老一套,将message发送到 队列中,不过 getPostMessage()方法是什么呢?
代码就几行,将Runnable包装成Message。并且m.callback = r; 而关于Message,我们可以new,也可以使用obtain方法。后者效率更好一些,因为系统会维护一个Message池进行复用。

不要忘记了我们的dispatchMessage()方法。

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

此时再读读if判断的第一句,msg.callback是什么,不就是post中传递进去的 Runnable嘛,而这里回调的 handleCallback()方法,转了一圈,最后还是回调到了 Runnable的run()方法了。
所以使用Handler.post(Runnable)算是一种简写了。

不过在dispatchMessage()方法中,还有这样一句判断,mCallback != null ,其实是这样的。

 /**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
public Handler(Callback callback) {
this(callback, false);
}

所以说,处理Message的方式不只一种。

写完这些,Handler这种消息处理机制已经从源码角度分析ok了。理解了原理,今后使用起来才更加的得心应手。原理其实不算复杂,但是真正的细节问题其实也有很多。这篇文章呢,也是我写的第一篇关于分析源码的文章,在这里呢,也给自己立个flag,希望今后能写更多的分析源码的文章。

---

作者: www.yaoxiaowen.com

github: https://github.com/yaowen369

上一篇:Problem C: Pie


下一篇:Sublime Text、webstorm等编译器快速编写HTML/CSS代码的技巧