转 Android 多线程:手把手教你使用AsyncTask
转自:https://www.jianshu.com/p/ee1342fcf5e7
前言
多线程的应用在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
的使用步骤有3个:
- 创建
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);
}
});
}
}
- 运行结果
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的原理 及其源码分析
作者:Carson_Ho
链接:https://www.jianshu.com/p/ee1342fcf5e7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
转 Android 多线程:手把手教你使用AsyncTask的更多相关文章
- Android多线程分析之五:使用AsyncTask异步下载图像
Android多线程分析之五:使用AsyncTask异步下载图像 罗朝辉 (http://www.cnblogs.com/kesalin) CC 许可,转载请注明出处 在本系列文章的第一篇<An ...
- 使用Android Studio手把手教你将应用打包+代码混淆
最近几天用Google的Design库写了个App,使用Android Studio将app打包时遇到的几个瓶颈,所以把详细步骤写入下来. AS中怎么获取应用签名 这和eclipse不同,eclips ...
- Android:手把手教你 实现Activity 与 Fragment 相互通信,发送字符串信息(含Demo)
前言Activity 与 Fragment 的使用在Android开发中非常多今天,我将主要讲解 Activity 与 Fragment 如何进行通信,实际上是要解决两个问题: Activity 如何 ...
- 【转】手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)
1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理! 我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...
- 手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)
1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理! 我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...
- Android多线程任务优化1:探讨AsyncTask的缺陷
AsyncTask还有别的缺陷,在生成listview的时候,如果adapter里面的count动态改变的话,不能使用AsyncTask,只能使用Thread+Handler,否则会出现如下错误 j ...
- Android开发之手把手教你写ButterKnife框架(三)
欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52672188 本文出自:[余志强的博客] 一.概述 上一篇博客讲了, ...
- Android开发之手把手教你写ButterKnife框架(二)
欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52664112 本文出自:[余志强的博客] 上一篇博客Android开 ...
- Android:手把手教你打造可缩放移动的ImageView(下)
在上一篇Android:手把手教你打造可缩放移动的ImageView最后提出了一个注意点:当自定义的MatrixImageView如ViewPager.ListView等带有滑动效果的ViewGrou ...
随机推荐
- Laravel/Lumen 分组求和问题 where groupBy sum
在Laravel中使用分组求和,如果直接使用Laravel各数据库操作方法,应该会得出来如下代码式: DB::table('table_a') ->where('a','=',1) ->g ...
- 这些年我@yangbodong22011参与的开源
2020年第一天,水一篇博客,对新年起码的尊重.这里记录下我参与的开源项目情况. Talk is cheap. Show me the code Linus Torvalds Jedis PR:htt ...
- ONVIF协议客户端
前几天跟大家聊了一些关于ONVIF的一些基础知识,它的工作原理以及优势.今天安徽思蔷信息科技为带大家了解一下simpleonvif 百度云盘下载地址:链接:https://pan.baidu.com/ ...
- Django 小实例S1 简易学生选课管理系统 总目录
python Django实现的一个简易的教务选课系统. 介绍与演示的视频版本已发到我的b站: https://www.bilibili.com/video/BV1er4y1w7ty. 项目已上传到我 ...
- 1组-Alpha冲刺-3/6
一.基本情况 队名:震震带着六菜鸟 组长博客:https://www.cnblogs.com/Klein-Wang/p/15544334.html 小组人数:7人 二.冲刺概况汇报 王业震 过去两天完 ...
- Python-Unittest多线程执行用例
前言 假设执行一条脚本(.py)用例一分钟,那么100个脚本需要100分钟,当你的用例达到一千条时需要1000分钟,也就是16个多小时... 那么如何并行运行多个.py的脚本,节省时间呢?这就用到多线 ...
- [loj6736]最小连通块
定义$f(S)$表示点集$S$的最小连通块 做法1 通过对所有节点判定,可以在$n$次询问中求出具体的$f(S)$ 对于$x\ne y$,显然$(x,y)\in E$当且仅当$f(\{x,y\})=\ ...
- Spring Cloud Gateway过滤器精确控制异常返回(分析篇)
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 在<Spring Cloud Gate ...
- 【JavaSE】类与类的关系--UML
类(对象/接口)之间的关系 -- UML类图展现 2019-07-14 14:37:19 by冲冲 在面向对象程序设计时,类与类之间的关系主要分为:继承,实现,依赖,关联,聚合,组合等6种关系. ...
- 解决:ElasticsearchException[X-Pack is not supported and Machine Learning is not available for
解决:在config/elasticsearch.yml添加一条配置: xpack.ml.enabled: false 能启动了就. 在docker安装出现这个问题,目前还没解决. 算了用win10得 ...