继承Thread类
定义
- Thread类=java中实现多线程的具体类
- 封装了所需的线程操作
作用
- 实现多线程
优缺点
- 优点
- 实现简单,只要继承了Thread类&复写run()即可实现多线程操作
- 缺点
- 局限性大
- 必须继承Thread类
- java规定的单继承,即继承了Thread类后就不能再继承其他的类了
- 不适合资源共享
- 1个线程=1个实例对象
- 相对独立,无法进行资源共享
- 消耗资源
- Thread线程=一次性消费品(一次性筷子)
- 1个线程=1个耗时任务,有100个耗时任务就得开100个线程
- 多次创建和销毁线程,十分耗费系统资源
- 局限性大
使用
- 继承Thread类
- 复写run()
- 创建线程对象
- 通过 线程对象 控制线程状态(运行,睡眠,挂起,停止)
1 | // 常规方式 |
1 | // 步骤1:采用匿名类,直接 创建 线程类的实例 |
实现Runnable接口
定义
- 一个与多线程相关的抽象接口
- 仅定义了1个方法=run()
作用
- 实现多线程
优缺点
- 适合资源共享
- Runnable的代码能被多个线程(Thread实例共享)
- 适合多个线程处理同一资源的情况
- 灵活
- 一个类可以实现多个接口
- 避免了 继承Thread类方式 所导致的单继承局限性
使用
- 创建线程辅助类(实现Runnable接口)
- 复写run()(定义线程行为)
- 创建线程辅助对象(实例化 线程辅助类)
- 创建线程对象(实例化线程类,线程类 = Thread类,创建时传入线程辅助类对象)
- 通过线程对象控制线程状态(运行,睡眠,挂起,停止)
特别注意:
Java
中真正能创建新线程的只有Thread
类对象- 通过实现
Runnable
的方式,最终还是通过Thread类对象来创建线程
所以对于 实现了
Runnable
接口的类,称为 线程辅助类;Thread
类才是真正的线程类
1 | // 步骤1:创建线程辅助类,实现Runnable接口 |
1 | // 步骤1:通过匿名类 直接 创建线程辅助对象,即 实例化 线程辅助类 |
Handler
定义
- 一套Android消息传递机制/异步通信机制
作用
- 工作线程更新UI
- 在多线程的应用场景中,将工作线程中需要更新UI的操作信息,传递到UI主线程
- 从而实现工作线程对UI的更新处理,最终实现异步消息的处理
- 多线程和异步通信
使用
使用步骤:
- 写一个内部类继承Handler并实现handlerMessage方法
- 在主线程中创建Handler实例
- 在子线程中写Message以及sendMessage
场景:
由于
Handler
的作用 = 将工作线程需操作UI的消息 传递 到主线程,使得主线程可根据工作线程的需求 更新UI,从而避免线程操作不安全的问题故下文的实例 = 1个简单 “更新
UI
操作” 的案例主布局文件相同 = 1个用于展示的
TextView
,具体如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14<?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"
android:gravity="center"
tools:context="com.example.carson_ho.handler_learning.MainActivity">
<TextView
android:id="@+id/show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="" />
</RelativeLayout>
handler.sendMessage()
- 分为以下两种
新建Handler子类(内部类)
1 | public class MainActivity extends AppCompatActivity { |
匿名Handler子类
1 | public class MainActivity extends AppCompatActivity { |
handler.post()
1 | public class MainActivity extends AppCompatActivity { |
handler的内存泄漏问题
原因
- 存在 未被处理/正处理的消息->Handler实例->外部类 的引用关系
- Handler的生命周期>外部类的生命周期
- 即:Handler消息队列还有未处理的消息/正在被处理的消息,而外部类需要销毁
解决办法
静态内部类+弱引用
代码如下:
1 | public class MainActivity extends AppCompatActivity { |
AsyncTask
定义:
一个Android已封装好的轻量级异步类
属于抽象类,使用时需要实现子类
作用
1.实现多线程
在工作线程中执行任务,如:耗时任务
2.异步通信,消息传递
实现工作线程和主线程之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的ui操作
优缺点
优点:
方便实现异步通信
不需要使用”任务线程(如继承Thread类) + Handler”的复杂组合
节省资源
采用线程池的缓存线程+复用线程,避免了频繁创建和销毁线程所带来的系统资源开销
使用
类定义
AsyncTask
类属于抽象类,即使用时需 实现子类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public abstract class AsyncTask<Params, Progress, Result> {
...
}
// 类中参数为3种泛型类型
// 整体作用:控制AsyncTask子类执行线程任务时各个阶段的返回类型
// 具体说明:
// a. Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数
// b. Progress:异步任务执行过程中,返回下载进度值的类型
// c. Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致
// 注:
// a. 使用时并不是所有类型都被使用
// b. 若无被使用,可用java.lang.Void类型代替
// c. 若有不同业务,需额外再写1个AsyncTask的子类
}核心方法
3.使用步骤
AsyncTask
的使用步骤有3个:
- 创建
AsyncTask
子类 & 根据需求实现核心方法 - 创建
AsyncTask
子类的实例对象(即 任务实例) - 手动调用
execute(()
从而执行异步线程任务
- 具体介绍如下
1 | /** |
实例
- 点击按钮 则 开启线程执行线程任务
- 显示后台加载进度
- 加载完毕后更新UI组件
- 期间若点击取消按钮,则取消加载
1 | <?xml version="1.0" encoding="utf-8"?> |
1 | public class MainActivity extends AppCompatActivity { |
AsyncTask的内存泄漏问题
- 结论
若AsyncTask
被声明为Activity
的非静态内部类,当Activity
需销毁时,会因AsyncTask
保留对Activity
的引用 而导致Activity
无法被回收,最终引起内存泄露 - 使用建议
AsyncTask
应被声明为Activity
的静态内部类
HandlerThread
定义
一个Android已经封装好的轻量级异步类
作用
实现多线程,异步通信,消息传递
优点
方便实现异步通信
不需要使用”任务线程(如继承Thread类) + Handler”的复杂组合
工作原理
通过继承Thread类,快速地创建1个带有looper对象的新工作线程
通过封装Handler类,快速创建Handler与其他线程进行通信
使用
HandlerThread
的本质:继承Thread
类 & 封装Handler
类HandlerThread
的使用步骤分为5步
1 | // 步骤1:创建HandlerThread实例对象 |
实例
- 点击按钮实现延迟操作
- 最终更新UI组件
1 | <?xml version="1.0" encoding="utf-8"?> |
1 | public class MainActivity extends AppCompatActivity { |