Android之AsyncTask源码分析(第四篇:执行任务过程)

(注意:本文基于API 28的源码分析,API 29上或其他平台的源码略有不同)

疑问1、调用AsyncTask的execute()方法后,onPreExecute()方法、doInBackground()方法、onPostExecute()方法,这些方法按照先后顺序会被回调,这些方法是如何被回调的?整个流程是怎么样的呢??

疑问2、为什么onPreExecute()、onPostExecute()方法是在UI线程调用的?而doInBackground()方法是在工作线程调用的?

疑问3、为什么调用了publishProgress()方法,onProgressUpdate()方法会在UI线程中被回调?

本篇文章将会从源码分析,解决这三个疑问……

 

AsyncTask中可以重写的部分方法

上方截图是继承AsyncTask后子类可以重写的部分方法,onPreExecute、onPostExecute、onProgressUpdate、doInBackground是我们经常重写的方法!每次继承AsyncTask后,唯一要求必须重写的是doInBackground方法

 

继承AsyncTask,重写必须重写的doInBackground()方法

    public class TempTask extends AsyncTask<Void, Void, Boolean> {

              @Override
              protected Boolean doInBackground(Void... params) {
                       …………省略好多代码…………
              }
              
    }

AsyncTask的三个参数类型的官方解读

Params:在执行时发送给任务的参数的类型

Progress:在工作线程计算期间的进度单元的类型

Result:在工作线程计算结束后结果的类型

 

主线程执行的execute()方法分析

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

开发者用于执行任务的方法,该方法必须在主线程中调用

1、内部先调用了一个executeOnExecutor()方法,并传入AsyncTask对象持有的sDefaultExecutor、以及传入的可变参数params

2、向调用者返回executeOnExecutor()方法的结果

 

主线程中执行的executeOnExecutor()过程分析

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        

        ………………省略很多源码………………

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

1、onPreExecute()方法在该方法内部得到调用,说明onPreExecute()最先被调用,同样在主线程中被调用,开发者可以重写onPreExecute()方法,作为任务执行前的准备工作(这是疑问1、疑问2的部分答案)

 

2、在onPreExecute()方法执行结束后,还没有看到doInBackground()方法的执行,那么doInBackground()方法、onPostExecute()方法在哪里执行呢?

        ………………省略好多源码………………

        mWorker.mParams = params; 
        exec.execute(mFuture);

        ………………省略好多源码………………

上述代码解读:先把传入的参数对象params赋值mWorker持有的mParams持有,然后执行传入的exec的execute()方法,并且把mFuture传进去,后面已经没有别的源码了,说明doInBackground()方法、onPostExecute()方法一定跟这两行代码有关系!那么mWorker是什么?exec又是什么?mFuture又是什么?

 

3、还记得湖边的夏雨荷吗?在我们调用execute()方法时,它的内部会执行executeOnExecutor()方法时,还记得传递给executeOnExecutor()方法的第一个参数sDefaultExecutor吗?

sDefaultExecutor会赋值给executeOnExecutor方法内部的exec变量,sDefaultExecutor默认指向一个Executor对象,下方是它的初始化代码,最终sDefaultExecutor指向一个SerialExecutor对象,那么SerialExecutor对象又是什么?

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

 

4、SerialExecutor是AsyncTask类中定义的一个静态内部类,它实现了Executor接口,SerialExecutor具备作为Executor的能力

    private static class SerialExecutor implements Executor {
    
                    ………………省略好多源码………………
                   
    } 

 

5、既然exec指向的是SerialExecutor对象,那么执行的execute()方法就是SerialExecutor对象的execute()方法

        exec.execute(mFuture);

 

6、此时方法执行到这里(此时还在主线程执行),注意是SerialExecutor类中的execute()方法

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

mTasks是一个ArrayDeque对象(循环数组实现的双端队列),先调用mTasks的offer()方法向ArrayDeque对象中添加一个Runnable对象,注意:该Runnable对象中重写的run方法,在重写的run方法内部,即会调用传入的Runnable对象r的run方法,又一定会调用一个scheduleNext()方法

mActive是SerialExecutor对象持有的一个Runnable对象,当Active为null时,会调用一个scheduleNext()方法

 

7、scheduleNext()方法也定义在SerialExecutor类中,注意:该方法为同步方法,为了保证mActive、mTasks的线程安全,只有持有SerialExecutor对象锁的线程才能执行该方法

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }

a、先从双端队列mTasks对象中取出一个Runnable对象并赋值给mActive,当mActive保存上Runnable对象后就会执行下面的逻辑

b、THREAD_POOL_EXECUTOR是AsyncTask类持有的一个线程池对象,在线程池对象中管理着多个工作线程,调用它的execute()方法,会将mActive对象放入到线程池中执行(由线程池中的工作线程执行)

 

8、线程池中的工作线程将调用Runnable对象中的run()方法,此时代码已经从主线程切换到工作线程中执行了

           new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            }

这个Runnable对象就是在上面SerialExecutor类中mTasks中持有的Runnable对象,在该Runnable对象的run()方法内部:会调用r.run()方法,还记得r代表的是什么吗?

 

