美文网首页
从0开始学安卓源码分析--AsyncTask源码

从0开始学安卓源码分析--AsyncTask源码

作者: 花生壳_9048 | 来源:发表于2019-01-23 10:50 被阅读24次

    问题:

    1. 为什么一个AsyncTask只能被执行一次?
    2. 为什么AsyncTask是串行执行的, 不能并行吗?
    3. 为什么AsyncTask必须在UI线程中创建?

    带着问题看源码, 稳.

    看一个源码当然从其构造函数出发.

    public AsyncTask(@Nullable Looper callbackLooper) {
            // 这里是为什么需要在主线程创建的原因, 不然怎么去更新ui?
            mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
                ? getMainHandler()
                : new Handler(callbackLooper);
            // WorkerRunnable实现callable接口并实现了它的call()方法
            mWorker = new WorkerRunnable<Params, Result>() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
                    Result result = null;
                    try {
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        //noinspection unchecked
                        result = doInBackground(mParams);
                        Binder.flushPendingCommands();
                    } catch (Throwable tr) {
                        mCancelled.set(true);
                        throw tr;
                    } finally {
                        postResult(result);
                    }
                    return result;
                }
            };
    
            mFuture = new FutureTask<Result>(mWorker) {
                @Override
                protected void done() {
                    try {
                        postResultIfNotInvoked(get());
                    } 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);
                    }
                }
            };
        }
    

    在这个构造方法里可以看到doInBackground(mParams) 方法, 也就是我们继承AsyncTask 复写的方法. 该方法返回了result结果, 最终在finally里调用postResult(result); 将结果通过handler传递进行处理. 在handler的handleMessage中会调用AsyncTask的finish()方法.

    private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
    

    这里就找到了 onPostExecute(result); 将结果通过onPostExecute回调到上层了.

    FutureTask是一个可管理的任务类, 它实现了Runnable和Callable两个接口, 稍微瞄一眼源码可知其可以对Runnable和Callable进行包装.
    这里将WorkerRunnable作为入参传递给了FutureTask.
    AsyncTask还并未开始执行, 真正执行需要调用AsyncTask.execute(params);, 并将params传递进去.

    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
        }
    

    接着看executeOnExecutor(sDefaultExecutor, params);

    这里需要看下AsyncTask的三个状态:

    • PENDING // 等待, 表示任务未执行
    • RUNNING // 表示任务正在运行
    • FINISHED // 表示任务已经完成结束
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                Params... params) {
            // 这里也是为什么一个AsyncTask只能执行一次的原因
            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()方法, 我们会在onPreExecute进行一些不耗时初始化的操作
            onPreExecute();
    
            mWorker.mParams = params;
            // 这里开始真正执行task任务
            exec.execute(mFuture);
    
            return this;
        }
    

    调用exec.execute(mFuture);真正执行任务的exec是什么呢?
    是传递进来的sDefaultExecutor .

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    
    private static class SerialExecutor implements Executor {
            // ArrayDeque 先进先出的双端队列
            final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
            Runnable mActive;
    
            public synchronized void execute(final Runnable r) {
                // 先将任务都存入队列里
                // 当前没有执行的任务则从队列中取出一个用THREAD_POOL_EXECUTOR执行.
                // 当前有执行的任务则是在任务完成后的finally里取出一个执行
                mTasks.offer(new Runnable() {
                    public void run() {
                        try {
                            r.run();
                        } finally {
                            scheduleNext();
                        }
                    }
                });
                if (mActive == null) {
                    scheduleNext();
                }
            }
    
            protected synchronized void scheduleNext() {
                if ((mActive = mTasks.poll()) != null) {
                    THREAD_POOL_EXECUTOR.execute(mActive);
                }
            }
        }
    

    sDefaultExecutor是一个静态的串行执行器, 也就是如果都调用AsyncTask.execute(params);方法, 那么一个进程内所有AsyncTask都是由sDefaultExecutor 串行执行的. 这样保证一个时间只有一个任务执行.
    如果需要并行执行任务则需要调用executeOnExecutor(Executor exec,Params... params)

    另外注意再看看THREAD_POOL_EXECUTOR

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
                new LinkedBlockingQueue<Runnable>(128);
                
     public static final Executor THREAD_POOL_EXECUTOR;
    
        static {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    sPoolWorkQueue, sThreadFactory);
            threadPoolExecutor.allowCoreThreadTimeOut(true);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
        }
    

    线程池用到了一个容量128的阻塞队列.

    到这里我们继承AsyncTask需要复写的方法,就都已经在流程中出现了

    • onPreExecute()
    • doInBackground(params)
    • onPostExecute(result)

    还有另外一个onProgressUpdate(progress) 在哪儿呢?
    搜索一下源码.

    protected final void publishProgress(Progress... values) {
            if (!isCancelled()) {
                getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                        new AsyncTaskResult<Progress>(this, values)).sendToTarget();
            }
        }
    

    可以在doInBackground()中调用publishProgress()方法来进行进度的更新. 通过handler来将更新进度发布到UI线程并回调onProgressUpdate(progress)方法

    AsyncTask的源码到这里已经分析完了, 总的来说难度不大. 最后AsyncTask可能会导致内存泄漏, 记得适时的调用AsyncTask的cancel()方法

    相关文章

      网友评论

          本文标题:从0开始学安卓源码分析--AsyncTask源码

          本文链接:https://www.haomeiwen.com/subject/kkkjjqtx.html