• 本篇文章主要讲解一下问题
  • HandlerThread的使用场景以及怎样使用HandlerThread?
    • 使用场景
    • 怎样使用HandlerThread?
    • 完整测试代码如下
  • HandlerThread源码分析
    • 1)首先我们先来看一下它的构造方法
    • 2)接着我们来看一下run()方法,在run方法里面我们可以看到我们会初始化一个Looper,并设置线程的优先级别
    • 3)接着我们来看一下quit方法和quitSafe方法

    我们知道在Android系统中,我们执行完耗时操作都要另外开启子线程来执行,执行完线程以后线程会自动销毁。想象一下如果我们在项目中经常要执行耗时操作,如果经常要开启线程,接着又销毁线程,这无疑是很消耗性能的?那有什么解决方法呢?

    1. 使用线程池
    2. 使用HandlerThread

    本篇文章主要讲解一下问题

    1. HandlerThread的使用场景以及怎样使用HandlerThread?
    2. HandlerThread源码分析

    HandlerThread的使用场景以及怎样使用HandlerThread?

    使用场景

    HandlerThread是Google帮我们封装好的,可以用来执行多个耗时操作,而不需要多次开启线程,里面是采用Handler和Looper实现的。

    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.

    怎样使用HandlerThread?

    1. 创建HandlerThread的实例对象
    1. HandlerThread handlerThread = new HandlerThread("myHandlerThread");

    该参数表示线程的名字,可以随便选择。

    1. 启动我们创建的HandlerThread线程
    1. handlerThread.start();
    1. 将我们的handlerThread与Handler绑定在一起。
      还记得是怎样将Handler与线程对象绑定在一起的吗?其实很简单,就是将线程的looper与Handler绑定在一起,代码如下:
    1. mThreadHandler = new Handler(mHandlerThread.getLooper()) {
    2. @Override
    3. public void handleMessage(Message msg) {
    4. checkForUpdate();
    5. if(isUpdate){
    6. mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    7. }
    8. }
    9. };

    注意必须按照以上三个步骤来,下面在讲解源码的时候会分析其原因

    完整测试代码如下

    1. public class MainActivity extends AppCompatActivity {
    2. private static final int MSG_UPDATE_INFO = 0x100;
    3. Handler mMainHandler = new Handler();
    4. private TextView mTv;
    5. private Handler mThreadHandler;
    6. private HandlerThread mHandlerThread;
    7. private boolean isUpdate = true;
    8. @Override
    9. protected void onCreate(Bundle savedInstanceState) {
    10. super.onCreate(savedInstanceState);
    11. setContentView(R.layout.activity_main);
    12. mTv = (TextView) findViewById(R.id.tv);
    13. initHandlerThread();
    14. }
    15. private void initHandlerThread() {
    16. mHandlerThread = new HandlerThread("xujun");
    17. mHandlerThread.start();
    18. mThreadHandler = new Handler(mHandlerThread.getLooper()) {
    19. @Override
    20. public void handleMessage(Message msg) {
    21. checkForUpdate();
    22. if (isUpdate) {
    23. mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    24. }
    25. }
    26. };
    27. }
    28. /**
    29. * 模拟从服务器解析数据
    30. */
    31. private void checkForUpdate() {
    32. try {
    33. //模拟耗时
    34. Thread.sleep(1200);
    35. mMainHandler.post(new Runnable() {
    36. @Override
    37. public void run() {
    38. String result = "实时更新中,当前股票行情:<font color='red'>%d</font>";
    39. result = String.format(result, (int) (Math.random() * 5000 + 1000));
    40. mTv.setText(Html.fromHtml(result));
    41. }
    42. });
    43. } catch (InterruptedException e) {
    44. e.printStackTrace();
    45. }
    46. }
    47. @Override
    48. protected void onResume() {
    49. isUpdate = true;
    50. super.onResume();
    51. mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    52. }
    53. @Override
    54. protected void onPause() {
    55. super.onPause();
    56. isUpdate = false;
    57. mThreadHandler.removeMessages(MSG_UPDATE_INFO);
    58. }
    59. @Override
    60. protected void onDestroy() {
    61. super.onDestroy();
    62. mHandlerThread.quit();
    63. mMainHandler.removeCallbacksAndMessages(null);
    64. }
    65. }

    运行以上测试代码,将可以看到如下效果图(例子不太恰当,主要使用场景是在handleMessage中执行耗时操作)

    img

    HandlerThread源码分析

    官方源代码如下,是基于sdk23的,可以看到,只有一百多行代码而已。

    1. public class HandlerThread extends Thread {
    2. int mPriority;
    3. int mTid = -1;
    4. Looper mLooper;
    5. public HandlerThread(String name) {
    6. super(name);
    7. mPriority = Process.THREAD_PRIORITY_DEFAULT;
    8. }
    9. public HandlerThread(String name, int priority) {
    10. super(name);
    11. mPriority = priority;
    12. }
    13. /**
    14. * Call back method that can be explicitly overridden if needed to execute some
    15. * setup before Looper loops.
    16. */
    17. protected void onLooperPrepared() {
    18. }
    19. @Override
    20. public void run() {
    21. mTid = Process.myTid();
    22. Looper.prepare();
    23. //持有锁机制来获得当前线程的Looper对象
    24. synchronized (this) {
    25. mLooper = Looper.myLooper();
    26. //发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
    27. notifyAll();
    28. }
    29. //设置线程的优先级别
    30. Process.setThreadPriority(mPriority);
    31. //这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
    32. onLooperPrepared();
    33. Looper.loop();
    34. mTid = -1;
    35. }
    36. public Looper getLooper() {
    37. if (!isAlive()) {
    38. return null;
    39. }
    40. // 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
    41. synchronized (this) {
    42. while (isAlive() && mLooper == null) {
    43. try {
    44. wait();
    45. } catch (InterruptedException e) {
    46. }
    47. }
    48. }
    49. return mLooper;
    50. }
    51. public boolean quit() {
    52. Looper looper = getLooper();
    53. if (looper != null) {
    54. looper.quit();
    55. return true;
    56. }
    57. return false;
    58. }
    59. public boolean quitSafely() {
    60. Looper looper = getLooper();
    61. if (looper != null) {
    62. looper.quitSafely();
    63. return true;
    64. }
    65. return false;
    66. }
    67. /**
    68. * Returns the identifier of this thread. See Process.myTid().
    69. */
    70. public int getThreadId() {
    71. return mTid;
    72. }
    73. }

    1)首先我们先来看一下它的构造方法

    1. public HandlerThread(String name) {
    2. super(name);
    3. mPriority = Process.THREAD_PRIORITY_DEFAULT;
    4. }
    5. public HandlerThread(String name, int priority) {
    6. super(name);
    7. mPriority = priority;
    8. }

    有两个构造方法,一个参数的和两个参数的,name代表当前线程的名称,priority为线程的优先级别

    2)接着我们来看一下run()方法,在run方法里面我们可以看到我们会初始化一个Looper,并设置线程的优先级别

    1. public void run() {
    2. mTid = Process.myTid();
    3. Looper.prepare();
    4. //持有锁机制来获得当前线程的Looper对象
    5. synchronized (this) {
    6. mLooper = Looper.myLooper();
    7. //发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
    8. notifyAll();
    9. }
    10. //设置线程的优先级别
    11. Process.setThreadPriority(mPriority);
    12. //这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
    13. onLooperPrepared();
    14. Looper.loop();
    15. mTid = -1;
    16. }
    • 还记得我们前面我们说到使用HandlerThread的时候必须调用start()方法,接着才可以将我们的HandlerThread和我们的handler绑定在一起吗?其实原因就是我们是在run()方法才开始初始化我们的looper,而我们调用HandlerThread的start()方法的时候,线程会交给虚拟机调度,由虚拟机自动调用run方法:
    1. mHandlerThread.start();
    2. mThreadHandler = new Handler(mHandlerThread.getLooper()) {
    3. @Override
    4. public void handleMessage(Message msg) {
    5. checkForUpdate();
    6. if(isUpdate){
    7. mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    8. }
    9. }
    10. };
    • 这里我们为什么要使用锁机制和notifyAll();,原因我们可以从getLooper()方法中知道
    1. public Looper getLooper() {
    2. if (!isAlive()) {
    3. return null;
    4. }
    5. // 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
    6. synchronized (this) {
    7. while (isAlive() && mLooper == null) {
    8. try {
    9. wait();
    10. } catch (InterruptedException e) {
    11. }
    12. }
    13. }
    14. return mLooper;
    15. }

    总结:在获得mLooper对象的时候存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值。这里等待方法wait和run方法中的notifyAll方法共同完成同步问题。

    3)接着我们来看一下quit方法和quitSafe方法

    1. //调用这个方法退出Looper消息循环,及退出线程
    2. public boolean quit() {
    3. Looper looper = getLooper();
    4. if (looper != null) {
    5. looper.quit();
    6. return true;
    7. }
    8. return false;
    9. }
    10. //调用这个方法安全地退出线程
    11. @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    12. public boolean quitSafely() {
    13. Looper looper = getLooper();
    14. if (looper != null) {
    15. looper.quitSafely();
    16. return true;
    17. }
    18. return false;
    19. }

    跟踪这两个方法容易知道只两个方法最终都会调用MessageQueue的quit(boolean safe)方法

    1. void quit(boolean safe) {
    2. if (!mQuitAllowed) {
    3. throw new IllegalStateException("Main thread not allowed to quit.");
    4. }
    5. synchronized (this) {
    6. if (mQuitting) {
    7. return;
    8. }
    9. mQuitting = true;
    10. //安全退出调用这个方法
    11. if (safe) {
    12. removeAllFutureMessagesLocked();
    13. } else {//不安全退出调用这个方法
    14. removeAllMessagesLocked();
    15. }
    16. // We can assume mPtr != 0 because mQuitting was previously false.
    17. nativeWake(mPtr);
    18. }
    19. }

    不安全的会调用removeAllMessagesLocked();这个方法,我们来看这个方法是怎样处理的,其实就是遍历Message链表,移除所有信息的回调,并重置为null。

    1. private void removeAllMessagesLocked() {
    2. Message p = mMessages;
    3. while (p != null) {
    4. Message n = p.next;
    5. p.recycleUnchecked();
    6. p = n;
    7. }
    8. mMessages = null;
    9. }

    安全地会调用removeAllFutureMessagesLocked();这个方法,它会根据Message.when这个属性,判断我们当前消息队列是否正在处理消息,没有正在处理消息的话,直接移除所有回调,正在处理的话,等待该消息处理处理完毕再退出该循环。因此说quitSafe()是安全的,而quit()方法是不安全的,因为quit方法不管是否正在处理消息,直接移除所有回调。

    1. private void removeAllFutureMessagesLocked() {
    2. final long now = SystemClock.uptimeMillis();
    3. Message p = mMessages;
    4. if (p != null) {
    5. //判断当前队列中的消息是否正在处理这个消息,没有的话,直接移除所有回调
    6. if (p.when > now) {
    7. removeAllMessagesLocked();
    8. } else {//正在处理的话,等待该消息处理处理完毕再退出该循环
    9. Message n;
    10. for (;;) {
    11. n = p.next;
    12. if (n == null) {
    13. return;
    14. }
    15. if (n.when > now) {
    16. break;
    17. }
    18. p = n;
    19. }
    20. p.next = null;
    21. do {
    22. p = n;
    23. n = p.next;
    24. p.recycleUnchecked();
    25. } while (n != null);
    26. }
    27. }
    28. }