1 AsyncTask介绍
Android中有Handler和AsyncTask来实现异步类,Android中UI的更新只能在主线程中完成,为了不阻塞主线程,耗时的操作需要在异步类中完成。
AsyncTask是Android提供的轻量级的异步类,可以直接继承AsyncTaask,在类中实现异步操作,并提供结构反馈当前异步执行的程度,简单快捷,进程可控。
AsyncTask定义了三种泛型类型参数,Params, Progress和Result。
- Params 启动任务执行的输入参数,如Http请求的URL
- Progress 后台任务执行的百分比
- Result 后台执行任务最终返回的结果
使用AsyncTask来实现异步任务时,至少需要重写以下两个方法:
- doInBackground(Params…) 后台执行的耗时操作,如网络请求,注意不能在这个方法中更新UI。
- onPostExecute(Result) 这个方法是在UI线程中,主要用来在耗时操作完成后更新UI
还有其他的三个方法,但不是必须要实现的
- onProgressUpdate(Progress) 显示任务执行的进度
- onPreExecute() 任务执行之前调用此方法,可以在这里显示进度对话框
- onCancelled() 用户取消耗时操作时的操作
使用AsyncTask类实现异步操作时,需要注意一下几点:
- Task必须在主线程中创建
- execute()方法必须在主线程中调用
- 每个task只能执行一次,多次调用会产生异常
下面是一个AsyncTask使用的例子,注意需要在主线程中调用execute()方法
public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {
private TextView textView;
private ProgressBar progressBar;
public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
super();
this.textView = textView;
this.progressBar = progressBar;
}
/**
* 这里的Integer参数对应AsyncTask中的第一个参数
* 这里的String返回值对应AsyncTask的第三个参数
* 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
* 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
*/
@Override
protected String doInBackground(Integer... params) {
NetOperator netOperator = new NetOperator();
int i = 0;
for (i = 10; i <= 100; i+=10) {
netOperator.operator();
publishProgress(i);
}
return i + params[0].intValue() + "";
}
/**
* 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)
* 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置
*/
@Override
protected void onPostExecute(String result) {
textView.setText("异步操作执行结束" + result);
}
//该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置
@Override
protected void onPreExecute() {
textView.setText("开始执行异步线程");
}
/**
* 这里的Intege参数对应AsyncTask中的第二个参数
* 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
* onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作
*/
@Override
protected void onProgressUpdate(Integer... values) {
int vlaue = values[0];
progressBar.setProgress(vlaue);
}
}
2 AsyncTask代码解析
我们进入AsyncTask类中查看这个异步类的工作工作流程,当我们在主线程中调用AsyncTask的execute()方法后。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
执行的是executeOnExecutor()方法,其中一个参数是我我们调用时的params,而sDefaultExecutor我们就不知道是什么类型了。查课你sDefaultExecutor的定义,是一个SerialExecutor类的对象。SerialExecutor内部类定义如下:
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
...
}
protected synchronized void scheduleNext() {
...
}
}
SerialExecutor类实现了Executor的接口,主要是execute()方法。我们稍后再介绍。回到前面executeOnExecutor()函数,我们看一下这个函数的定义.
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
首先判断状态status, Status是一个枚举类型,包含三个值:
- PENDING : 待定状态
- RUNNING : 正在运行状态,如果执行一个操作,而这个操作的状态为正在执行,会抛出异常
- FINISHED : 已完成状态
如果状态为PENDING, 则会继续执行,首先会把状态改为RUNNING,然后回调onPreExecute()方法。接下来我们看一下mWorker的定义。
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
//….
}
可以看到mWorker在构造方法中完成了初始化,在call方法中最终调用了我们熟悉的doInBackground()方法,并返回Result值作为参数给postResult方法。
接下来我们分析postResult方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在postResult方法中,首先调用了getHandler()方法,可以猜测这个方法得到了一个实现异步消息机制的Handlder对象,我们查看该函数
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
果然,函数返回一个Handler对象,不过对象是自定义的InternalHandler的。我们继续查看InternalHandler类的定义
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
当msg.what = MESSAGE_POST_RESULT时,调用了mTask的finish方法,当msg.what = MESSAGE_POST_PROGRESS时,调用了mTask.onProgressUpdate()方法。我们分析一下finish()方法
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
当请求没有被cancel时,调用了onPostExecute(result)函数,即通过handler,在主线程中改变UI的状态。最后将状态改为已完成。
3 AsyncTask存在的问题
(1) 生命周期
很多人认为一个在Activity中的AsyncTask会随着Activity的销毁而销毁,然而事实并非如此,AsyncTask会一直执行doInBackground方法直到方法结束。一旦上述方法结束,会根据情况进行不同的操作:
- 如果cancel(boolean)调用了,则执行onCancelled(Resule)方法
- 如果cancel(boolean)没有调用,则执行onPostExecute(Result)方法
(2)内存泄露
在Activiy中使用非静态匿名内部AsyncTask类,由于java内部类的特点,AsysncTask内部类会持有外部类的隐式引用。由于AsyncTask的生命周期可能比Activity的长,当Activity销毁时AsyncTask还在执行,而AsyncTask持有Activity的引用,导致Activity无非回收,进而产生内存泄露。