前言
今天我们来回顾复习下AsyncTask, 它是Android 一种轻量级的异步任务类,从实现来说,AsyncTask封装了线程池和Handler。它可以在线程池中执行后台任务,把执行的进度和结果传递给主线程并在主线程中更新UI,通过AsyncTask可以更加方便的执行后台任务以及在主线程访问UI,但他不适合进行特别耗时的后台任务。
一、什么是AsyncTask?
1、是一种轻量级的异步任务类;
2、是一个封装了线程池和Handler的异步框架;
3、使用它可以更加方便的执行后台任务以及在主线程访问UI,但他不适合进行特别耗时的后台任务;
二、AsyncTask的使用方法
1、3个参数
- Params:执行AsyncTask时,后台任务需要传入的参数,在doInBackground中取出使用。
- Progress:后台任务执行时,如果需要显示当前进度,则制定进度的数据类型。
- Result:后台任务执行完成后的返回值类型。
2、5个方法
onPreExecute:
在主线程中执行,在后台任务执行前调用,通常用于做一些准备操作。doInBackground(Params… params):
在线程池中执行,用于执行后台任务;params 参数是 execute(Params… params)方法中传递的参数。在此方法中可以调用 publishProgress 方法来更新任务的进度,publishProgress方法会调用onProgressUpdate方法。publishProgress(Progress… values):
用于更新任务的进度,需要手动调用,publishProgress方法会调用onProgressUpdate方法;values参数为设置的进度值。onProgressUpdate(Progress… values):
在主线程中执行,当后台任务的执行进度发生改变时,会被调用,values参数为进度值。onPostExecute(Result result):
在主线程中执行,当后台任务执行完成时,会被调用。result 的值是doInBackground的返回值。
三、AsyncTask的机制原理
- AsyncTask 的本质是一个静态的线程池,可以执行不同的异步任务,这些任务提交到静态的线程池中执行。
- 线程池中的工作线程执行doInBackgroup(Params… params)方法执行异步任务。
- 当任务状态改变后,工作线程向UI线程发送消息,AsyncTask内部的InternalHandler 响应这些消息,并调用相关的回调函数。
四、AsyncTask的注意事项
1、内存泄漏
原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放。
解决:
- AsyncTask内部持有外部Activity的弱引用。
- AsyncTask改为静态内部类。
- AsyncTask.cancel()。
2、生命周期
不受Activity生命周期的影响,在Activity销毁之前,取消AsyncTask的运行,以此来保证程序的稳定。
3、结果丢失
由于屏幕旋转、Activity在内存紧张时被回收等情况下,Activity会被重新创建,此时,还在运行的AsyncTask会持有一个Activity的非法引用即之前的Activity实例。导致onPostExecute()没有任何作用。
4、并行or串行
- Android 1.6之前,默认采用串行执行任务;
- Android 1.6~ 2.3 ,默认采用并行执行任务;
- Android 3.0,默认采用串行执行任务,如果需要改为并行,可以调用AsyncTask的executeOnExecutor()来执行任务即可。
5、AsyncTask的使用限制
- AsyncTask的对象必须在主线程中创建;
- execute 方法必须在UI线程调用;
- 不要在程序中手动调用 onPreExecute、onPostExecute、 doInBackground、onProgressUpdate方法;
- 一个AsyncTask对象只能调用一次excute()方法,执行一次,否则会报异常。
6、代码实践
模拟文件下载:
package com.example.ling.review.asynctask;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import java.lang.ref.WeakReference;
public class AsyncTaskActivity extends AppCompatActivity {
private DownLoadFileTask mTask = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建 DownLoadFileTask 实例
mTask = new DownLoadFileTask(this);
// 执行任务
mTask.execute("参数1", "参数2", "参数3");
}
@Override
protected void onDestroy() {
super.onDestroy();
// 取消 AsyncTask的任务
mTask.cancel(true);
}
/**
* 1、DownLoadFileTask 改为静态内部类,避免内存泄漏
* 2、将Activity 包装成弱引用,避免内存泄漏
*/
public static class DownLoadFileTask extends AsyncTask<String, Integer, String> {
private WeakReference<AsyncTaskActivity> mWeakReference = null;
private ProgressDialog mProgressDialog = null;
private Context mContext = null;
public DownLoadFileTask(AsyncTaskActivity activity) {
mWeakReference = new WeakReference<>(activity);
}
/**
* 运行在主线程,执行后台任务之前调用,通常用来做一些准备操作
*/
@Override
protected void onPreExecute() {
mContext = mWeakReference.get();
if (mContext != null) {
mProgressDialog = new ProgressDialog(mContext);
mProgressDialog.setCancelable(false);
mProgressDialog.setTitle("任务正在执行中");
mProgressDialog.setMessage("任务正在执行中,敬请期待...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setMax(200);
mProgressDialog.show();
}
}
/**
* 运行在线程池中,执行后台耗时操作
*
* @param strings 参数
* @return 结果
*/
@Override
protected String doInBackground(String... strings) {
// 打印 mTask.execute();传递的参数
StringBuilder builder = new StringBuilder();
for (String string : strings) {
builder.append(string).append(",");
}
Log.d("doInBackground", builder.toString());
// 模拟下载
int progress = 0;
while (progress < 200) {
try {
Thread.sleep(1000); // 模拟耗时操作
progress += 10;
publishProgress(progress);// 更新进度,该方法调用后会触发 onProgressUpdate方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
publishProgress(progress);
String msg = "文件下载成功!";
return msg;
}
/**
* 运行在主线程,用于更新进度,publishProgress方法执行后,会触发该方法
*
* @param values 进度值
*/
@Override
protected void onProgressUpdate(Integer... values) {
if (mProgressDialog != null) {
mProgressDialog.setProgress(values[0]);
}
}
/**
* 运行在主线程,后台任务执行完成后,将返回值传给该方法
*
* @param s 返回的结果
*/
@Override
protected void onPostExecute(String s) {
if (mContext != null) {
mProgressDialog.dismiss();
Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();
}
}
}
}
模拟文件下载:运行结果:
1、打印参数日志:
01-28 10:25:56.385 5019-5089/com.example.ling.review D/doInBackground: 参数1,参数2,参数3,
2、运行界面效果