HandlerThread使用&源码解析

全面解析Android开发中HandlerThread的原理与应用,包括定义、作用、使用方法及源码分析。

前言

  • 多线程的应用在Android开发中是非常常见的,常用方法主要有:

    1. 继承Thread类
    2. 实现Runnable接口
    3. Handler
    4. AsyncTask
    5. HandlerThread
  • 今天,我将全面解析多线程其中一种常见用法:HandlerThread

由于本文涉及多线程知识和Handler源码解析,所以阅读本文前建议先看: 
Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)


目录

目录


1. 定义

  • Android API提供的一个封装类
  • 继承自Thread类

2. 作用

  • 通过继承Thread类,实现快速地创建一个带有Looper的新工作线程的功能
  • 通过对Handler类使用的封装,实现快速创建Handler并与其他线程进行通信的功能

总结

HandlerThread本质上是通过继承Thread类封装Handler类的使用,从而使得创建新线程和与其他线程进行通信变得更加方便易用


3. 使用对比

3.1 Thread+Handler用法

因为HandlerThread本质上是继承Thread类和对Handler类使用的封装,所以我们先来看下一般情况下使用Thread+Handler创建线程的用法: 
1. 使用Thread类创建一个新的线程 
2. 通过创建Handler与其他线程进行通信

如果你还没阅读过Handler的源码,强烈建议先看文章: 
Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)

Thread+Handler的一般用法

Handler mHandler;
private void Thread_With_Handler() {
//步骤1:实例化Thread类对象
  new Thread() {
      @Override
        public void run() {
            super.run();
            //步骤2:创建与当前线程绑定的Looper实例,并初始化MessageQueue
            Looper.prepare();
            //步骤3:生成Handler实例并与上面创建的Looper进行绑定
            mHandler = new Handler(Looper.myLooper());
            //步骤4:实现消息循环
            Looper.loop();
        }
    }.start();
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在注释里已经非常清晰了,接下来我将继续介绍更加方便易用的HandlerThread

3.2 HandlerThread用法

  • HandlerThread的本质:继承Thread类和对Handler类使用的封装。
  • 我们先来看HandlerThread的具体使用步骤
//步骤1:创建HandlerThread的实例对象=已经创建了一个新线程
//参数=线程名字,作用是标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");

//步骤2:启动线程
mHandlerThread.start();

//步骤3:创建工作线程Handler,实现消息处理的操作,并与其他线程进行通信
Handler mHandler = new Handler( handlerThread.getLooper() ) {
            @Override
            public boolean handleMessage(Message msg) {
                //消息处理
                return true;
            }
        });

//步骤4:结束线程,即停止线程的消息循环
mHandlerThread.quit();
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 是不是感觉 HandlerThread更加清晰、简单、易用?!
  • 接下来,我们开始对HandlerThread的用法进行源码分析,看一下HandlerThread是如何通过继承Thread类和封装Handler类用法从而使得多线程通信变得如此方便。

4. HandlerThread源码分析

结合HandlerThread上面的用法和本质来看,其实主要是分析: 
- 如何创建新线程,即分析

HandlerThread mHandlerThread = new HandlerThread("handlerThread");
 
  • 1
  • 1
  • 如何创建Handler并进行通信,即分析
mHandlerThread.start();
Handler mHandler = new Handler( handlerThread.getLooper()) ;
 
  • 1
  • 2
  • 1
  • 2
  • 如何结束线程,即分析
mHandlerThread.quit();
 
  • 1
  • 1
以下是HandlerThread类的源码(注释标注得非常清晰)

public class HandlerThread extends Thread {
    //线程优先级
    int mPriority;
    //当前线程id
    int mTid = -1;
    //当前线程持有的Looper对象
    Looper mLooper;

/*分析1:如何创建新线程*/

   //HandlerThread在进行实例化的同时,已经创建了一个新线程,这说明猫腻是在构造方法
   //HandlerThread类有两个构造方法
   //不同之处就是设置当前线程的优先级参数。你可以根据自己的情况来设置优先级,也可以使用默认优先级。

    //方法1. 默认优先级
    public HandlerThread(String name) {
        //通过调用父类默认的方法创建线程
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    //方法2. 根据需求设置优先级-构造方法带优先级参数
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

/*分析2:如何创建Handler*/

    //步骤1:通过run()方法作好创建Handler的准备
    @Override
    public void run() {
        //获得当前线程的id
        mTid = Process.myTid();
        //创建了一个Looper对象并初始化了一个MessageQueue
        Looper.prepare();
        //持有锁机制来获得当前线程的Looper对象
        synchronized (this) {
            mLooper = Looper.myLooper();

            //通知getLooper方法中的wait当前线程已经创建mLooper对象成功,让wait结束等待
            notifyAll();
        }
        //设置当前线程的优先级
        Process.setThreadPriority(mPriority);
        //在线程循环之前做一些准备工作
        //该方法实现体是空的,子类可以实现该方法,也可以不实现。
        onLooperPrepared();
        //进行消息循环,即不断从MessageQueue中取消息和派发消息
        Looper.loop();
        mTid = -1;
    }
}

    //线程循环前的准备工作
    protected void onLooperPrepared() {
    }