9、r是AysncTask对象持有的mFuture,所有此时mFuture的run()方法会被调用,我们知道mFuture是个FutureTask对象,mFuture是在AsyncTask的构造方法中初始化的!注意:以下代码位于AsyncTask类中的构造方法中!这时候r.run(),这个run方法在哪里?怎么没看到?创建FutureTask对象时传入的mWorker又是什么?

        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);
                }
            }
        };

 

10、r.run,调用的是FutureTask的run方法,你得进入FutureTask类中,才能看到这个run方法,注意:以下的run方法位于FutureTask类中

    public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call(); //这里调用了Callable对象的call方法
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

 没有看到doInBackground方法在哪被调用啊?是啊,因为使用了FutureTask对象,FutureTask对象创建时会接受一个Callbable对象或者Runnable对象,AsyncTask是怎么做的呢?AsyncTask是为mFuture传递了一个Callable对象,就是mWorker,那么mWorker是什么?

 

11、mWorker实现了Callable接口,他是在AsyncTask类的构造方法中初始化的,在FutureTask中的run方法中,Callable的call方法会被调用(见10号知识点的代码注释中),所以代码又走到了mWorker的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); //doInBackground在这里被回调
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

哇塞,我们终于看到了doInBackground()方法的调用(疑问1、疑问2的部分答案)…………证明它是在线程池中的某一个工作线程中执行的,此时我们还没有看到onPostExecute()方法的调用?那么onPostExecute()方法在哪被调用?

 

12、在mWorkder对象重写的call()方法中,还没看见onPostExecute()方法的调用,别着急继续往下看,我们发现在finally代码块中的postResult()方法非常可疑,难道onPostExecute()方法是在里面调用的吗?

 

13、仍然没有看到onPostExecute()方法调用,你想想Android的线程间通信用的什么?必须用Handler机制!

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

getHandler()方法会返回一个AsyncTask对象持有的Handler对象mHandler,它指向的是一个InternalHandler对象(见14号知识点),在调用Handler对象的obtainMessage方法时,传入的是第一参数是MESSAGE_POST_RESULT(它是AsyncTask类中定义的常量),另外一个参数是一个AsyncTaskResult对象(见17号知识点),Message对象的sendToTarget方法会经层层传递,将当前Mesage对象传到Handler对象的handleMessage方法中(见14号知识点),此时我们仍然没有看到onPostExecute方法的调用

    private static final int MESSAGE_POST_RESULT = 0x1;

 

14、Message对象的sendToTarget方法,调用的是Message对象持有的Handler对象的send系列方法,最终Message对象又会回到Handler对象的handleMessage方法中,此时handleMessage方法已经切换到主线程中执行

在红圈处,AsyncTaskResult对象持有的mTask是什么呢?正是当前的AsyncTask对象(见17号知识点),我们去看看AsyncTask对象实现的finish方法(见15号知识点)

 

15、AsyncTask实现的finish()方法,在主线程中执行finish()方法

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

在finish方法中,如果AsyncTask没有被取消,就会在主线程中回调onPostExecute()方法(疑问1、疑问2的最后答案)哇,我们终于找到了onPostExecute方法被调用的地方

 

16、无参的getHandler()方法

    private Handler getHandler() {
        return mHandler;
    }

返回AsyncTask对象持有的mHandler,它是自定义的一个Handler对象,与主线程相关的Handler对象,实际类型是InternalHandler

 

17、定义在AsyncTask类中的静态内部类

    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

负责持有当前AsyncTask对象mTask、负责持有一个数组对象mData,

 

18、疑问3的答案呢?14号知识点的InternalHanlder的源码中的handleMesasge方法中还有一条MESSAGE_POST_PROGRESS消息的处理逻辑,这正是onProgressUpdate方法被调用的地方,那么是谁发出这个消息的?往下看!

 

19、在工作线程中执行的publishProgress方法

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

该方法的调用会发出一条MESSAGE_POST_PROGRESS的消息,此时在Handler对象中的handleMessage方法会被调用,你会看到onProgressUpdate方法的调用(见14号知识点)

 

总结

1、AsyncTask对象必须在主线程创建,因为AsyncTask内部使用Handler机制实现工作线程与主线程间的切换,这也是onPostExecute()、onProgressUpdate()在主线程被执行的原因

2、Executer接口,实现了该接口的类只是具备Executer能力,不能说为一个线程池,这里要注意,很多地方描述的并不准确

3、理解本文需要对Callable、FutureTask有所熟悉

4、默认的同一个进程中的所有AsyncTask对象提交的任务确实是串行执行的,任务是由线程池中的工作线程,执行完一个Runnable,才会再执行下一个Runnable(SerialExecutor对象持有的ArrayDeque对象mTasks负责持有任意一个AsyncTask对象提交的Runnable对象),不过如果能改变sDefaultExecutor的值就好了,可惜setDefaultExecutor方法是hide修饰的(此处不谈反射调用),这样我们就只能通过AsyncTask的executeOnExecutor()方法来改变AsyncTask执行任务的规则,把串行执行任务改变成并行执行任务

5、注意:本文源码基于API 28,其他版本的源码会有不同(我看API 29就改了部分实现方式)

    /** @hide */
    public static void setDefaultExecutor(Executor exec) {
        sDefaultExecutor = exec;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值