HandlerThread学习

Author Avatar
第五季 2016.5.14
字数:1,861字 时长:9分钟
  • 微信扫一扫分享

  回想一下,我们要在主线程中使用Handler发消息给子线程的场景。大概类似于下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private Looper myLooper;
private Thread thread = new Thread(new Runnable() {
public void run() {

Looper.prepare();
myLooper = Looper.myLooper();
//do something
Looper.loop();

}
});

private Handler handler = new Handler(myLooper){
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};

handler.sendEmptyMessage(1);

  上面的代码不仅需要自己产生Looper,而且还会有myLooper对象空指针的错误。那么有没有什么方式来避免这些不足呢?答案是有的,那就是使用HandlerThread。

HandlerThread是什么

  首先我们来看一下类签名:

1
2
3
4
5
6
7
/**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
Looper mLooper;
}

  这里很明确HandlerThread继承自Thread,它本质是一个线程。通过注释还可以了解到这个线程拥有一个可以创建Handler的Looper成员属性,这一点通过成员变量mLooper可以佐证。
  既然是一个线程我们很有必要看一下它的run方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}

  有木有觉得这跟我们开场那段代码很像,但是这里还有一些额外的设置,其中最主要的就是synchronized同步代码块这部分了,它就是用来保证mLooper不为空的关键。需要注意的是代码块中还调了notifyAll()方法,既然有notify那很自然就想到什么时间wait的呢,不要着急,我们继续往下看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
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;
}

  可以看出我们在外部通过getLooper方法获取looper对象时会先先判断当前线程是否启动了,如果线程已经启动,那么将会进入同步语句并判断Looper是否为null,为null则代表Looper对象还没有被赋值,也就是还没被创建,此时当前调用线程进入等待阶段,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,最后才返回Looper对象,之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,这样我们就无法保障我们在调用getLooper方法时Looper已经被创建,到这里我们也清楚了为什么在获取mLooper时会存在对象空指针异常了,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,HandlerThread内部通过等待唤醒机制解决了同步问题。
  除此之外HandlerThread还为我们提供了退出消息循环的方法,帮我们解决内存泄漏等隐患:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}

  从源码可以看出当我们调用quit方法时,内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法,这里默认你熟悉Handler消息机制,该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
  还有一个退出的方法quitSafely,暂叫安全的退出方式吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}

  从源码看到跟quit方法只有一处不同就是调用的looper方法不同。这里调用quitSafely方法,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。

  通过上面的分析,我们可以总结HandlerThread的特点如下:
  1、HandlerThread本质上是一个线程类,它继承了Thread;
  2、HandlerThread有自己的内部Looper对象,可以进行looper循环;
  3、获取HandlerThread的looper对象绑定给Handler对象,可以在handleMessage方法中执行异步任务;
  4、创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象;
  5、HandlerThread可以退出消息循环。

怎么使用HandlerThread

  HandlerThread的使用也是很简单的,只需要初始化一个HandlerThread,并将它的getLooper对象绑定给一个Handler就ok了。具体代码如下小例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.app.Activity;
import android.util.Log;

public class MainActivity extends Activity {

private HandlerThread myHandlerThread ;
private Handler handler ;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//创建HandlerThread线程,线程名字:I'm a handlerthread
myHandlerThread = new HandlerThread( "I'm a handlerthread");

//开启HandlerThread线程
myHandlerThread.start();

//在这个线程中创建一个handler对象,并绑定myHandlerThread的looper对象
handler = new Handler( myHandlerThread.getLooper() ){

public void handleMessage(Message msg) {
super.handleMessage(msg);
//这个方法是运行在myHandlerThread线程中的
Log.d( "handler " , "发送的消息是: " + msg.what + " 所在线程为: " + Thread.currentThread().getName());
}
};

//在主线程给handler发送消息
handler.sendEmptyMessage( 1 ) ;

new Thread(new Runnable() {

public void run() {

//在子线程给handler发送数据
handler.sendEmptyMessage( 2 ) ;
}
}).start() ;

}

protected void onDestroy() {
super.onDestroy();

//退出消息循环,释放资源
myHandlerThread.quit() ;
}
}

最后看到的日志输出:

1
2
D/handler: 发送的消息是: 1  所在线程为: I'm a handlerthread
D/handler: 发送的消息是: 2 所在线程为: I'm a handlerthread