Handler简单介绍与使用
说起Handler,大多数Android开发者会想到:在子线程中更新UI,这确实是Handler的主要用途之一。分析Handler的运行机制,就从最简单的使用开始吧:
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.i("thread",Thread.currentThread().getName());
Bundle data = msg.getData();
Log.i("message",data.getString("msg"));
return true;
}
});
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("msg","This is in the child thread message");
message.setData(bundle);
mHandler.sendMessage(message);
}
}).start();
}
上面的代码应该没什么好解释的,首先在onCreate方法中第三行创建了Handler对象,然后在子线程中创建出一个Message对象,并往Message对象中set了一个Bundle,最终调用Handler的sendMessage方法。Handler的handleMessage方法会被调用,这个handleMessage方法是回调在主线程的,我们也就成功实现了将子线程的消息发送给主线程处理,因此控制台输出如下:
I/thread: main
I/message: This is in the child thread message
对于使用而言,知道这么用就行了,但本文是对Handler运行机制的总结,因此会继续深入。
先来看Handler的构造器:
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从这里我们至少能得到以下信息:Handler与Looper,MessageQueue有关。在构造方法中第一行就是获取Looper,跟进到myLooper方法中:return sThreadLocal.get();
实际上是获取当前线程的Looper,最后将当前线程的Looper对象引用赋给了自己的成员变量mLooper,如果获取到的Looper是null,就会抛出异常。接着看mLooper.mQueue
,这个mQueue实际上就是当前线程的Looper中的MessageQueue,在Handler的构造器中,将当前线程Looper中的MessageQueue对象引用赋给了自己的成员变量mQueue。
为何在子线程中创建Handler会报异常?
还记得刚才Handler的构造器中需要获取当前线程的Looper吗?如果获取不到,就会抛出异常,我们实验一下:
new Thread(new Runnable() {
@Override
public void run() {
mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
}
}).start();
果然GG:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
这是因为子线程中,默认是没有Looper的,所以当然会报错。那按照它提供的方法,在创建Handler前,调用一下Looper.prepare方法,就不会有问题了。那这个prepare方法究竟做了什么?实际上猜都能猜出来,肯定是给当前线程创建了一个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));
}
我们可以看到sThreadLocal.set(new Looper(quitAllowed));
这里就往当前线程中设置了Looper。子线程有了Looper,Looper中有MessageQueue,满足了创建Handler的条件。虽然这样写,创建Handler是没有问题了,但是此时Handler是无法处理Message的,还必须要调用下Looper.loop方法,让Looper开始循环取出MessageQueue中的消息交给Handler。那为何主线程不需要调用prepare方法就能直接创建Handler了呢?因为在ActivityThread中,已经帮我们创建好了Looper,所以无需我们手动再去创建。因此,如果我们想在子线程中创建Handler,并正常使用,应该这样写:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
Looper.loop();
}
}).start();
Handler的工作流程
其实说了这么多,依然不知道Handler、Looper、MessageQueue三者间到底是什么关系,是如何协作工作的。在这里,就先简单说一下它们三者间的关系,后面再具体分析每一个。当创建了Handler以后,Handler就获取到了当前线程的Looper和MessageQueue。MessageQueue顾名思义就是消息队列的意思,维护着当前线程未处理的消息列表。虽然叫队列,但是它内部的数据结构是单链表,这样做的优点就是增加和删除速度更快,因为对于MessageQueue来说,最主要的操作就是新增、读取和删除。当调用Handler的send系列方法时,会向MessageQueue中放入这个Message,放入了Message后,怎么才能交给Handler处理呢?答案是Looper。Looper的作用简单理解就是一个死循环,不断的从MessageQueue中取出Message交给Handler,Handler就会根据dispatchMessage的分发对消息进行处理。注意Looper所在的线程是创建Looper的线程,我们在主线程中创建Handler使用的是主线程的Looper,因此handleMessage的调用是在主线程,而如果我们在子线程通过Looper.prepare创建了Looper,这个Looper是属于子线程的,自然Looper中的MessageQueue也是在子线程中的。这个时候我们在子线程创建Handler,这个Handler获取到的Looper就是这个子线程的Looper,那么Looper循环MessageQueue时取出的Message在交给handleMessage处理时,也是处于子线程的。不要以为只要用了Handler,它的handleMessage就一定是回调在主线程,这个是和Handler所使用的Looper有关的。
MessageQueue
前面提到过MessageQueue是Android中的消息队列,虽然叫队列,但是内部实现并不是队列,而是单链表。当我们调用Handler的sendMessage方法的时候,其实最终都是向MessageQueue中插入了一个Message:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最终,调用的是enqueueMessage方法,这个方法里面,就会去调用当前线程的Looper中的MessageQueue的enqueueMessage方法,这个方法就是往MessageQueue中插入一个Message。光插入了消息,如何才能取出来交给Handler处理呢?这就是Looper的事情了。
Looper
Looper是与线程所关联的,一个线程只能有一个Looper,主线程初始化时,默认就会创建Looper,这也是为何能直接在主线程中创建使用Handler的原因。主线程创建Looper是在ActivityThread.java中的main方法中创建的,这也是Android应用程序的入口:
//省略部分代码
public static void main(String[] args) {
//创建Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//开始消息循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我们知道,要让Looper开始消息循环,需要调用Looper.loop方法,这也是为什么我们在子线程中创建Handler后,还要调用Looper.loop的原因,如果不调用,loop方法是不执行的,也就不会有循环读取消息队列中的消息交给Handler处理了。重点看下Looper的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;
...
for (;;) {
//取出Message
Message msg = queue.next();
if (msg == null) {
return;
}
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
首先获取了当前线程的Looper和MessageQueue,然后开起了死循环,不停的从MessageQueue中取出Message(通过MessageQueue的next方法)。如果取出的Message不为null,就会执行msg.target.dispatchMessage(msg);
target是什么呢?我们再回过头看看Handler将Message放进MessageQueue的代码:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
target就是你正在使用的那个Handler的对象。因此当Looper从MessageQueue中取出一条Message后,就会调用对应的Handler对象中的dispatchMessage方法。如果MessageQueue中已经没有任何消息了,就会一直阻塞在MessageQueue的next方法,直到有新消息到来。
Handler
既然Looper从MessageQueue中取出Message后,调用了Handler的dispatchMessage方法,我们就来看下dispatchMessage方法中,做了什么事情?
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
感觉有点疑问,msg.callback,这个callback是什么?查看Message类的成员变量发现:Runnable callback
,什么时候用到呢?我们回想一下Handler中有post这个方法,用法很简单,例如我们要在子线程中更新UI,也可以这样做:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
Log.i("thread", Thread.currentThread().getName());
}
});
}
}).start();
}
这个Runnable中的run方法就是回调在主线程的,其实原理都是一模一样的,我们看Handler的post方法
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;
}
原来,和我们调用send系列方法一样,最终都是将Message放进MessageQueue中!只不过send系列方法是你显示传入一个Message,而post是你传入一个Runnable,它内部调用getPostMessage方法给你隐式构建一个Message,并且把构建的Message对象的callbak字段赋值为传过来的Runnable。因此回到Handler的dispatchMessage方法,如果我们使用post方式,则msg.callback就不为null,就会执行handleCallback:
private static void handleCallback(Message message) {
message.callback.run();
}
这样就调用了Runnable中的run方法。而如果我们使用的send方式,那么msg.callback就是null,就会执行下面的:else语句块:
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
这里的mCallback还有印象吗?回想下我们之前看的Handler的构造器中有这么一句:
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
...
mCallback = callback;
...
}
这个mCallback就是我们之前在创建Handler时传的匿名内部类,因此我们匿名内部类中的handleMessage方法就会被回调。注意看最后一行,还有一个handleMessage,这个在什么情况下会用呢?比如你这样创建Handler时:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
这样创建Handler,mCallback就为null,因此回调的就是Handler的handleMessage了。
通过查看dispatchMessage源码,我们现在可以说:优先级最高的是handleCallback,其次是Callback中的handleMessage,这个回调中的返回值决定了是否还要继续回调Handler中的handleMessage,优先级最低的就是Handler中的handleMessage。
关于Handler内存泄漏
这里再说一下Handler的内存泄漏问题,如果我们日常开发中这样来使用Handler,就可能导致内存泄漏:
public class MainActivity extends AppCompatActivity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
}
}
上面这种方式采用了匿名内部类的写法,在java中,内部类会隐式持有外部类的引用,在这里,Handler就持有了MainActivity的引用。通过前面的学习我们又知道,只要使用handler发送了消息,消息中都会持有Handler的引用,而MessageQueue中又持有Message的引用。最终形成了这样一个持有链:Activity被Handler持有,Handler被Message持有,Message被MessageQueue持有。一旦我们退出Activity时,MessageQueue中仍有未处理的Message,就会发生内存泄漏。常规的解决方法如下:
- 使用静态内部类,为了让Handler能调用Activity中的方法,因此让这个静态内部类持有Activity的弱引用
- 在Activity结束的时候,清空MessageQueue中未处理的Message
public class MainActivity extends AppCompatActivity {
private MyHandler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new MyHandler(this);
}
private static class MyHandler extends Handler {
private WeakReference<MainActivity> activity;
MyHandler(MainActivity activity) {
this.activity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainActivity = activity.get();
if (mainActivity != null) {
//处理message
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
}
}
总结
要使用Handler,就必须要有Looper,有了Looper,就自然有了MessageQueue。主线程中,自带了Looper,因此不需要手动创建Looper就可以正常使用Handler。而子线程中,默认是没有Looper的,因此要想在子线程中使用Handler,就必须要手动为当前线程创建Looper。创建的方法是Looper.prepare,这样当前线程就拥有了Lopper和MessageQueue,再创建Handler,就能与这个线程的Looper和MessageQueue关联了。但是要想实现消息循环,还需要调用Looper.loop。
当你使用Handler的send系列方法或是post系列方法时,本质上都是将Message放入与当前Handler所关联的消息队列中,通过Looper循环取出Message再交给Handler处理。同样的,Activity中的runOnUiThread也就没什么好神奇的了,只要你搞懂了Handler的原理,一看代码就懂了:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
好了,到这里就总结完成了。如果文中对知识的理解有错误,欢迎指正,共同学习进步。谢谢!
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!