一、什么是AsyncTask
在之前的文章异步消息处理机制学习笔记中有简单介绍Android的异步消息处理机制,但实际运用上,Android提供了更加好用的工具——AsyncTask。AsyncTask背后的实现原理也是基于异步消息处理机制的,只是Android帮我们做了很好的封装而已,其主要功能还是完成了子线程和主线程之间的消息传递。
二、AsyncTask的基本用法
1.AsyncTask是一个抽象类,因此使用AsyncTask的时候需要继承它,同时要为AsyncTask指定三个泛型参数:
Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用该泛型作为进度单位
Result:当任务执行完毕后,如果需要对结果进行返回,则使用该泛型作为返回值类型
2.除了指定上述的三个泛型之外,我们通常还要重写下面的四个方法:
onPreExecute():
该方法会在后台任务开始执行之前调用,通常用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
doInBackground(Params...):
该方法接收的参数即为我们指定的第一个泛型,其返回值即为我们指定的第三个泛型。这个方法中所有的代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。如果AsyncTask的第三个泛型指定的是Void,那么就可以不返回任务执行结果。如果在该方法体内,需要反馈任务进度,则可以调用publishProgress(Progress...)方法来完成。
3.onProgressUpdate(Progress...):
当我们在后台任务中调用了publishProgress(Progress...)方法后,该方法便很快被调用。它所接收的参数就是publishProgress(Progress...)方法的传参,也是AsyncTask的第二个泛型。通常在这个方法中对UI进行操作更新。
4.onPostExecute(Result):
该方法接收的参数即为我们指定的第三个泛型。当后台任务执行完毕并通过return语句进行返回时,该方法便很快被调用。通常在这个方法中提醒任务执行结果以及关闭进度条对话框等。
三、AsyncTask的简单运用
示例场景:这里我创建了一个下载任务,通过AsyncTask在后台执行下载任务,然后切换的主线程更新UI上的下载进度。
1.创建一个下载任务类DownloadTask去继承AsyncTask并指定它的三个泛型
/**
* 类描述:下载功能类
* 第一个泛型 String 表示在执行AsyncTask的时候需要传入一个字符串参数给后台任务
* 第二个泛型 Integer 表示用整型数据来作为进度显示单位
* 第三个泛型 Integer 表示用整型数据来反馈执行结果
*/
public class DownloadTask extends AsyncTask<String,Integer,Integer>
2.在DownloadTask的构造器中传入一个下载监听器
public DownloadTask(DownloadListener downloadListener){
this.downloadListener = downloadListener;
}
下载监听器DownloadListener是一个自定义的接口,通过它去监听下载过程中的各个状态,从而回调其中相应的方法
public interface DownloadListener {
void onProgress(int progress);
void onSuccess();
void onFailed();
void onPaused();
void onCanceled();
}
3.重写doInBackground()方法实现后台下载功能
@Override
protected Integer doInBackground(String... strings) {
RandomAccessFile savedFile = null;
InputStream is = null;
File file = null;
//记录已下载的文件长度
long downloadedFileLength = 0;
//需要下载的文件总长度
long contentLength = 0;
try {
//获取下载的URL地址
String downloadUrl = strings[0];
//获取下载文件名(例如:/example)
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
//获取SD卡Download路径
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
//下载文件路径
file = new File(directory+fileName);
//判断需要下载的文件是否已经存在
if(file.exists()){
downloadedFileLength = file.length();
}
//获取需要下载的文件总长度
contentLength = getContentLength(downloadUrl);
if(contentLength == 0){
return TYPE_FAILED;
} else if (contentLength == downloadedFileLength){
return TYPE_SUCCESS;
}
//请求网络下载文件
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
//断点下载,指定从哪个字节开始下载
.addHeader("RANGE","bytes="+downloadedFileLength+"-")
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if(response!=null){
is = response.body().byteStream();
savedFile = new RandomAccessFile(file,"rw");
//跳过已经下载的字节
savedFile.seek(downloadedFileLength);
//io流读写
byte[] b = new byte[1024];
int total = 0;
int len;
while((len = is.read(b)) != -1){
if(isCanceled){
return TYPE_CANCLED;
}else if(isPaused){
return TYPE_PAUSED;
}else{
total += len;
savedFile.write(b,0,len);
//计算已经下载的百分比
int progress = (int) ((total+downloadedFileLength)*100/contentLength);
publishProgress(progress);
}
}
//下载完成
response.body().close();
return TYPE_SUCCESS;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(is!=null){
is.close();
}
if(savedFile!=null){
savedFile.close();
}
if(isCanceled && file != null){
file.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return TYPE_FAILED;
}
PS:这个方法中用到的TYPE值是静态常量,用来反馈下载任务的执行结果
//四个整型常量表示下载状态:成功,失败,暂停,取消
public static final int TYPE_SUCCESS = 0;
public static final int TYPE_FAILED = 1;
public static final int TYPE_PAUSED = 2;
public static final int TYPE_CANCLED = 3;
这个方法中用到的isPaused和isCanceled是两个flag,用于标识当前下载状态
private boolean isCanceled = false;
private boolean isPaused = false;
这个方法还调用了一个自定义方法,用于获取待下载文件的大小
private long getContentLength(String downloadUrl) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if(request != null && response.isSuccessful()){
long contentLength = response.body().contentLength();
response.close();
return contentLength;
}
return 0;
}
4.重写onProgressUpdate()方法更新下载进度
@Override
protected void onProgressUpdate(Integer... values) {
int progress = values[0];
//如果进度更新
if(progress>lastProgress){
//回调监听器onProgress方法更新进度
downloadListener.onProgress(progress);
lastProgress = progress;
}
}
5.重写onPostExecute()方法通知下载结果
@Override
protected void onPostExecute(Integer integer) {
switch (integer){
case TYPE_SUCCESS:
downloadListener.onSuccess();
break;
case TYPE_FAILED:
downloadListener.onFailed();
break;
case TYPE_PAUSED:
downloadListener.onPaused();
break;
case TYPE_CANCLED:
downloadListener.onCanceled();
break;
default:
break;
}
}
6.在服务中启动DownloadTask
public void startDownload(String url){
//当前没有下载任务时才能开始下载
if(downloadTask == null){
downloadUrl = url;
downloadTask = new DownloadTask(downloadListener);
downloadTask.execute(downloadUrl);
}
}