Android开发:异步任务AsyncTask源码解析

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)execAsyncTask的内部类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 内部维护了一个任务队列ArrayDequeArrayDeque也是集合中的一种,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    }

InternalHandlerAsyncTask内部的一个静态内部类。我们看到它的构造函数则是获取主线程的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方法的源码可以得知)

发布了194 篇原创文章 · 获赞 42 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_39027256/article/details/103579285
今日推荐