HandlerThread详解

1 HandlerThread基本原理

  HandlerThread继承自Thread,它是一种可以使用Handler的Thread。它的实现很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。这样,我们就可以直接在HandlerThread中创建Handler了。HandlerThread的run方法如下所示:

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

  既然HandlerThread中已经有了消息循环,那么我们就可以在HandlerThread类或其子类中直接创建Handler对象,但是我们要记住一点:如果我们需要创建与该HandlerThread线程相关联的Handler的话,我们要使用Handler(Looper looper)构造方法,这样我们所创建的Handler对象才会与当前线程的消息循环相关联。在HandlerThread中,我们可以通过getLooper()方法获得Looper实例。

  从HandlerThread的实现上来看,它和普通Thread有显著不同,普通Thread主要在run方法中执行耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体任务。通常,一个普通的Thread线程也可以成为Looper线程,我们只需要为其创建消息队列并开启消息循环即可。看如下实现代码:

class LooperThread extends Thread {
  public Handler mHandler;

  public void run() {
      Looper.prepare();

      mHandler = new Handler() {
          public void handleMessage(Message msg) {
              // process incoming messages here
          }
      };

      Looper.loop();
  }
}

我们可以根据不同应用场景选择不同的实现方式,不过既然Android已经提供了HandlerThread类,那我们在开发过程中可能会更倾向于使用该类完成耗时操作。

2 示例程序

  我们将通过一个简单示例程序来说明HandlerThread的用法。本示例程序中,我们向HandlerThread发送消息,然后HandlerThread返回该消息并显示到TextView上,在这个过程中我们通过sleep来模拟耗时操作。我们将创建一个继承自HandlerThread的类:CustomHandlerThread,

 1 public class CustomHandlerThread extends HandlerThread {
 2     private static final String TAG = "CustomHandlerThread";
 3     private static final int MSG_WHAT = 0x11;
 4     private Handler mResponseHandler;
 5     private Handler mHandler;
 6     private Callback mCallback;
 7     public CustomHandlerThread(Handler responseHandler, Callback callback){
 8         super(TAG);
 9         mHandler = new Handler();
10         mResponseHandler = responseHandler;
11         mCallback = callback;
12     }
13 
14     public void enqueTask(String str, TextView textView){
15         Log.i(TAG, "receive a str: " + str);
16         mHandler.obtainMessage(MSG_WHAT, str).sendToTarget();
17     }
18     public void prepareHandler(){
19         mHandler = new Handler(getLooper()){
20             @Override
21             public void handleMessage(final Message msg) {
22                 try{
23                     TimeUnit.SECONDS.sleep(2);
24                     final String result = (String)msg.obj;
25                     mResponseHandler.post(new Runnable() {
26                         @Override
27                         public void run() {
28                             mCallback.onResult(result);
29                         }
30                     });
31                 }catch (InterruptedException ie){
32                     ie.printStackTrace();
33                 }
34             }
35         };
36     }
37 
38     public interface Callback{
39         public void onResult(String result);
40     }
41     public void removeMsg(){
42         mHandler.removeMessages(MSG_WHAT);
43     }
44 
45 }

  我们先看enqueTask方法,该方法很简单,我们仅用它来接收从外部传来的参数(准确的说是消息内容),并且通过当前线程的Handler将消息发送到当前线程消息队列中,对该消息的处理是在prepareHandler()方法中完成的。在prepareHandler方法中,我们创建Handler实例,并且与当前线程的消息队列相关联,这通过Handler(Looper looper)构造方法完成。mResponseHandler.post方法会将Runnable对象添加到主线程的消息队列,该Runnable对象将会在该mResponseHandler所属的线程中执行,由于mResponseHandler是从主线程传过来的(稍后我们会看到具体实现),因此Runnable将会运行在主线程中。run方法中我们通过回调接口来传递结果。下面我们看下MainActivity中的处理:

 

 1 public class MainActivity extends Activity implements CustomHandlerThread.Callback{
 2     private TextView mTextView;
 3 
 4     private CustomHandlerThread mCustomHandlerThread;
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_main);
 9         mTextView = (TextView)findViewById(R.id.textView);
10 
11         mCustomHandlerThread = new CustomHandlerThread(new Handler(), this);
12         mCustomHandlerThread.start();
13         mCustomHandlerThread.prepareHandler();
14         for(int i = 0; i < 10; i++){
15             mCustomHandlerThread.enqueTask("string"+i);
16         }
17     }
18 
19     @Override
20     public void onResult(String result) {
21         mTextView.setText(result);
22     }
23 
24     @Override
25     protected void onDestroy() {
26         super.onDestroy();
27         mCustomHandlerThread.removeMsg();
28         mCustomHandlerThread.quit();
29     }
30 }

 

上面代码很简单,我们创建CustomHandlerThread并传入Handler对象,该Handler对象是在主线程中创建的,因此与主线程消息队列相关联,创建HandlerThread对象后必须要调用start方法,此外,在调用enqueTask之前必须先调用prepareHandler方法,以免导致空指针异常(Handler还没创建?怎么能向消息队列发送消息呢?)。最后要注意的是,当Activity退出时,我们一定要通过quit方法结束HandlerThread的执行。

 

posted on 2017-01-11 11:29  阳光沙滩  阅读(265)  评论(0编辑  收藏  举报