Handler 是什么?这个是android面试官最喜欢问的问题,用脚趾都能回答出来,Handler可用于android多线程通信,可以用来更新UI的,用来发送message,处理message的。于是在很多地方我们都直接new 一个Handler 来使用,那么问题来了,如果你在一个自定义线程中new一个Handler,这个Handler能用么?能 or 不能。 Why?
先给大家上个图,好有个整的印象:
呵呵,现在老鸟带你揭开Handler这个神秘的面纱,大家都知道Handler 是用来发送和处理message的,那么message存在哪里呢?答案是MessageQueue, Handler发送的message当然要放在MessageQueue了,那么问题又来了,MessageQueue中的message谁在管理,是Handler? NO, NO, 是Looper。 下面我们看看Handler的部分源码:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
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需要一个Looper, Looper里面有一个mQueue, 这个mQueue就是MessageQueue,不信?你去看Looper.java源码。所以自定义线程中是不能就这么简单的new一个Handler来用滴。如果没有这个Looper,在运行时是会抛出异常滴。那么问题又来了,为啥我们在Activity或Fragment里面可以直接new一个Handler来用呢?答案是:因为应用的主线程帮我们定义好了这个Looper,我们在Activity或Fragment里面new一个Handler都是使用主线程的Looper。
呵呵,说到主线程,是不是又让人蒙啦,大名鼎鼎的android主线程,就是UI线程,老鸟在这里给你简单介绍下吧,更详细的关于主线程,老鸟会单独N篇文章讲解。首先,主线程就是ActivityThread, 每一个应用有且只有一个主线程,UI改变都是在这个AcitivityThread里面完成的。在这里我们只关心ActivityThread是如何创建主线程Looper的,下面是ActivityThread部分源码:
public static void main(String[] args) {
Environment.initForCurrentUser();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
ActivityThread的main方法里会调用Looper.prepareMainLooper方法,这个方法就是创建主线程的Looper, 并且后面执行了Looper.loop(),这个loop()就是一个for(;;)永久循环,这个循环体里面就是不断判断是否有新的message进入了Looper的MessageQueue里面,如果有message,就把他发送给发送这个message的handler去执行。当我们在activity或fragment里面new一个Handler的时候,其实都是拿到主线程这个Looper,然后发消息到这个Looper的MessageQueue里。那么我们在activity里面是怎么拿到这个主线程Looper的呢?在上面的Handler代码里我们new一个Handler的时候,会调用Looper.myLooper(),下面看看Looper的部分代码:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
由上面代码可以知道prepareMainLooper调用了prepare(), prepare()里面执行了new Looper(), 这里新建了一个MessageQueue, 最后并且把这个looper存入了sThreadLocale里面。所以当我们在activity或fragment里面new Handler的时候,都会从myLooper()里面拿到这个主线程looper啦。
那么又有问题来了,我们自己定义的thread里面我也想用Handler怎么办?这个问题现在是不是很简单了?就是我们自己也给我们的线程生成一个Looper,不就OK了么,对滴,就这样。上面调用Looper的prepare()就会为我们生成一个Looper。如何把这个looper跟我们的Handler关联起来呢,我们可以用调用Handler(Looper looper)这样,把他们关联起来,最后调用Looper.loop()让这个looper动起来,这样你就可以用这个Handler发送处理message啦。是不是很爽。
class MyLooperThread extends Thread{
private final String TAG = "MyLooperThread";
private MyHandler mHandler;
public MyLooperThread(){
}
@Override
public void run() {
super.run();
Looper.prepare();
mHandler = new MyHandler(Looper.myLooper());
Looper.loop();
}
public void sendMessage(Message msg){
mHandler.sendMessage(msg);
}
class MyHandler extends Handler{
MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
Log.i(TAG,"1111111111111");
break;
default:
}
}
}
}
上面我们就可以在handleMessage里面处理我们自己的消息啦。
说了这么多我们还没说HandlerThread, 这个又是什么鬼呢?其实这个就是android帮我们封装了Looper的一个thread,它的实现就跟我们上面自己的MyLooperThread差不多,使用它我们就不用自己去手动调用生成Looper的代码啦,这是不是很贴心呢。