关注公众号“码农帮派”,查看更多系列技术文章:
在android-sdk/sources/android-x下可以看到源码。
在android.os的包下,有一个HandlerThread的类,继承自Thread,是Google提供的线程类。
package android.os;
public class HandlerThread extends Thread {}
为什么要提供这类呢,下面对此进行分析:
在Android开发中,当我们进行耗时操作,如网络请求时,为了不阻塞UI线程,会讲耗时操作在子线程中进行,等耗时操作完成之后,在子线程中通过Handler发送Message到UI线程进行UI的更新,那么为什么子线程可以通过Handler可以将Message跨线程发送到UI Thread呢?
查看Handler的源码,我们可以发现,Activity在ActivityThread中被创建,在ActivityThread的main()方法中,会调用Looper.prepareMainLooper()的方法,在Looper类中,prepareMainLooper()的过程是new Looper(),然后将创建的Looper对象保存在当前线程(Activity Thread/ UI Thread)的ThreadLocal变量中,而我们会发现,new Looper()的过程中,同时会创建一个MessageQueue,并与当前的Looper关联在一起,在prepareMainLooper()之后,紧接着会有mLooper.loop()的过程,即启动了该Looper对象。
当我们在UIThread中new Handler()的过程中,其中最重要的步骤,就是currentThread.sThreadLocal.get(),这一步拿到了当前线程(UIThread)的Looper对象mLooper,并进行了this.mQueue = mLooper.mQueue的过程,就是将Looper的MessageQueue对象绑定到Handler上,Handler有一个内部属性mQueue( MessageQueue对象 )。这样主要的流程就结束了,当子Thread通过Handler对象sendMessage()的时候,在Handler内部源码可以看到,内部是通过dispatchMessage(Message msg)的方法,最终将Message加入到了Handler的内部属性mQueue中,而new Handler()的时候, 该Handler对象的内部属性mQueue已经指向了Looper对象的MessageQueue,而该Looper对象来自于当前线程(UIThread)的ThreadLocal变量,也就是说,子线程中handler.sendMessage()的过程,实质上就是把一个Message对象加入到了UIThread的MessageQueue中,那UIThread如何去处理这些加入到它的MessageQueue中的Message呢?MessageQueue的创建伴随着Looper的创建,而Looper在被ActivityThread创建的之后,立即调用了该Looper对象的loop()方法,该方法的源码可以看到,是启动了一个死循环,阻塞的从MessageQueue中取出Message,当取出的Message msg != null时,便拿出Message对象的target,调用它的dispatchMessage(Message)的方法:
msg.target.dispatchMessage(msg);
那这个target是什么呢?翻看源码,可以发现,target的类型是Handler,也就是说,loop()函数在UIThread中建立了一个轮训的机制,不停的读取MessageQueue中的Message,当读到之后,就会将读到的Message对象发送给Handler,而Handler对象就会调用new Handler()过程中重写的方法,比如handlerMessage(Message msg)方法,这样就将一个Message信息从子线程最终传递到了UIThread中。
上面就是子Thread如何通过Handler与UIThread通信的过程,即Handler的机制,那么问题来了,上面的过程从始至终都是子线程向UIThread发送信息,并没有反过来的过程,也就是UIThread如何给子线程发送Handler信息,其实我们翻看Handler的源码会发现,Handler的构造函数有很多,其中一个是new Handler()最为常用,但是还有一个是new Handler(Looper mLooper),其实在使用无参构造函数的时候,在绑定Handler的mQueue的时候会有一个currentThread.sThreadLocal.getLooper()的过程,就是获取当前线程的Looper对象,当我们在new Handler()的时候要是传入一个Looper对象,就会将Handler的mQueue指向传入的Looper对象的MessageQueue了,那么我们要是传入子线程的Looper对象,那么在UIThread中handler.sendMessage(msg)时,发送的Message对象最终会发送到子线程的MessageQueue中,而子线程同样可以looper.loop()的方式循环的读取Message,并在子线程中处理。
这里需要说明,Looper,MessageQueue以及Message这一套体系来自于Android系统,而Thread来自于java,所以Thread中并没有Looper这一套内容,必须我们自己写入。
那么按照上面的思路,我们的代码,就可以如下:
package com.handlerThread;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
/**
* Created by maxiaodong on 16/7/25.
*/
public class ThirdActivity extends Activity
{
Handler threadHandler;
MyThread thread;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
thread = new MyThread();
thread.setName("我是子thread");
thread.start();
try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
threadHandler.sendEmptyMessage(0x1);
}
private class MyThread extends Thread
{
@Override
public void run()
{
Looper.prepare();
// CODE
threadHandler = new Handler(Looper.myLooper())
{
@Override
public void handleMessage(Message msg)
{
Log.v("tag", Thread.currentThread().getName());
}
};
Looper.loop();
}
}
}
打印:
注意,我们在UIThread中通过Handler向子线程发送Handler Message的时候,首先让UIThread-sleep了1s,那么要是没有这个sleep,会如何呢?
在UIThread向子线程发送Handler Message的时候,出现了一个null reference,这是由于子线程的Looper是在其run()函数中创建的,当调用子线程的start()方法,子线程并不一定立即运行,这要看CPU是否刚好分配给它时间片段,要是分配到了,那就会运行子线程run()中的方法,但要是没有CPU时间片段,那么它就需要等待,要是在子线程还没有运行run()方法的时候,UIThread先获得CPU时间片段,那么UIThread就会在子线程之前首先运行到threadHandler.sendEmputyMessage(0x1)的地方,而这个方法会向handler的指向的MessageQueue中发送消息,此时子线程还没有创建自己的Looper,那么自然也还没有MessageQueue,所以就出现了上面的情况。
那么如何解决呢,使用HandlerThread,为什么HandlerThread就可以避免上面的Exception呢,看源码:
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
上面就很明白了,当我们需要获取HandlerThread的Looper对象的时候,要是mLooper == null,则会让当前线程wait()一下,而在HandlerThread中,当mLooper被成功创建的时候,会调用notify()唤醒线程,这样就会让之前wait()的线程唤醒了。
而且,我们会发现HandlerThread的run()方法中,为我们实现了Looper对象的创建过程:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
所以,一般的Thread对象中,我们可以删除run()方法中的super.run(),但是HandlerThread的子类中,一定不要删除run()方法中的super.run(),可以节省我们手动创建Looper的过程。
使用HandlerThread实现UIThread向子线程发送消息的过程如下:
package com.threadHandler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
/**
* Created by maxiaodong on 16/7/25.
*/
public class ThirdActivity extends Activity
{
Handler threadHandler;
MyThread thread;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
thread = new MyThread("我是子thread");
thread.start();
threadHandler = new Handler(thread.getLooper())
{
@Override
public void handleMessage(Message msg)
{
Log.v("tag", Thread.currentThread().getName());
}
};
threadHandler.sendEmptyMessage(0x1);
}
private class MyThread extends HandlerThread
{
public MyThread(String name)
{
super(name);
}
@Override
public void run()
{
super.run();
}
}
}