一、AsyncTask的简介
AsyncTask是一个抽象泛型类,它是由Android封装的轻量级异步类(使用方便、代码简介),它可以在线程中执行后台任务,然后将执行的结果以及进度传递给主线程,并在主线程更新UI。
AsyncTask内部封装了两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler)。
- SerialExecutor 线程池用于任务的排队,让需要执行的多个耗时任务按顺序排列。
- THREAD_POOL_EXECUTOR 线程池是真正执行任务的线程池。
- InternalHandler 用于线程的切换,从工作线程切换到主线程。
二、AsyncTask的泛型参数
public abstract class AsyncTask<Params, Progress, Result>
-
Params:开始异步任务时传入的参数类型,在execute()传入,跟doInBackground(Void... params)里的params类型一样。
-
Progress:执行进度,跟onProgressUpdate(Integer... values)的values类型一致,一般情况为Integer。
-
Result:执行结束的返回值,跟doInBackground返回的参数类型一致,并且跟onPostExecute的参数一致。
如果AsyncTask确定不需要传递具体的参数,那么这三个泛型参数可以用Void代替。
三、AsyncTask的核心方法
onPreExecute()
- 后台任务开始执行之前调用,在主线程调用,用来进行一些界面的初始化操作,如显示一个进度条对话框。
doInBackground(Params...)
-
这个方法的所有代码都在子线程中执行,我们应该在这里去处理所有的耗时操作。
-
这个方法不可以进行UI操作,如果需要更新UI,如反馈进度,可以调用publishProgress(Progress...)方法来完成。
onProgressUpdate(Progress...)
-
当后台任务调用了publishProgress(Progress...)方法后,这个方法就会被调用,方法中携带的参数是后台任务传递过来的。
-
这个方法可以对UI进行操作,在主线程中进行,可以利用参数的值对界面元素进行相应更新。
onPostExecute(Result)
-
当doInBackground(Params...)执行完并通过return语句进行返回时,这个方法就会被调用。
-
返回的数据会作为参数传递到该方法中,可进行一写UI操作,提醒、关闭进度框之类的。
上述方法执行顺序:
onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()
如果不需要执行更新进度,则:
onPreExecute() --> doInBackground() --> onPostExecute()
AsyncTask还提供了cancel(boolean)方法,用于取消异步任务,在主线程中调用。当cancel(boolean)被调用时,onPostExecute()则不被调用,将会调用onCancelled()方法。但并不是真正的取消任务,只是设置了这个任务为取消状态,我们需要在doInBackground()判断终止任务。
四、AsyncTask的简单使用
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... params) {
try {
while (true) {
int downloadPercent = doDownload();
publishProgress(downloadPercent);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
progressDialog.setMessage("当前下载进度:" + values[0] + "%");
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result) {
Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
}
}
}
这里模仿下载任务,doInBackground()执行下载逻辑,onProgressUpdate()显示当前下载进度,onPostExecute()提示任务执行结果。
如果启动任务,只需调用以下代码:
new DownloadTask().execute();
五、AsyncTask的使用注意事项
-
异步任务的实例必须在UI线程创建,即AsyncTask对象必须在UI线程创建。
-
execute()、cancelled()方法必须在UI线程调用。
-
不能在doInBackground()方法中更新UI。
-
一个任务实例只能执行一次,如果执行第二次会抛异常。
六、AsyncTask的缺陷
生命周期:
- AsyncTask不与任何组件绑定生命周期,所以在Activity或者Fragment创建AsyncTask时,最好在onDestory()调用cancel(boolean)方法。总之我们要确保正确的取消AsyncTask。
内存泄漏:
- 如果AsyncTask被声明为Activity的非静态内部类,那么AsyncTask会保留一个对创建Activity的引用。如果Activity已经被销毁,AsyncTask还在后台执行,它将继续在内存保留这个引用,导致Activity无法被回收,引起内存泄漏。
cancel(boolean)不一定好使:
- 当AsyncTask已经完成,亦或其他原因不能被取消,调用cancel(boolean)并没有实际效果,还是会继续执行doInBackground()方法,只是不执行onPostExecute()。
boolean参数mayInterruptIfRunning决定是否立即stop该Task
结果丢失:
- 屏幕旋转或者Activity在后台被系统杀掉等情况,会导致Activity的重新创建,之前运行的AsyncTask(非静态内部类的)会持有之前Activity的引用,但这个引用已经无效,这时调用onPostExecute()再去更新UI将不再有效。
串行还是并行:
- AsyncTask默认串行执行,也可以通过设置executeOnExecutor(Executor)来实现多个AsyncTask并行。
七、AsyncTask与Handler的关系
-
它们的关系并不大,甚至可以说不是一个层面的东西,因为AsyncTask本质是对Handler的封装,并且内部有自己维护的线程池。
-
有一些相同的目的,都可以异步执行一些代码,避免阻塞UI线程。
至于AsyncTask的源码理解,等以后理解了,再补上,再次留下了没技术的眼泪~
最后奉上参考博文,毕竟东拼西凑(捂脸):
Android中的线程状态之AsyncTask详解
AsyncTask 使用和缺陷
网友评论