1, 引言
一般情况下Android中新诞生的线程是没有开启消息循环的。主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。 Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。然后通过Looper.loop() 让Looper开始工作,从消息队列里取消息,处理消息。如果想在子线程开启消息循环呢?
Class mythread extends Thread{
@Override
public void run() {
Looper.prepare();
Handler Myhandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 2:
•••
break;
}
};
Message reset = mHandler.obtainMessage();
reset.what = 2;
Myhandler.sendMessageDelayed(test,1000);
Looper.loop();
}
}
注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
说了这么多,还没有说到正题,这些和HandlerThread有什么关系呢?
2,HandlerThread 使用方法
在一个activity中, HandlerThread用法如下,
protected void onCreate(Bundle savedInstanceState) {
•••
//创建一个线程,线程名字:handler-thread
myHandlerThread = new HandlerThread( "handler-thread") ;
//开启一个线程
myHandlerThread.start();
//在这个线程中创建一个handler对象
handler = new Handler( myHandlerThread.getLooper() ){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
Log.d( "handler " , "消息: " + msg.what + " 线程: " +
Thread.currentThread().getName() ) ;
}
};
//在主线程给handler发送消息
handler.sendEmptyMessage( 1 ) ;
new Thread(new Runnable() {
@Override
public void run() {
//在子线程给handler发送数据
handler.sendEmptyMessage( 2 ) ;
}
}).start() ;
}
@Override
protected void onDestroy() {
super.onDestroy();
//释放资源
myHandlerThread.quit() ;
}
运行效果:
/com.app D/handler: 消息: 1 线程: handler-thread
/com.app D/handler: 消息: 2 线程: handler-thread
由此可见, HandlerThread的主要作用如下,
1,HandlerThread将loop转到子线程中处理,说白了就是将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
2,开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理。HandlerThread本质是一个线程,在线程内部,代码是串行处理的。
3,但是由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
4,HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
5,对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。
3,HandlerThread 源码
HandlerThread定义如下,
public class HandlerThread extends Thread {
int mPriority; // 线程优先级
int mTid = -1;
Looper mLooper;
HandlerThread继承于Thread,并且有自己的Looper。HandlerThread的2个构造方法如下,
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
3.1 start方法
Start方法是调用父类Thread的方法, 随之的run方法如下,
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper(); // 初始化mLooper对象
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
看到了吧,在该方法中调用了Looper的prepare和loop方法,并且初始化mLooper对象。几乎和android进程中的UI线程一样。
3.2 getLooper方法
构造Handler对象是,会调用HandlerThread的getLooper方法,源码如下,
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;
}
返回run方法中得到的mLooper对象。
4,小结
说到这里, HandlerThread的实质就是在子线程中处理消息循环。如果不用HandlerThread的话,需要向引言中的那样手动调用Looper的prepare和loop方法。
run方法里面当mLooper创建完成后有个notifyAll(),getLooper方法中有个wait(),这是为什么呢?因为mLooper在一个子线程中执行,而我们的handler是在UI线程初始化的,也就是说,必须等到mLooper创建完成,才能正确的返回getLooper();wait(),notify()就是为了解决这两个线程的同步问题。