前言
多线程的应用在 Android 开发中是非常常见的, 常用方法主要有:
继承 Thread 类
实现 Runnable 接口
- Handler
- AsyncTask
- HandlerThread
今天, 我将献上一份 AsyncTask 使用教程, 希望大家会喜欢
目录
1. 定义
一个 Android 已封装好的轻量级异步类
属于抽象类, 即使用时需 实现子类
- public abstract class AsyncTask<Params, Progress, Result> {
- ...
- }
2. 作用
实现多线程
在工作线程中执行任务, 如 耗时任务
异步通信消息传递
实现工作线程 & 主线程 (UI 线程) 之间的通信, 即: 将工作线程的执行结果传递给主线程, 从而在主线程中执行相关的 UI 操作
从而保证线程安全
3. 优点
方便实现异步通信
不需使用 任务线程(如继承 Thread 类) + Handler 的复杂组合
节省资源
采用线程池的缓存线程 + 复用线程, 避免了频繁创建 & 销毁线程所带来的系统资源开销
4. 类 & 方法介绍
4.1 类定义
AsyncTask 类属于抽象类, 即使用时需 实现子类
- public abstract class AsyncTask < Params,
- Progress,
- Result > {...
- }
- // 类中参数为 3 种泛型类型
- // 整体作用: 控制 AsyncTask 子类执行线程任务时各个阶段的返回类型
- // 具体说明:
- // a. Params: 开始异步任务执行时传入的参数类型, 对应 excute()中传递的参数
- // b. Progress: 异步任务执行过程中, 返回下载进度值的类型
- // c. Result: 异步任务执行完成后, 返回的结果类型, 与 doInBackground()的返回值类型保持一致
- // 注:
- // a. 使用时并不是所有类型都被使用
- // b. 若无被使用, 可用 java.lang.Void 类型代替
- // c. 若有不同业务, 需额外再写 1 个 AsyncTask 的子类
- }
4.2 核心方法
AsyncTask 核心 & 常用的方法如下:
方法执行顺序如下
5. 使用步骤
AsyncTask 的使用步骤有 4 个:
创建 AsyncTask 子类 & 根据需求实现核心方法
创建 AsyncTask 子类的实例对象(即 任务实例)
手动调用 execute(()从而执行异步线程任务
具体介绍如下
- /**
- * 步骤 1: 创建 AsyncTask 子类
- * 注:
- * a. 继承 AsyncTask 类
- * b. 为 3 个泛型参数指定类型; 若不使用, 可用 java.lang.Void 类型代替
- * c. 根据需求, 在 AsyncTask 子类内实现核心方法
- */
- private class MyTask extends AsyncTask<Params, Progress, Result> {
- ....
- // 方法 1:onPreExecute()
- // 作用: 执行 线程任务前的操作
- // 注: 根据需求复写
- @Override
- protected void onPreExecute() {
- ...
- }
- // 方法 2:doInBackground()
- // 作用: 接收输入参数执行任务中的耗时操作返回 线程任务执行的结果
- // 注: 必须复写, 从而自定义线程任务
- @Override
- protected String doInBackground(String... params) {
- ...// 自定义的线程任务
- // 可调用 publishProgress()显示进度, 之后将执行 onProgressUpdate()
- publishProgress(count);
- }
- // 方法 3:onProgressUpdate()
- // 作用: 在主线程 显示线程任务执行的进度
- // 注: 根据需求复写
- @Override
- protected void onProgressUpdate(Integer... progresses) {
- ...
- }
- // 方法 4:onPostExecute()
- // 作用: 接收线程任务执行结果将执行结果显示到 UI 组件
- // 注: 必须复写, 从而自定义 UI 操作
- @Override
- protected void onPostExecute(String result) {
- ...// UI 操作
- }
- // 方法 5:onCancelled()
- // 作用: 将异步任务设置为: 取消状态
- @Override
- protected void onCancelled() {
- ...
- }
- }
- /**
- * 步骤 2: 创建 AsyncTask 子类的实例对象(即 任务实例)
- * 注: AsyncTask 子类的实例必须在 UI 线程中创建
- */
- MyTask mTask = new MyTask();
- /**
- * 步骤 3: 手动调用 execute(Params... params) 从而执行异步线程任务
- * 注:
- * a. 必须在 UI 线程中调用
- * b. 同一个 AsyncTask 实例对象只能执行 1 次, 若执行第 2 次将会抛出异常
- * c. 执行任务中, 系统会自动调用 AsyncTask 的一系列方法: onPreExecute() doInBackground()onProgressUpdate() onPostExecute()
- * d. 不能手动调用上述方法
- */
- mTask.execute();
6. 实例讲解
下面, 我将用 1 个实例讲解 具体如何使用 `AsyncTask`
6.1 实例说明
点击按钮 则 开启线程执行线程任务
显示后台加载进度
加载完毕后更新 UI 组件
期间若点击取消按钮, 则取消加载
如下图
6.2 具体实现
建议先下载源码再看: Carson_Ho 的 Github 地址: AsyncTask
主布局文件: 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"
- android:gravity="center"
- tools:context="com.example.carson_ho.handler_learning.MainActivity">
- <Button
- android:layout_centerInParent="true"
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="点我加载"/>
- <TextView
- android:id="@+id/text"
- android:layout_below="@+id/button"
- android:layout_centerInParent="true"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="还没开始加载!" />
- <ProgressBar
- android:layout_below="@+id/text"
- android:id="@+id/progress_bar"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:progress="0"
- android:max="100"
- style="?android:attr/progressBarStyleHorizontal"/>
- <Button
- android:layout_below="@+id/progress_bar"
- android:layout_centerInParent="true"
- android:id="@+id/cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="cancel"/>
- </RelativeLayout>
主逻辑代码文件: MainActivity.java
- public class MainActivity extends AppCompatActivity {
- // 线程变量
- MyTask mTask;
- // 主布局中的 UI 组件
- Button button,
- cancel; // 加载取消按钮
- TextView text; // 更新的 UI 组件
- ProgressBar progressBar; // 进度条
- /**
- * 步骤 1: 创建 AsyncTask 子类
- * 注:
- * a. 继承 AsyncTask 类
- * b. 为 3 个泛型参数指定类型; 若不使用, 可用 java.lang.Void 类型代替
- * 此处指定为: 输入参数 = String 类型执行进度 = Integer 类型执行结果 = String 类型
- * c. 根据需求, 在 AsyncTask 子类内实现核心方法
- */
- private class MyTask extends AsyncTask < String,
- Integer,
- String > {
- // 方法 1:onPreExecute()
- // 作用: 执行 线程任务前的操作
- @Override protected void onPreExecute() {
- text.setText("加载中");
- // 执行前显示提示
- }
- // 方法 2:doInBackground()
- // 作用: 接收输入参数执行任务中的耗时操作返回 线程任务执行的结果
- // 此处通过计算从而模拟加载进度的情况
- @Override protected String doInBackground(String...params) {
- try {
- int count = 0;
- int length = 1;
- while (count < 99) {
- count += length;
- // 可调用 publishProgress()显示进度, 之后将执行 onProgressUpdate()
- publishProgress(count);
- // 模拟耗时任务
- Thread.sleep(50);
- }
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- return null;
- }
- // 方法 3:onProgressUpdate()
- // 作用: 在主线程 显示线程任务执行的进度
- @Override protected void onProgressUpdate(Integer...progresses) {
- progressBar.setProgress(progresses[0]);
- text.setText("loading..." + progresses[0] + "%");
- }
- // 方法 4:onPostExecute()
- // 作用: 接收线程任务执行结果将执行结果显示到 UI 组件
- @Override protected void onPostExecute(String result) {
- // 执行完毕后, 则更新 UI
- text.setText("加载完毕");
- }
- // 方法 5:onCancelled()
- // 作用: 将异步任务设置为: 取消状态
- @Override protected void onCancelled() {
- text.setText("已取消");
- progressBar.setProgress(0);
- }
- }@Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // 绑定 UI 组件
- setContentView(R.layout.activity_main);
- button = (Button) findViewById(R.id.button);
- cancel = (Button) findViewById(R.id.cancel);
- text = (TextView) findViewById(R.id.text);
- progressBar = (ProgressBar) findViewById(R.id.progress_bar);
- /**
- * 步骤 2: 创建 AsyncTask 子类的实例对象(即 任务实例)
- * 注: AsyncTask 子类的实例必须在 UI 线程中创建
- */
- mTask = new MyTask();
- // 加载按钮按按下时, 则启动 AsyncTask
- // 任务完成后更新 TextView 的文本
- button.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {
- /**
- * 步骤 3: 手动调用 execute(Params... params) 从而执行异步线程任务
- * 注:
- * a. 必须在 UI 线程中调用
- * b. 同一个 AsyncTask 实例对象只能执行 1 次, 若执行第 2 次将会抛出异常
- * c. 执行任务中, 系统会自动调用 AsyncTask 的一系列方法: onPreExecute() doInBackground()onProgressUpdate() onPostExecute()
- * d. 不能手动调用上述方法
- */
- mTask.execute();
- }
- });
- cancel = (Button) findViewById(R.id.cancel);
- cancel.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {
- // 取消一个正在执行的任务, onCancelled 方法将会被调用
- mTask.cancel(true);
- }
- });
- }
- }
运行结果
源码地址
Carson_Ho 的 Github 地址: AsyncTask
7. 使用时的注意点
在使用 AsyncTask 时有一些问题需要注意的:
7.1 关于 生命周期
结论
AsyncTask 不与任何组件绑定生命周期
使用建议
在 Activity 或 Fragment 中使用 AsyncTask 时, 最好在 Activity 或 Fragment 的 onDestory()调用 cancel(boolean);
7.2 关于 内存泄漏
结论
若 AsyncTask 被声明为 Activity 的非静态内部类, 当 Activity 需销毁时, 会因 AsyncTask 保留对 Activity 的引用 而导致 Activity 无法被回收, 最终引起内存泄露
使用建议
AsyncTask 被声明为 Activity 的非静态内部类
7.3 线程任务执行结果 丢失
结论
当 Activity 重新创建时 (屏幕旋转 / Activity 被意外销毁时后恢复), 之前运行的 AsyncTask(非静态的内部类) 持有的之前 Activity 引用已无效, 故复写的 onPostExecute()将不生效, 即无法更新 UI 操作
使用建议
在 Activity 恢复时的对应方法 重启 任务线程
8. 源码分析
知其然 而须知其所以然, 了解 AsyncTask 的源码分析有利于更好地理解 AsyncTask 的工作原理
具体请看文章: Android 多线程: AsyncTask 的原理 及其源码分析
9. 总结
本文全面介绍了多线程中的 AsyncTask, 含使用方法工作原理 & 源码分析
来源: https://juejin.im/entry/5a80e2a9f265da4e9c6326e4