在介绍AsyncTask之前,得先再回顾一下Handler。
众所周知,我们的ui操作需要在主线程,所以一般会先创建一个子线程,然后把耗时操作放到子线程,然后通过handler发送消息到主线程,通知主线程进行ui操作。
所以handler的步骤是:
1.主线程创建handler(接收消息)
2.创建线程(进行耗时操作)
3.子线程发送消息
4.主线程进行ui操作
而AsyncTask就像是对上述过程的一个封装。
Android提供了AsyncTask异步任务,AsyncTask对线程和handler进行了封装,使得我们可以直接在AsyncTask进行ui操作,就好像是在子线程进行ui操作一样
AsyncTask的步骤是:
1.创建子线程(进行耗时操作)
2.创建AsyncTask子类
3.在activity传入参数
在用AsyncTask之前,我们得先创建一个AsyncTask的子类继承AsyncTask(抽象类),然后在子类中 完成具体的业务逻辑操作。
AsyncTask抽象类指定了三个泛型。
public abstract class AsyncTask{ ... }
Params:开始异步任务执行时传入的参数类型,–>doInBackground()方法中的参数类型;
Progress:异步任务执行过程中,返回下载进度值的类型,–>doInBackground中调用publishProgress()(ui操作)时传入的参数类型;
Result:异步任务执行完成后,返回的结果类型,–>doInBackground()方法的返回值类型;
AsyncTask的回调方法
(1)onPreExecute(): 在执行后台下载操作之前调用, 运行在主线程中 ;
(2)doInBackground():核心方法,执行后台下载操作的方法,必须实现的一个方法, 运行在子线程中;
(3)onPostExecute():后台下载操作完成后调用,运行在主线程中;
AsyncTask的基本生命周期过程为: onPreExecute() --> doInBackground() --> onPostExecute()
(4)onProgressUpdate():在下载操作 doInBackground()中调用publishProgress()时的回调方法,用于更新下载进度,运行在主线程中;
因此,在需要更新进度值时, AsyncTask的基本生命周期过程为:onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate()–> onPostExecute()。
Mainactivity核心代码
asyncTask = new MyAsyncTask(mProgressBar, mImageView);
asyncTask.execute(url);
AsyncTask内部使用的是线程池,相当于里面有一个线程队列,执行一次execute时会将一个下载任务加入到线程队列,只有前一个任务完成了,下一个下载任务才会开始执行
为了达到我们想要的效果,我们自然想到把上一个任务给取消掉。的确,AsyncTask为我们提供了**cancel()**方法来取消一个任务的执行,但是要注意的是, cancel方法并没有能力真正去取消一个任务,其实只是设置这个任务的状态为取消状态,我们需要在doInBackground()下载中进行检测,一旦检测到该任务被用户取消了,立即停止doInBackground()方法的执行。
在AsyncTask中需要handler的地方其实就是两个地方,一个是doInBackground()在运行过程中,需要更新进度值的时候;一个是doInBackground()运行完成后,需要回到到UI线程中的onPostExecute()方法的时候。
**对于一:**我们在doInBackground()中调用publicProgress()进行进度值的更新,因此在publicProgress()中肯定会有handler的身影,如下:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
对于二:其实就是一个异步任务执行完后后的返回处理,而FutureTask正是处理处理Runnable运行返回结果的。
在2.1部分的executeOnExecutor方法中第四步,我们在执行execute(mFuture),传入了一个mFuture,mFuture是在初始化AsyncTask的时候进行构建的,如下:
mFuture = new FutureTask(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());//这里面通过handler往UI线程发送消息
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
小结:
AsyncTask主要是对异步任务和handler的封装,在处理异步任务时,AsyncTask内部使用了两个线程池,
一个线程池 sDefaultExecutor是用来处理用户提交(执行AsyncTask的execute时)过来的异步任务,这个线程池中有一个Runnable异步任务队列 ArrayDeque mTasks ,把提交过来的异步任务放到这个队列中;
另一个线程池 THREAD_POOL_EXECUTOR, 用来真正执行异步任务的。在处理handler时,自定义了一个InternalHandler,在publicProgress()和doInBackground()运行完成后,会通过这个handler往UI线程发送Message。
我们在使用AsyncTask的时候,一般会在onPreExecute()和onPostExecute()中进行UI的更新,比如等待图片的显示、进度条的显示…当我们一个Activity中正在使用AsyncTask进行文件的下载时,
如果此时屏幕发生了旋转,Activity会进行re-onCreate,又会创建一个AsyncTask进行文件的下载,这个正是我们前面将取消任务的时候谈到的第二个现象,我们只需要在onPause()中进行取消cancel()即可。但是这样仅仅是解决了发生等待的情况,因为Activity再次进入了onCreate()方法,还是会进行文件的下载,
为了解决这个问题,一种方案是通过判断onCreate(Bundle savedInstanceState)方法参数中的savedInstanceState== null?来判断是哪种情况,只有savedInstanceState== null时才去创建新的AsyncTask。
AsyncTask:
优点:AsyncTask是一个轻量级的异步任务处理类,轻量级体现在,使用方便、代码简洁上,而且整个异步任务的过程可以通过cancel()进行控制;
缺点:不适用于处理长时间的异步任务,一般这个异步任务的过程最好控制在几秒以内,如果是长时间的异步任务就需要考虑多线程的控制问题;当处理多个异步任务时,UI更新变得困难。
Handler:
优点:代码结构清晰,容易处理多个异步任务;
缺点:当有多个异步任务时,由于要配合Thread或Runnable,代码可能会稍显冗余。
总之,AsyncTask不失为一个非常好用的异步任务处理类,只要不是频繁对大量UI进行更新,可以考虑使用;而Handler在处理大量UI更新时可以考虑使用。