• Android之HandlerThread


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

  • 相关阅读:
    Kotlin系列之序列(Sequences)源码完全解析
    JVM不稳定参数
    akka共享内存
    内存占用过高 kill 调整mysql内存占用
    系统级监控
    linux环境变量
    进程启动,崩溃异常日志++++
    JVM致命错误日志(hs_err_pid.log)分析
    批处理之坑爹的感叹号和变量延迟扩展
    kafka消费端
  • 原文地址:https://www.cnblogs.com/e007/p/6272922.html
Copyright © 2020-2023  润新知