• HandlerThread源码理解


    一、概述

            本篇文章是对Android HandlerThread源码分析并应用,谈一下自己对HandlerThread的认知理解,并通过Demo来讲解它的使用方法。

    二、HandlerThread是什么

    2.1 概念

            从字面上来讲,它是 Handler + Thread 的组合,首先它是一个Thread线程,它是用来做耗时任务用的,其次在HandlerThread中已经实现好了Looper消息循环处理,我们在应用的时候只需要通过Handler发送具体的消息给HandlerThread执行对应的任务即可。

    首先明确一点:Handler 是Android系统  同进程  不同线程间 通信用的。

    为了理解上面这句话,我们引入进程和线程的概念

    进程:进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发。

    线程:线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发,每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。

    两者的区别

    1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。

    2. 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)

    比如在某个Activity界面,默认有一个UI主线程,我们称为A线程,在里面创建一个Handler, 然后在创建一个线程B,在里面做耗时任务,待任务完成,在B中通过Handler发送消息,在A中更新UI显示,这样就相当于在把线程B中的消息分发到线程A中处理,从而完成了线程间通信

    为什么呢?因为线程A 和 线程B 可以共享数据段(Handler对象)。

    2.2 特点

    1. HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。

    2. 优点是不会有堵塞,减少对性能的消耗,缺点是不能同时进行多任务的处理,需要排队,处理效率较低。

    3. 与线程池注重并发不同,HandlerThread是一个窜行队列,因为HandlerThread背后只有一个线程。

    三、源码解析

    1. 两个构造方法

    1. //构造方法,设置线程名称,默认线程优先级0
    2. public HandlerThread(String name) {
    3. super(name);
    4. mPriority = Process.THREAD_PRIORITY_DEFAULT;
    5. }
    6. //构造方法,设置线程名称和自定义线程优先级
    7. public HandlerThread(String name, int priority) {
    8. super(name);
    9. mPriority = priority;
    10. }

    2. onLooperPrepared,此方法是 Looper消息循环前的准备工作,可以重写。

    1. /**
    2. * Call back method that can be explicitly overridden if needed to execute some
    3. * setup before Looper loops.
    4. * Looper消息循环前的准备工作,可以重写
    5. */
    6. protected void onLooperPrepared() {
    7. }

    3. run方法:当线程调用start()方法时,回调此方法

    1. @Override
    2. public void run() {
    3. mTid = Process.myTid(); //线程id
    4. Looper.prepare(); // 创建Looper对象和消息队列MessageQueue
    5. synchronized (this) { //通过同步锁机制获取当前线程的Looper对象
    6. mLooper = Looper.myLooper();
    7. notifyAll(); //通知getLooper方法looper已经创建成功,对应getLooper方法中的wait()
    8. }
    9. Process.setThreadPriority(mPriority); //设置线程优先级
    10. onLooperPrepared(); //重写此方法,作用是在消息循环之前进行一些准备工作
    11. Looper.loop(); //开启消息循环
    12. mTid = -1;
    13. }

    4. getLpoper()方法,与run()方法中有个同步锁机制,目的是为了准确且必定要获取当前线程的Looper对象

    1. /**
    2. * This method returns the Looper associated with this thread. If this thread not been started
    3. * or for any reason isAlive() returns false, this method will return null. If this thread
    4. * has been started, this method will block until the looper has been initialized.
    5. * @return The looper.
    6. 此方法返回与此线程关联的Looper。如果此线程尚未启动或不处于活跃状态,
    7. 则此方法将返回null。如果此线程已启动,则此方法将阻塞,直到循环器已初始化。
    8. */
    9. public Looper getLooper() {
    10. if (!isAlive()) {
    11. return null;
    12. }
    13. boolean wasInterrupted = false;
    14. // If the thread has been started, wait until the looper has been created.
    15. // 使用对象同步锁机制:如果线程已开启, 进入等待状态直到looper成功初始化.
    16. synchronized (this) {
    17. while (isAlive() && mLooper == null) {
    18. try {
    19. wait(); //等待Looper初始化完成,对应run方法中的notifyAll
    20. } catch (InterruptedException e) {
    21. wasInterrupted = true;
    22. }
    23. }
    24. }
    25. /*
    26. * We may need to restore the thread's interrupted flag, because it may
    27. * have been cleared above since we eat InterruptedExceptions
    28. */
    29. if (wasInterrupted) {
    30. Thread.currentThread().interrupt();
    31. }
    32. return mLooper;
    33. }

    为了理解上面synchronized同步锁机制,我们通过一个Demo来讲解

    1. /**
    2. * author : me
    3. * date : 22-11-14下午2:49
    4. * desc : 线程测试类
    5. * version: 1.0
    6. */
    7. public class TestMain {
    8. public static void main(String[] args){
    9. Object object = new Object();
    10. MyThreadRunnable myThread1 = new MyThreadRunnable(object,"thread11111");
    11. Thread test1 = new Thread(myThread1);//通过public Thread(Runnable runnable)方法创建线程test1
    12. test1.start();//开始启动
    13. try {
    14. Thread.sleep(6000);
    15. System.out.println("6秒后调用notifiyAll方法唤醒线程,让线程继续执行任务");
    16. synchronized (object) {
    17. object.notifyAll();
    18. }
    19. } catch (InterruptedException e) {
    20. e.printStackTrace();
    21. }
    22. }
    23. }
    24. /**
    25. * author : me
    26. * date : 22-11-14下午2:49
    27. * desc :
    28. * version: 1.0
    29. */
    30. public class MyThreadRunnable implements Runnable {
    31. private Object object;
    32. private String threadName;
    33. public MyThreadRunnable(Object object, String threadName) {
    34. this.object = object;
    35. this.threadName = threadName;
    36. }
    37. @Override
    38. public void run() {
    39. try {
    40. for (int i=1; i<6; i++) {
    41. if (i == 3) {
    42. synchronized (this.object) {
    43. System.out.println("当i==3时,线程调用wait方法 等待");
    44. this.object.wait();
    45. }
    46. }
    47. System.out.println(this.threadName + " i的值: " + i );
    48. Thread.sleep(1000);
    49. }
    50. } catch (InterruptedException e) {
    51. e.printStackTrace();
    52. }
    53. }
    54. }

    打印如下:

    1. thread11111 i的值: 1
    2. thread11111 i的值: 2
    3. 当i==3时,线程调用wait方法 等待
    4. 6秒后调用notifiyAll方法唤醒线程,让线程继续执行任务
    5. thread11111 i的值: 3
    6. thread11111 i的值: 4
    7. thread11111 i的值: 5

    通过此例子,再来理解上面3和4中的代码,就可以明白了。

    5. getThreadHandler()方法

    1. /**
    2. * @return a shared {@link Handler} associated with this thread
    3. * @hide
    4. // 返回与该线程关联的handler对象
    5. //注意这是一个隐藏的方法,无法直接通过 HandlerThread对象调用 getThreadHandler() 获取Handler
    6. */
    7. @NonNull
    8. public Handler getThreadHandler() {
    9. if (mHandler == null) {
    10. mHandler = new Handler(getLooper());
    11. }
    12. return mHandler;
    13. }

    此方法可以忽略,是一个hide方法,无法直接调用获取Handler, 一般还是得通过

    public Handler(@NonNull Looper looper)方法来 获取handler对象, 比如

    1. Handler mThreadHandler = new Handler(myHandlerThread.getLooper()) {
    2. @Override
    3. public void handleMessage(@NonNull Message msg) {
    4. //消息处理
    5. }
    6. }

    6. quit()和quitSafely()方法

    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. //待消息队列中剩余消息全部处理完毕后,退出线程的looper消息循环,安全退出消息循环
    11. public boolean quitSafely() {
    12. Looper looper = getLooper();
    13. if (looper != null) {
    14. looper.quitSafely();
    15. return true;
    16. }
    17. return false;
    18. }

    四、HandlerThread使用步骤

    1. // 步骤1:创建HandlerThread实例对象
    2. HandlerThread mHandlerThread = new HandlerThread("handlerThread");
    3. // 步骤2:启动线程
    4. mHandlerThread.start();
    5. //如果不调用start方法,会报错:
    6. 11-15 17:30:32.347 8763 8763 E AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue' on a null object reference
    7. 11-15 17:30:32.347 8763 8763 E AndroidRuntime: at android.os.Handler.(Handler.java:257)
    8. 11-15 17:30:32.347 8763 8763 E AndroidRuntime: at android.os.Handler.(Handler.java:162)
    9. //通过报错的堆栈log信息找到源码:Handler.java:257
    10. public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    11. mLooper = looper;
    12. mQueue = looper.mQueue; //这行代码报错,空对象异常
    13. mCallback = callback;
    14. mAsynchronous = async;
    15. }
    16. //looper为空对象,说明looper对象没有创建成功。 这也间接地表明 源码中通过同步锁机制必须保证要成功创建Looper对象的缘由。
    17. // 步骤3:创建工作线程Handler 并 复写handleMessage()
    18. //关联HandlerThread的Looper对象、实现消息处理操作与其他线程进行通信
    19. Handler workHandler = new Handler( mHandlerThread.getLooper() ) {
    20. @Override
    21. //消息处理
    22. public boolean handleMessage(Message msg) {
    23. .......
    24. //当然这里可以做耗时任务,因为是处于子线程中,不会堵塞UI线程,不会引起ANR
    25. return true;
    26. }
    27. });
    28. // 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
    29. Message msg = Message.obtain();
    30. msg.what = 1;
    31. workHandler.sendMessage(msg);
    32. // 步骤5:一般在Activity的OnDestory()方法中,结束线程,即停止线程的Looper消息循环
    33. mHandlerThread.quit();

    五、实例演示

    1. /**
    2. * author : me
    3. * date : 22-11-15 下午3:46
    4. * desc : 主界面
    5. * version: 1.0
    6. */
    7. public class MainActivity extends AppCompatActivity {
    8. private Button mButton1;
    9. private Button mButton2;
    10. private MyHandlerThread myHandlerThread;
    11. @Override
    12. protected void onCreate(Bundle savedInstanceState) {
    13. super.onCreate(savedInstanceState);
    14. setContentView(R.layout.activity_main);
    15. mButton1 = findViewById(R.id.btn_one);
    16. mButton2 = findViewById(R.id.btn_two);
    17. //1. 创建HandlerThread实例对象
    18. myHandlerThread = new MyHandlerThread("handlerThread test");
    19. //2. 启动线程
    20. myHandlerThread.start();
    21. //3. 创建工作线程Handler, 并复写handleMessage
    22. Handler mThreadHandler = new Handler(myHandlerThread.getLooper()) {
    23. @Override
    24. public void handleMessage(@NonNull Message msg) {
    25. super.handleMessage(msg);
    26. if (msg.what == 1) {
    27. try {
    28. Log.e("test", "用线程睡眠6秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR");
    29. Thread.sleep(6000);
    30. } catch (InterruptedException e) {
    31. e.printStackTrace();
    32. }
    33. Log.e("test", "====接收主线程中发来的消息 处理消息的线程名称为:" + Thread.currentThread().getName());
    34. } else if (msg.what == 2) {
    35. try {
    36. Log.e("test", "用线程睡眠12秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR");
    37. Thread.sleep(12000);
    38. } catch (InterruptedException e) {
    39. e.printStackTrace();
    40. }
    41. Log.e("test", "====接收自定义线程中发来的消息 处理消息的线程名称为:" + Thread.currentThread().getName());
    42. }
    43. }
    44. };
    45. //点击此按钮: UI主线程 与 HandlerThread线程 进行通信
    46. mButton1.setOnClickListener(new View.OnClickListener() {
    47. @Override
    48. public void onClick(View v) {
    49. Message msg = Message.obtain();
    50. msg.what = 1;
    51. mThreadHandler.sendMessage(msg);
    52. Log.e("test", "调用线程的名称:" + Thread.currentThread().getName());
    53. }
    54. });
    55. //点击此按钮: 自定义线程 与 HandlerThread线程 进行通信
    56. mButton2.setOnClickListener(new View.OnClickListener() {
    57. @Override
    58. public void onClick(View v) {
    59. Thread thread = new Thread(new Runnable() {
    60. @Override
    61. public void run() {
    62. Message msg = Message.obtain();
    63. msg.what = 2;
    64. mThreadHandler.sendMessage(msg);
    65. Log.e("test", "调用线程的名称:" + Thread.currentThread().getName());
    66. }
    67. });
    68. thread.start();
    69. }
    70. });
    71. @Override
    72. protected void onDestroy() {
    73. super.onDestroy();
    74. //退出app时,子线程中的looper要退出,终止消息循环
    75. myHandlerThread.quitSafely();
    76. }
    77. }
    78. /**
    79. * author : me
    80. * date : 22-11-14下午6:09
    81. * desc :
    82. * version: 1.0
    83. */
    84. public class MyHandlerThread extends HandlerThread {
    85. public MyHandlerThread(String name) {
    86. super(name);
    87. }
    88. @Override
    89. protected void onLooperPrepared() {
    90. //如果需要执行某些Looper循环之前的设置,可以在该方法中处理
    91. super.onLooperPrepared();
    92. Log.e("test", "=====onLooperPrepared======");
    93. }
    94. @Override
    95. public boolean quitSafely() {
    96. Log.e("test", "=====quitSafely======");
    97. return super.quitSafely();
    98. }
    99. }

    当进入主界面,点击button1,然后退出app,打印log如下:

    1. 11-16 17:18:49.977 23277 23384 E test : =====onLooperPrepared======
    2. 11-16 17:18:53.121 23277 23277 E test : 调用线程的名称:main
    3. 11-16 17:18:53.121 23277 23384 E test : 用线程睡眠6秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR
    4. 11-16 17:18:59.122 23277 23384 E test : ====接收主线程中发来的消息 处理消息的线程名称为:handlerThread test
    5. 11-16 17:21:33.309 23277 23277 E test : =====quitSafely======

    当进入主界面,点击button2,然后退出app,打印log如下:

    1. 11-16 17:22:43.080 23277 23620 E test : =====onLooperPrepared======
    2. 11-16 17:22:44.758 23277 23622 E test : 调用线程的名称:Thread-2
    3. 11-16 17:22:44.758 23277 23620 E test : 用线程睡眠12秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR
    4. 11-16 17:22:56.759 23277 23620 E test : ====接收自定义线程中发来的消息 处理消息的线程名称为:handlerThread test
    5. 11-16 17:23:06.764 23277 23277 E test : =====quitSafely======

    好了,通过此Demo可以知道Handler可以在不同进程间通信,并且了解HandlerThread的使用方法啦。

    总结

    一. 关于Handler对象创建,取决于与哪个线程的Looper挂钩.

           1. Handler  uiHandler =  new Handler()  或 new  Handler(getMainLooper()) 或 new Handler(Looper.myLooper());   这3个方法都是创建主线程的Handler对象.

           2. Handler  workHandler = new Handler(new  HandlerThread("workthread").getLooper());  它是创建子线程中的Handler对象.

    通过workHandler 这个来发送消息,然后在handleMessage方法中处理任务,因为是任务在子线程中处理,所以不会引起ANR现象.

    二. 使用注意

            HandlerThread在线程内部创建Looper和消息队列,并且在线程启动之后开启消息循环。我们可以使用该Looper构建Handler对象从而绑定工作线程,然后通过Handler向消息队列发送消息,这样HandlerThread就可以取出消息并根据消息类型执行具体的后台任务,这里执行任务是在子线程中,它不会阻塞UI线程,所以不会引起ANR现象。

            由于HandlerThread的run方法是一个无限循环,因此当我们不再使用HandlerThread的时候应该调用它的quit或quitSafely方法来终止线程。

  • 相关阅读:
    Cy3.5-PEG-DPSE,Cy3.5-聚乙二醇-磷脂,DPSE-PEG-Cy3.5
    键值编码KVC与键值监听KVO
    动态规划/背包问题总结/小结——01背包、完全背包
    营收下滑,腾讯游戏还能保持「王者」地位吗?
    esp32如何获得蓝牙地址
    华为交换机:ARP静态绑定技术
    腾讯云标准型s5和s6有什么区别?CPU处理器有差异吗?
    2 个python美化表格数据输出结果的工具,摸鱼简直心安理得~
    5.32 综合案例2.0 - TTS语音云播报(支持M320开发板)
    装修服务预约小程序的内容如何
  • 原文地址:https://blog.csdn.net/u012514113/article/details/127882535