    //步骤2:在创建Handler时调用了getLooper()
    //Handler mHandler = new Handler( handlerThread.getLooper())
    //getLooper()的作用主要是获得当前HandlerThread线程中的mLooper对象
    public Looper getLooper() {
        //如果线程不是存活的,则直接返回null
        if (!isAlive()) {
            return null;
        } 

        synchronized (this) {
      //首先判断当前线程是否存活
      //如果不是存活的,这直接返回null。
      //其次如果当前线程存活的,在判断线程的成员变量mLooper是否为null,如果为null,说明当前线程已经创建成功,但是还没来得及创建Looper对象
            while (isAlive() && mLooper == null) {
                try {
      //因此,这里会调用wait方法去等待
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        //当run方法中的notifyAll方法调用之后通知当前线程的wait方法结束等待并跳出循环
        //最终getLooper()返回的是我们在run方法中创建的mLooper
        return mLooper;
    }

/*分析3:结束新线程*/

  //Handler有两种让当前线程退出循环的方法:quit() 和 quitSafely()
  //第一种:效率高,但线程不安全
  public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
  //第二种:效率低,但线程安全
  public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

注意:

  • 在获得mLooper对象的时候存在一个同步的问题:只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,才能将创建的Handler与该Looper绑定
  • 解决方案:在run()中成功创建Looper对象后,立即调用notifyAll()通知 getLooper()中的wait()结束等待,并返回run()中成功创建的Looper对象,使得Handler与该Looper对象绑定

5. 实例使用

5.1 实例说明:
  • 目的:点击按钮实现延迟操作并更新UI组件
  • 使用方法:HandlerThread类

在看下面例子之前我建议你们先去下载Demo,边下边读效果更佳哦!

源码地址:Demo for HandlerThread

5.2 具体代码
  1. 主布局文件 
    activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

 tools:context="com.example.carson_ho.handlerthread.MainActivity">
//用于显示结果
    <TextView
        android:id="@+id/text1"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="显示结果" />
//点击按钮1和2实现相应延时操作 & UI组件更新
    <Button
        android:id="@+id/button1"
        android:layout_centerInParent="true"
        android:layout_below="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="延迟3秒显示我爱学习"/>

    <Button
        android:id="@+id/button2"
        android:layout_centerInParent="true"
        android:layout_below="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="延迟10秒显示我不爱学习"/>
//点击按钮结束线程
    <Button
        android:id="@+id/button3"
        android:layout_centerInParent="true"
        android:layout_below="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="结束线程的消息循环"/>
</RelativeLayout>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  1. MainActivity文件

MainActivity.Java(注释已经标注得非常清楚)

package com.example.carson_ho.handlerthread;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    HandlerThread mHandlerThread;
    Handler mainHandler,workHandler;
    TextView text;
    Button button1,button2,button3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //显示文本
        text = (TextView) findViewById(R.id.text1);

        //创建与主线程关联的Handler
        mainHandler = new Handler();

        //创建后台线程
        initBackground();

        //点击Button1-通过SendMessage方法将0x121消息发送至MessageQueue
        //在工作线程中,当消息循环时取出0x121消息并在工作线程执行相关操作
        button1 = (Button) findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                workHandler.sendEmptyMessage(0x121);
            }
        });

        //点击Button2-通过SendMessage方法将0x122消息发送至MessageQueue
        //在工作线程中,当消息循环时取出0x122消息并在工作线程执行相关操作
        button2 = (Button) findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                workHandler.sendEmptyMessage(0x122);
            }
        });


        //点击Button3-退出消息循环
        button3 = (Button) findViewById(R.id.button3);
        button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                mHandlerThread.quit();
            }
        });

    }

    private void initBackground(){
        //通过实例化mHandlerThread从而创建新线程
        mHandlerThread = new HandlerThread("handlerThread");
        //启动新线程
        mHandlerThread.start();

        //创建与工作线程相关联的Handler,并与mHandlerThread所创建的Looper相关联
        //实现了Handler与工作线程相关联
        //下面HandlerMessage的方法体均会在mHandlerThread所创建的工作线程中执行
        workHandler = new Handler(mHandlerThread.getLooper()){
            @Override
            //消息处理的操作
            public void handleMessage(Message msg)
            {
                //设置了两种消息处理操作,通过msg来进行识别
                switch(msg.what){
                    //标识1:0x121
                    case 0x121:
                        try {
                            //延时操作
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //通过主线程Handler.post方法进行在主线程的UI更新操作
                        //可能有人看到new了一个Runnable就以为是又开了一个新线程
                        //事实上并没有开启任何新线程,只是使run()方法体的代码抛到与mHandler相关联的线程中执行,我们知道mainHandler是与主线程关联的,所以更新TextView组件依然发生在主线程
                        mainHandler.post(new Runnable() {
                            @Override
                            public void run () {
                                text.setText("我爱学习");
                            }
                        });
                        break;

                    //标识2:0x122
                    case 0x122:
                        try {
                            Thread.sleep(10000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        mainHandler.post(new Runnable() {
                            @Override
                            public void run () {
                                text.setText("我不喜欢学习");
                            }
                        });
                        break;
                    default:
                        break;
                      }
            }
        };
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123

5.3 运行结果

未点击状态

点击了“延迟3秒”按钮

点击了“延迟10秒”按钮

5.4 特别注意

从上面的例子中会出现两个细节问题: 
5.4.1 细节问题1:内存泄露 
在上面的例子中,你会发现出现了严重的警告:

In Android, Handler classes should be static or leaks might occur.
 
  • 1
  • 1

即造成了严重的内存泄漏,关于Handler的内存泄露请参考 Android开发:详解Handler的内存泄露

5.4.2 细节问题2:连续发送消息

  • 当你连续点击3下时,发现并没有马上按照最新点击的按钮的操作去显示,而是按顺序的一个个显示出来
  • 原因:使用HandlerThread时只是开了一个工作线程,当你点击了x下后,只是将x个消息发送到消息队列MessageQueue里排队,等候派发消息给Handler再进行对应的操作

5.5 Demo的源码地址

源码地址放在Github上:Demo for HandlerThread

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值