Android HandlerThread全解析

Handler异步消息处理机制是安卓最常见内容之一,

  1. HandlerThread是什么?⭐⭐⭐⭐⭐

  1. HandlerThread原理和使用场景?⭐⭐⭐⭐

目录

  • 1、HandlerThread是什么?

  • 2、HandlerThread怎么使用

  • 3、源码分析

  • 4、总结

1、HandlerThread是什么?

在安卓开发中,如果需要执行耗时操作,则可以开启子线程来完成,然而手动创建销毁线程又麻烦又消耗系统性能,因此可以使用线程池来完成。如果还需要在线程中使用Handler异步消息机制,或者需要实现子线程和子线程之间的通讯(Handler是主线程和子线程之间的通讯),那么就可以用HandlerThreaad

HandlerThread是Google封装好的一个类,它的内部有自己的Looper对象,可以进行Loop轮询,用于执行多个耗时操作,而不需要多次开启线程,本质是使用Handler和Looper实现的。

2、HandlerThread怎么使用

如果我们需要使用HandlerThread来读取一个大文件的内容,可以这么写:

public class Xuruictivity extends AppCompatActivity {
    private static final int MSG_READ_INFO = 100;
    Handler mMainHandler = new Handler();
    private Handler mThreadHandler;
    private HandlerThread mHandlerThread;
    private boolean isFinish = false; 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_xurui);
        initHandlerThread(); //1
        startHandlerThread(); 
    }

    private void initHandlerThread() {
        mHandlerThread = new HandlerThread("Xurui"); //2
        mHandlerThread.start(); //3 
        mThreadHandler = new Handler(mHandlerThread.getLooper()) { //4
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_READ_INFO: {
                        readFileInfo();
                        // 如果没有读完,则继续
                        if (!isFinish) {
                            mThreadHandler.sendEmptyMessage(MSG_READ_INFO);
                        }
                    }
                    break;
                    default:
                        break;
                }
                
            }
        };
    }

    /**
     * 模拟读取文件
     */
    private void readFileInfo() {
        try {
            //模拟耗时
            Thread.sleep(1200);
            mMainHandler.post(new Runnable() {
                @Override
                public void run() {
                    // 读取大文件内容
                }
            });
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    private void startHandlerThread() {
       mThreadHandler.sendEmptyMessage(MSG_READ_INFO);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mThreadHandler.removeMessages(MSG_READ_INFO); //5
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandlerThread.quit();//6
        mMainHandler.removeCallbacksAndMessages(null); //7
    }
}

使用mHandlerThread的looper创建的mThreadHandler,对应注释4,里面的handleMessage是可以进行耗时操作的,因为它是执行在mHandlerThread所在的子线程。因此其优点是不会阻塞主线程,但是多任务时也需要有序执行,导致执行效率低。因此HandlerThread比较适合耗时不且不会产生较大的阻塞,比如读取文件,操作数据库等,至于网络IO操作这种可能有较大阻塞等,HandlerThread并不适合。

上面代码最关键的步骤都在注释1的函数里。我们来看看HandlerThread的使用方法:

  1. 创建HandlerThread实例:对应注释2,其中“xurui”是子线程的名字,可以随意取;

  1. 执行start函数来启动HandlerThread线程:对应注释3;

  1. 将HandlerThread和Handler绑定:对应注释4;

  1. 然后执行startHandlerThread()就行了,最后记得退出HandlerThread,对应注释6。

为何是这样的步骤,那就进入激动人心的源码分析吧!

3、源码分析

HandlerThread的源码仅仅100+行,因此直接copy完整的源码,不做任何删减(下面源码是android9.0.0的源码)。

public class HandlerThread extends Thread {
     int mPriority;
     int mTid = -1;
     Looper mLooper;
     private @Nullable Handler mHandler;

       public HandlerThread(String name) { //1
         super(name);
         mPriority = Process.THREAD_PRIORITY_DEFAULT;
     }

       /**
      * Constructs a HandlerThread.
      * @param name
      * @param priority The priority to run the thread at. The value supplied must be from
      * {@link android.os.Process} and not from java.lang.Thread.
      */
     public HandlerThread(String name, int priority) { //2
         super(name);
         mPriority = priority;
     }

       /**
      * Call back method that can be explicitly overridden if needed to execute some
      * setup before Looper loops.
      */
     protected void onLooperPrepared() {
     }

       @Override
     public void run() {
         mTid = Process.myTid();
         Looper.prepare(); //3
         synchronized (this) {
             mLooper = Looper.myLooper();
             notifyAll(); //4 
         }
         Process.setThreadPriority(mPriority);
         onLooperPrepared();
         Looper.loop(); //5
         mTid = -1;
     }

       /**
      * This method returns the Looper associated with this thread. If this thread not been started
      * or for any reason 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() { //6
         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(); //7
                 } catch (InterruptedException e) {
                 }
             }
         }
         return mLooper;
     }

       /**
      * @return a shared {@link Handler} associated with this thread
      * @hide
      */
     @NonNull
     public Handler getThreadHandler() {
         if (mHandler == null) {
             mHandler = new Handler(getLooper());
          }
          return mHandler;
      }

        /**
       * 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;
      }

        /**
       * 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;
      }

        /**
       * Returns the identifier of this thread. See Process.myTid().
       */
      public int getThreadId() {
          return mTid;
      }
}  
  1. 构造方法

关键看看注释2的构造函数,其中name是该线程的名字,priority是线程的优先级,在此默认是THREAD_PRIORITY_DEFAULT,这些宏是在 Process 中定义的,值越小,进程优先级越高,主线程的优先级也是THREAD_PRIORITY_DEFAULT。

  1. run方法

在该方法里,执行了很经典的函数,也就是注释3的 Looper.prepare(),详情见本系列Handler小节,该方法主要是创建Looper实例,只有创建了Looper实例,才能将HandlerThread和handler绑定在一起。因此需要先执行start()函数,执行后,线程会交给虚拟机进行调度并自动调用run()方法。

注释5相信看过Handler小节的同学也很清楚了,看看注释4,为何这里会有notifyAll()方法呢?而且还使用了synchronized。

  1. HandlerThread和handler绑定

这里关键是看看getLooper()函数,对应注释6,这里也有synchronized,同时还有wait()方法,众所周知,wait()和notifyAll()是搭配使用来实现线程同步的。这里为了获取到mLooper对象,需要进行线程同步。因为我们希望getLooper()函数只有在线程创建成功且Looper实例也创建成功时才返回,因此在注释7开始阻塞,直到注释4已经创建完Looper实例时才唤醒。

  1. quit和quitSafe方法

最后看看如何退出线程。当调用quit和quitSafe方法就可以退出Looper消息循环并退出线程,从名字上就可以知道后者是“安全退出“。

跟踪这两个方法,会跑到MessageQueue.java里的:

void quit(boolean safe) {
     if (!mQuitAllowed) {
         throw new IllegalStateException("Main thread not allowed to quit.");
     }

     synchronized (this) {
         if (mQuitting) {
             return;
         }
         mQuitting = true;

         if (safe) {
             removeAllFutureMessagesLocked(); //8
         } else {
             removeAllMessagesLocked(); //9
         }

         // We can assume mPtr != 0 because mQuitting was previously false.
         nativeWake(mPtr);
     }
 }

如果安全则调用注释8,不安全调用注释9.继续看源码。removeAllMessagesLocked()只是遍历链表并把所有消息移除掉,最后将mMessages设置为null。而removeAllFutureMessagesLocked()会根据when变量,对应下面注释10,如果当前没有处理消息则同样移除所有消息,否则等待消息处理完毕后再退出循环。

因此这里所谓的安全或者不安全是因为quit()方法无论当前是否有处理消息,都强制移除所有回调,所以不安全。

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) { //10
            removeAllMessagesLocked();
        } else {
            Message n;
            for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}

4、 总结

最后做个总结:

  1. HandlerThread 是内部会创建Looper的线程,因此只能作为子线程使用;

  1. HandlerThread 需要配合 Handler 使用,HandlerThread 的耗时操作在handleMessage()中执行,因为此时执行的是在HandlerThread所在的子线程执行的,但不能像普通线程一样在run()方法中进行,因为HanderThread的run()方法已经被写死。

  1. HandlerThread和Handler绑定之前,必须先调用 mHandlerThread.start() 让 run() 方法跑起来.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值