AsyncTask使用方式:
/**
* @Author: david.lvfujiang
* @Date: 2019/10/15
* @Describe:
*/
public class DownloadTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.e("异步准备", "启动");
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if (aBoolean == true){
Log.e("异步结束", "结束");
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.e("更新ui", String.valueOf(values));
}
@Override
protected Boolean doInBackground(String... strings) {
Log.e("异步操作", "操作");
for (int i = 0; i < 10; i++) {
if (isCancelled()) {
Log.e("cancell", isCancelled()+"");
break;
}
else {
//通知ui更新
publishProgress(new Integer(i));
Log.e("cancell", isCancelled()+"");
}
}
return true;
}
}
DownloadTask task= new DownloadTask();
task.execute("name");
1.
onPreExecute()
:在这个方法是异步开始之前执行的,可以做一些初始化工作。它所在线程与异步任务的execute方法所在的线程一致,这里若需要更新UI等操作,则execute方法不能再子线程中执行。
2.doInBackground(String name)
:这个方法是运行在子线程,我们可以进行我们的耗时任务,我们可以每隔一秒在这个方法里调用一次publishProgress(Integer i)
,之后系统将执行onProgressUpdate(Integer i)
3.onProgressUpdate(Integer... values)
:这个方法运行在主线程,每次调用publishProgress()
后它也会被调用,所以我们可以在这里面进行UI更新
4.onPostExecute(Boolean aBoolean)
:doInBackground()
方法执行完后这个方法将会执行,我们可以在这做一些收尾工作
5.当如果我们异步任务想要停止的话可以调用cancell( Boolean b)
方法,但是cancel()
仅仅是给AsyncTask
对象设置了一个标识位,发生的事情只有:AsyncTask
对象的标识位变了,doInBackground()
和onProgressUpdate()
还是会继续执行直到doInBackground()
结束,而onPostExecute()
不会被回调。所以我们需要在doInBackground()
的每次循环中调用isCancelled()
去判断是否继续执行任务。
需要注意的是:
1.一个AsyncTask对象不能多次调用execute方法,否则会报异常
看完上面AsyncTask的用法我们会有几个疑问:
1.为什么多次调用一个AsyncTask对象的execute方法会报异常?
2.AsyncTask的生命周期它的每一个方法是怎么调用的?
3.为什么调用cancell()后onPostExecute()不会执行了?
我们从源码来一步步分析:
我们使用AsyncTask的时候是创建对象然后调用execute方法,那我们先来看构造函数:
297 public AsyncTask() {
//创建WorkerRunnable
298 mWorker = new WorkerRunnable<Params, Result>() {
299 public Result call() throws Exception {
300 mTaskInvoked.set(true);
301 Result result = null;
302 try {
303 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
304 //doInBackground()在这被调用
305 result = doInBackground(mParams);
306 Binder.flushPendingCommands();
307 } catch (Throwable tr) {
308 mCancelled.set(true);
309 throw tr;
310 } finally {
311 postResult(result);
312 }
313 return result;
314 }
315 };
316
//创建FutureTask对象
317 mFuture = new FutureTask<Result>(mWorker) {
318 @Override
319 protected void done() {
320 try {
321 postResultIfNotInvoked(get());
322 } catch (InterruptedException e) {
323 android.util.Log.w(LOG_TAG, e);
324 } catch (ExecutionException e) {
325 throw new RuntimeException("An error occurred while executing doInBackground()",
326 e.getCause());
327 } catch (CancellationException e) {
328 postResultIfNotInvoked(null);
329 }
330 }
331 };
332 }
333
我们可以看到构造函数中初始化了俩个对象mWorker 、mFuture 。mWorker 是WorkerRunnable的实例,WorkerRunnable是AsyncTask的内部类实现了Callable接口,因此mWorker 其实就是一个线程,它启动的时候会调用call()方法,call()方法又会调用doInBackground(),因此doInBackground()可以执行我们的耗时操作,至于mWorker 什么时候被启动我们后面讲解:
692
693 private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
694 Params[] mParams;
695 }
696
mWorker 是FutureTask的实例,FutureTask可以像Runnable一样运行,封装异步任务,然后提交给Thread或线程池执行,然后获取任务执行结果。不了解的可以看这篇博客:FutureTask用法及解析。在这里FutureTask封装了mWorker ,所以当我们的线程执行完成后会回调FutureTask的done()方法。构造函数到这解析完毕。
execute()方法源码:
565 @MainThread
566 public final AsyncTask<Params, Progress, Result> execute(Params... params) {
567 return executeOnExecutor(sDefaultExecutor, params);
568 }
603 @MainThread
604 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
605 Params... params) {
606 if (mStatus != Status.PENDING) {
//抛出异常
607 switch (mStatus) {
608 case RUNNING:
609 throw new IllegalStateException("Cannot execute task:"
610 + " the task is already running.");
611 case FINISHED:
612 throw new IllegalStateException("Cannot execute task:"
613 + " the task has already been executed "
614 + "(a task can be executed only once)");
615 }
616 }
617
618 mStatus = Status.RUNNING;
619
//调用onPreExecute
620 onPreExecute();
621
622 mWorker.mParams = params;
//将线程任务加入队列
623 exec.execute(mFuture);
624
625 return this;
626 }
可以看到execute()方法有个注解
@MainThread
,说明这个方法必须在主线程中调用。再来看executeOnExecutor()
方法,从606行开始有个判断,判断mStatus
的值。mStatus
是用来标识AsyncTask
是否已经在执行异步的一个状态,默认是Status.PENDING
,因此第一次调用execute()
方法不会发生异常,最后在618行设置mStatus = Status.RUNNING;
因此当我们第二次调用execute方法时将会抛出异常(上文第一个问题解决了)。然后在620行调用了onPreExecute();
,最后呢623行调用了exec.execute(mFuture)
,exec
是AsyncTask
的内部类SerialExecutor
的实例。
235 private static class SerialExecutor implements Executor {
//创建任务队列
236 final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
237 Runnable mActive;
238 //该方法使用同步锁
239 public synchronized void execute(final Runnable r) {
//创建一个线程加入任务队列
240 mTasks.offer(new Runnable() {
//这个线程的工作就是启动传进来的Runnable 对象
241 public void run() {
242 try {
243 r.run();
244 } finally {
245 scheduleNext();
246 }
247 }
248 });
//第一次执行这个方法会走这个判断
249 if (mActive == null) {
250 scheduleNext();
251 }
252 }
253
254 protected synchronized void scheduleNext() {
//获取任务队列的头执行任务
255 if ((mActive = mTasks.poll()) != null) {
256 THREAD_POOL_EXECUTOR.execute(mActive);
257 }
258 }
259 }
// Executor THREAD_POOL_EXECUTOR就是一个线程池
205 public static final Executor THREAD_POOL_EXECUTOR;
206
207 static {
208 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
209 CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
210 sPoolWorkQueue, sThreadFactory);
211 threadPoolExecutor.allowCoreThreadTimeOut(true);
212 THREAD_POOL_EXECUTOR = threadPoolExecutor;
213 }
SerialExecutor
内部维护了一个任务队列ArrayDeque
,ArrayDeque
也是集合中的一种,execute()
方法则是创建一个线程加入到任务队列,而线程则又封装了我们传入的Runnable
对象,即mFuture
对象。第一次执行的时候mActive
为null,因此250行执行了scheduleNext()
方法,scheduleNext()
方法则是从队列中取出任务用线程池执行(THREAD_POOL_EXECUTOR
是一个线程池),任务执行后则会执行241行到245行
的代码,243行执行了r.run()
,即执行mFuture.run()
方法,mFuture.run()
内部又调用了mWorker .call()
方法,call()
方法又会调用doInBackground()
。最后244行的finally
代码块中又调用了scheduleNext()
方法,因此我们的异步任务就是这样一个一个的被调用的。就是靠SerialExecutor
内部维护了一个任务队列,我们把任务按顺序的放到队列中,然后不断的从队列取出任务执行直到队列为空。
现在我们知道真正执行异步任务的是靠THREAD_POOL_EXECUTOR
这个线程池,并且这个线程池是静态的,在多线程下创建不同的AsyncTask对象执行异步操作也都是需要串行执行。android开发:AsyncTask实现并发执行异步任务
任务执行异步任务的时候会回调Callable的call()方法,因此我们回到构造函数看:
```c
297 public AsyncTask() {
//创建WorkerRunnable
298 mWorker = new WorkerRunnable<Params, Result>() {
299 public Result call() throws Exception {
300 mTaskInvoked.set(true);
301 Result result = null;
302 try {
303 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
304 //doInBackground()在这被调用
305 result = doInBackground(mParams);
306 Binder.flushPendingCommands();
307 } catch (Throwable tr) {
308 mCancelled.set(true);
309 throw tr;
310 } finally {
311 postResult(result);
312 }
313 return result;
314 }
315 };
316
//创建FutureTask对象
317 mFuture = new FutureTask<Result>(mWorker) {
318 @Override
319 protected void done() {
320 try {
321 postResultIfNotInvoked(get());
322 } catch (InterruptedException e) {
323 android.util.Log.w(LOG_TAG, e);
324 } catch (ExecutionException e) {
325 throw new RuntimeException("An error occurred while executing doInBackground()",
326 e.getCause());
327 } catch (CancellationException e) {
328 postResultIfNotInvoked(null);
329 }
330 }
331 };
332 }
333
先是调用
doInBackground()
执行异步操作,311行调用了postResult(result)
341 private Result postResult(Result result) {
342 @SuppressWarnings("unchecked")
343 Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
344 new AsyncTaskResult<Result>(this, result));
345 message.sendToTarget();
346 return result;
347 }
279 //获取handler对象
280 private static Handler getHandler() {
281 synchronized (AsyncTask.class) {
282 if (sHandler == null) {
//InternalHandler是一个内部类
283 sHandler = new InternalHandler();
284 }
285 return sHandler;
286 }
287 }
288
postResult(result)
则是使用Handler发送MESSAGE_POST_RESULT
消息。这个Handler又是从哪来的?
671
672 private static class InternalHandler extends Handler {
673 public InternalHandler() {
674 super(Looper.getMainLooper());
675 }
676
677 @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
678 @Override
679 public void handleMessage(Message msg) {
680 AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
681 switch (msg.what) {
682 case MESSAGE_POST_RESULT:
683 // 结束异步
684 result.mTask.finish(result.mData[0]);
685 break;
//更新UI
686 case MESSAGE_POST_PROGRESS:
687 result.mTask.onProgressUpdate(result.mData);
688 break;
689 }
690 }
691 }
663 private void finish(Result result) {
664 if (isCancelled()) {
665 onCancelled(result);
666 } else {
667 onPostExecute(result);
668 }
669 mStatus = Status.FINISHED;
670 }
InternalHandler
是AsyncTask
内部的一个静态内部类。我们看到它的构造函数则是获取主线程的Looper
对象和handler
进行绑定。所以我们使用这个handler发送消息时就可以在主线程中更新UI。可以看到handleMessage()
调用了finish()
和onProgressUpdate()
因此
doInBackground()
执行完成后最后会调用到finish()
,onPostExecute()
则会被执行。同时你可以看到如果我们调用了cancell()
,,onPostExecute()
则不会被执行,而是执行了onCancelled(result)
(解决了上文我们的第三个问题)
我们通知更新UI的时候是在
doInBackground()
调用publishProgress
(),它就是使用handler发送一个MESSAGE_POST_PROGRESS
消息,handler收到后则执行onProgressUpdate()
655 @WorkerThread
656 protected final void publishProgress(Progress... values) {
657 if (!isCancelled()) {
658 getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
659 new AsyncTaskResult<Progress>(this, values)).sendToTarget();
660 }
661 }
总结:
其实整个异步任务的大概流程就是这样子的,其中涉及的知识点比较多,这里总结一下:
1.AsyncTask内部维护了一个任务队列,它会把我们的异步任务封装成一个Callable实例,然后添加到任务队列中,并且使用线程池不断的从任务队列中取出任务执行,而Callable实例最后会回调doInBackground()。
2.onPreExecute()方法主要用于在异步任务执行之前做一些操作,execute方法不能再子线程中执行。
3.通过刚刚的源码分析可以知道异步任务一般是顺序执行的,即一个任务执行完成之后才会执行下一个任务。
doInBackground这个方法所在的线程为任务所执行的线程,在这里可以进行一些后台操作。
4.AsyncTask有个handler内部类,构造方法绑定主线程的looper对象,异步任务执行完成之后会通过handler发送消息,最终回调我们的onPostExecute()方法,在doInBackground()方法中通知更新ui调用publishProgress()方法,publishProgress也是通过handler发送消息,最后回调onProgressUpdate()。
5.异步任务对象不能执行多次,即不能创建一个对象执行多次execute方法。(通过execute方法的源码可以得知)