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的执行。