这是我的第一篇博客,之前也是酝酿了很久,经过几个伙伴的鼓励和计划之下,还是决定将自己的所学知识通过文字的形式表达出来,也是提升自己的一种方式。本文将通过使用、原理、拓展来详细介绍Handler、Looper、Message之间的关系。好了,废话不多说,直入主题。
一、使用篇
先看下面一个小demo:
public class SecondActivity extends Activity {
private Handler mHandler;
private EditText vEditText;
private static final int MSG_WHAT_1 = 0x11;
private static final int MSG_WHAT_2 = 0x12;
private static final int MSG_WHAT_3 = 0x13;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vEditText = (EditText)findViewById(R.id.cll_read_sms);
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_WHAT_1:
vEditText.setText("hello world!!!");
break;
case MSG_WHAT_2:
vEditText.setText("goodBye world!!!");
break;
case MSG_WHAT_3:
vEditText.setText("welcome world!!!");
break;
default:
break;
}
}
};
new LooperThread().start();
}
class LooperThread extends Thread {
public void run() {
mHandler.sendEmptyMessage(MSG_WHAT_1);
mHandler.sendEmptyMessageDelayed(MSG_WHAT_2, 2000);
mHandler.sendEmptyMessageDelayed(MSG_WHAT_3, 3000);
}
}
}
大家可以看到我定义了一个全局的handler变量,定义了一个LooperThread,在run方法里直接发送了三条消息,
可以看一下运行效果,是先显示出“hello World!!!”过了两秒之后换成“goodBye world!!!”又过了三秒之后换成了“welcome world!!!”,是不是很奇特,那这究竟是为什么呢?请接着往下面看。
二、原理篇
好了,大家看了上面的demo之后可能会提出疑问,为什么我在子线程里发了三条消息可以在主线程里得到操作呢?这些操作和Looper有什么关系呢?这其中又是怎么运行的呢?好,下面我会带着大家一步一步揭开这神秘的面纱。
首先,我们先来看Handler这个类里面的sendEmptyMessage这个方法的实现:
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
可以看到这里面只是调用了sendEmptyMessageDelayed 这个方法,我们在上面也调用了这个方法:
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
可以看到,在这个方法里,构建了一个Message对象,并且将what这个值赋给msg,好,我们接着往下面看:
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
在这个方法体里,只是对传入的时间做了一些校验,然后直接返回sendMessageAtTime这个方法:
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);
}
这个方法里面只是校验了一下MessageQueue,然后返回enqueueMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,这里面是将message的target赋值为Handler对应的实体,并且最后一句是将这个Message对象成功添加到MessageQueue这个队列里。
好了,那么这个时候有朋友就会问了,上面看见的只是将一个Message对象成功添加到MessageQueue的队列里,并没有出现调用Handler里面方法的地方啊?稍安勿躁,这里就要介绍一个比较隐蔽的知识点,构建Handler之前,一般都要加上Looper.prepare()
,构建完必须调用Looper.loop()
方法,子线程里都必须要这么使用,UI线程只是说系统已然为我们调用过了。OK,既然知道原理了,那么我们现在就来看Looper这个类里面的方法究竟干了什么吧:
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));
}
我们可以看到调用Looper.prepare()这个方法会构建一个Looper的实例放置到ThreadLocal里,OK,我们继续看:
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
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();
}
}
这段代码有点长,那我们就看主要部分,从上往下面看可以知道,这里面得到Looper对象,然后得到对应的MessageQueue对象,紧接着起了一个循环,不断的从MessageQueue里面取出Message,并且调用msg.target.dispatchMessage(msg);
,从上面对sendMessage的分析我们知道,msg的target就是对应着一个handler的实例,那么现在可以进Handler类里面看对应的分发了,离真相越来越近了:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到,在else里面调用了handleMessage(msg);
这个方法,而我们最开始demo中恰恰是复写了这个方法,由此,整个流程就分析完毕。
那么我们可以整理一下上面分析的结果,会得到下面几个结论:
(1)除了主线程之外,其他线程使用Handler必须首先调用Looper.prepare()这个方法,实例完必须调用Looper.loop()这个方法。
(2)调用Looper.prepare()时会构建一个Looper对象并放置在ThreadLocal中,Looper在构建时会自动构建一个MessageQueue对象并持有。
(3)Handler对象在构造中,会持有Looper和Looper绑定的MessageQueue,保证之后发送的Message会被放置到对应的队列中。
(4)调用Looper.loop()方法,会从Looper持有的MessageQueue中不断的取出Message,并调用Message对应Handler实例dispatchMessage()方法分发。
所以如果在子线程里调用上面demo中的方法的话,就应该这么写了:
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_WHAT_1:
vEditText.setText("hello world!!!");
break;
case MSG_WHAT_2:
vEditText.setText("goodBye world!!!");
break;
case MSG_WHAT_3:
vEditText.setText("welcome world!!!");
break;
default:
break;
}
}
};
new LooperThread().start();
Looper.loop();
三、拓展篇
现在很多人都不会直接去new一个Thread,而是借助Handler去new一个Runnable,例如:
new Handler().post(new Runnable() {
@Override
public void run() {
//这里执行一个操作
}
});
那这边的原理究竟是什么呢?其实很简单,跟上面我们分析的一样,下面给大家简单分析一下:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
除了调用getPostMessage方法之外,就是发送一个Message,很正常。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
将Runnable对象赋值给Message的callback,直接返回。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
重点在这边,我们还记得上面给Msg的callback赋值了,所以应该调用hanleCallback方法。
private static void handleCallback(Message message) {
message.callback.run();
}
果然,在这边直接调用callback的run方法,至此,大功告成!!!!