本篇文章是对Android HandlerThread源码分析并应用,谈一下自己对HandlerThread的认知理解,并通过Demo来讲解它的使用方法。
从字面上来讲,它是 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对象)。
1. HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
2. 优点是不会有堵塞,减少对性能的消耗,缺点是不能同时进行多任务的处理,需要排队,处理效率较低。
3. 与线程池注重并发不同,HandlerThread是一个窜行队列,因为HandlerThread背后只有一个线程。
1. 两个构造方法
- //构造方法,设置线程名称,默认线程优先级0
- public HandlerThread(String name) {
- super(name);
- mPriority = Process.THREAD_PRIORITY_DEFAULT;
- }
-
-
- //构造方法,设置线程名称和自定义线程优先级
- public HandlerThread(String name, int priority) {
- super(name);
- mPriority = priority;
- }
-
2. onLooperPrepared,此方法是 Looper消息循环前的准备工作,可以重写。
- /**
- * Call back method that can be explicitly overridden if needed to execute some
- * setup before Looper loops.
- * Looper消息循环前的准备工作,可以重写
- */
- protected void onLooperPrepared() {
-
- }
3. run方法:当线程调用start()方法时,回调此方法
- @Override
- public void run() {
-
- mTid = Process.myTid(); //线程id
-
- Looper.prepare(); // 创建Looper对象和消息队列MessageQueue
-
- synchronized (this) { //通过同步锁机制获取当前线程的Looper对象
-
- mLooper = Looper.myLooper();
-
- notifyAll(); //通知getLooper方法looper已经创建成功,对应getLooper方法中的wait()
- }
- Process.setThreadPriority(mPriority); //设置线程优先级
-
- onLooperPrepared(); //重写此方法,作用是在消息循环之前进行一些准备工作
-
- Looper.loop(); //开启消息循环
-
- mTid = -1;
- }
4. getLpoper()方法,与run()方法中有个同步锁机制,目的是为了准确且必定要获取当前线程的Looper对象
- /**
- * 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.
- 此方法返回与此线程关联的Looper。如果此线程尚未启动或不处于活跃状态,
- 则此方法将返回null。如果此线程已启动,则此方法将阻塞,直到循环器已初始化。
- */
- public Looper getLooper() {
- if (!isAlive()) {
- return null;
- }
-
- boolean wasInterrupted = false;
-
- // If the thread has been started, wait until the looper has been created.
- // 使用对象同步锁机制:如果线程已开启, 进入等待状态直到looper成功初始化.
- synchronized (this) {
- while (isAlive() && mLooper == null) {
- try {
- wait(); //等待Looper初始化完成,对应run方法中的notifyAll
- } catch (InterruptedException e) {
- wasInterrupted = true;
- }
- }
- }
-
- /*
- * We may need to restore the thread's interrupted flag, because it may
- * have been cleared above since we eat InterruptedExceptions
- */
- if (wasInterrupted) {
- Thread.currentThread().interrupt();
- }
-
- return mLooper;
- }
为了理解上面synchronized同步锁机制,我们通过一个Demo来讲解
- /**
- * author : me
- * date : 22-11-14下午2:49
- * desc : 线程测试类
- * version: 1.0
- */
- public class TestMain {
- public static void main(String[] args){
-
- Object object = new Object();
-
- MyThreadRunnable myThread1 = new MyThreadRunnable(object,"thread11111");
-
- Thread test1 = new Thread(myThread1);//通过public Thread(Runnable runnable)方法创建线程test1
-
- test1.start();//开始启动
-
- try {
-
- Thread.sleep(6000);
-
- System.out.println("6秒后调用notifiyAll方法唤醒线程,让线程继续执行任务");
-
- synchronized (object) {
- object.notifyAll();
- }
-
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
-
- /**
- * author : me
- * date : 22-11-14下午2:49
- * desc :
- * version: 1.0
- */
- public class MyThreadRunnable implements Runnable {
-
- private Object object;
- private String threadName;
-
- public MyThreadRunnable(Object object, String threadName) {
- this.object = object;
- this.threadName = threadName;
- }
-
- @Override
- public void run() {
-
- try {
- for (int i=1; i<6; i++) {
- if (i == 3) {
- synchronized (this.object) {
- System.out.println("当i==3时,线程调用wait方法 等待");
- this.object.wait();
- }
- }
- System.out.println(this.threadName + " i的值: " + i );
- Thread.sleep(1000);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
打印如下:
- thread11111 i的值: 1
- thread11111 i的值: 2
- 当i==3时,线程调用wait方法 等待
- 6秒后调用notifiyAll方法唤醒线程,让线程继续执行任务
- thread11111 i的值: 3
- thread11111 i的值: 4
- thread11111 i的值: 5
通过此例子,再来理解上面3和4中的代码,就可以明白了。
5. getThreadHandler()方法
- /**
- * @return a shared {@link Handler} associated with this thread
- * @hide
- // 返回与该线程关联的handler对象
- //注意这是一个隐藏的方法,无法直接通过 HandlerThread对象调用 getThreadHandler() 获取Handler
- */
- @NonNull
- public Handler getThreadHandler() {
- if (mHandler == null) {
- mHandler = new Handler(getLooper());
- }
- return mHandler;
- }
此方法可以忽略,是一个hide方法,无法直接调用获取Handler, 一般还是得通过
public Handler(@NonNull Looper looper)方法来 获取handler对象, 比如
- Handler mThreadHandler = new Handler(myHandlerThread.getLooper()) {
- @Override
- public void handleMessage(@NonNull Message msg) {
- //消息处理
- }
-
- }
6. quit()和quitSafely()方法
- // 直接退出线程的looper消息循环,强制退出循环
- public boolean quit() {
- Looper looper = getLooper();
- if (looper != null) {
- looper.quit();
- return true;
- }
- return false;
- }
-
-
- //待消息队列中剩余消息全部处理完毕后,退出线程的looper消息循环,安全退出消息循环
- public boolean quitSafely() {
- Looper looper = getLooper();
- if (looper != null) {
- looper.quitSafely();
- return true;
- }
- return false;
- }
- // 步骤1:创建HandlerThread实例对象
- HandlerThread mHandlerThread = new HandlerThread("handlerThread");
-
- // 步骤2:启动线程
- mHandlerThread.start();
-
- //如果不调用start方法,会报错:
- 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
- 11-15 17:30:32.347 8763 8763 E AndroidRuntime: at android.os.Handler.
(Handler.java:257) - 11-15 17:30:32.347 8763 8763 E AndroidRuntime: at android.os.Handler.
(Handler.java:162) -
-
- //通过报错的堆栈log信息找到源码:Handler.java:257
- public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
- mLooper = looper;
- mQueue = looper.mQueue; //这行代码报错,空对象异常
- mCallback = callback;
- mAsynchronous = async;
- }
- //looper为空对象,说明looper对象没有创建成功。 这也间接地表明 源码中通过同步锁机制必须保证要成功创建Looper对象的缘由。
-
-
-
- // 步骤3:创建工作线程Handler 并 复写handleMessage()
- //关联HandlerThread的Looper对象、实现消息处理操作与其他线程进行通信
- Handler workHandler = new Handler( mHandlerThread.getLooper() ) {
- @Override
- //消息处理
- public boolean handleMessage(Message msg) {
- .......
- //当然这里可以做耗时任务,因为是处于子线程中,不会堵塞UI线程,不会引起ANR
- return true;
- }
- });
-
-
-
- // 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
- Message msg = Message.obtain();
- msg.what = 1;
- workHandler.sendMessage(msg);
-
-
-
- // 步骤5:一般在Activity的OnDestory()方法中,结束线程,即停止线程的Looper消息循环
- mHandlerThread.quit();
- /**
- * author : me
- * date : 22-11-15 下午3:46
- * desc : 主界面
- * version: 1.0
- */
-
- public class MainActivity extends AppCompatActivity {
-
-
- private Button mButton1;
- private Button mButton2;
-
- private MyHandlerThread myHandlerThread;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- mButton1 = findViewById(R.id.btn_one);
- mButton2 = findViewById(R.id.btn_two);
-
- //1. 创建HandlerThread实例对象
- myHandlerThread = new MyHandlerThread("handlerThread test");
-
- //2. 启动线程
- myHandlerThread.start();
-
- //3. 创建工作线程Handler, 并复写handleMessage
- Handler mThreadHandler = new Handler(myHandlerThread.getLooper()) {
-
- @Override
- public void handleMessage(@NonNull Message msg) {
- super.handleMessage(msg);
- if (msg.what == 1) {
- try {
- Log.e("test", "用线程睡眠6秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR");
- Thread.sleep(6000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- Log.e("test", "====接收主线程中发来的消息 处理消息的线程名称为:" + Thread.currentThread().getName());
- } else if (msg.what == 2) {
- try {
- Log.e("test", "用线程睡眠12秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR");
- Thread.sleep(12000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- Log.e("test", "====接收自定义线程中发来的消息 处理消息的线程名称为:" + Thread.currentThread().getName());
- }
- }
- };
-
-
- //点击此按钮: UI主线程 与 HandlerThread线程 进行通信
- mButton1.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Message msg = Message.obtain();
- msg.what = 1;
- mThreadHandler.sendMessage(msg);
- Log.e("test", "调用线程的名称:" + Thread.currentThread().getName());
- }
- });
-
- //点击此按钮: 自定义线程 与 HandlerThread线程 进行通信
- mButton2.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Thread thread = new Thread(new Runnable() {
- @Override
- public void run() {
- Message msg = Message.obtain();
- msg.what = 2;
- mThreadHandler.sendMessage(msg);
- Log.e("test", "调用线程的名称:" + Thread.currentThread().getName());
- }
- });
- thread.start();
- }
- });
-
-
-
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- //退出app时,子线程中的looper要退出,终止消息循环
- myHandlerThread.quitSafely();
- }
-
- }
-
-
-
- /**
- * author : me
- * date : 22-11-14下午6:09
- * desc :
- * version: 1.0
- */
- public class MyHandlerThread extends HandlerThread {
- public MyHandlerThread(String name) {
- super(name);
- }
-
- @Override
- protected void onLooperPrepared() {
- //如果需要执行某些Looper循环之前的设置,可以在该方法中处理
- super.onLooperPrepared();
- Log.e("test", "=====onLooperPrepared======");
- }
-
- @Override
- public boolean quitSafely() {
- Log.e("test", "=====quitSafely======");
- return super.quitSafely();
- }
- }
当进入主界面,点击button1,然后退出app,打印log如下:
- 11-16 17:18:49.977 23277 23384 E test : =====onLooperPrepared======
- 11-16 17:18:53.121 23277 23277 E test : 调用线程的名称:main
- 11-16 17:18:53.121 23277 23384 E test : 用线程睡眠6秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR
- 11-16 17:18:59.122 23277 23384 E test : ====接收主线程中发来的消息 处理消息的线程名称为:handlerThread test
- 11-16 17:21:33.309 23277 23277 E test : =====quitSafely======
当进入主界面,点击button2,然后退出app,打印log如下:
- 11-16 17:22:43.080 23277 23620 E test : =====onLooperPrepared======
- 11-16 17:22:44.758 23277 23622 E test : 调用线程的名称:Thread-2
- 11-16 17:22:44.758 23277 23620 E test : 用线程睡眠12秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR
- 11-16 17:22:56.759 23277 23620 E test : ====接收自定义线程中发来的消息 处理消息的线程名称为:handlerThread test
- 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方法来终止线程。