美文网首页
AsyncTask 第三篇源代码篇

AsyncTask 第三篇源代码篇

作者: 王三的猫阿德 | 来源:发表于2016-04-03 12:08 被阅读38次

    转载注明出处:http://www.jianshu.com/p/26e1f7be4d0d

    简介

    上一篇主要针对AsyncTask内部使用到的一些线程的技术进行了基本的讲解,如果还没有看过的同学,可以点开这个AsyncTask 第二篇线程篇去查看一下。这一篇就从源代码分析AsyncTask的具体实现,也终于从第一篇的使用到了实现的思路。下面就直接开始了。

    方法调度说明

    AsyncTask里面其实我们最关心里面实现的思路了,这就需要结合一下AsyncTask怎么使用,才更方便的理解内部的实现,如果还有同学并不了解它的使用,可以查看AsyncTask 第一篇使用篇这篇文章。

    所有的开始都是从调用AsyncTask.execute(params)方法,我们直接看一下这个方法吧。

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

    execute(params)内部直接调用了executeOnExecutor方法,并将一个Executor的实例化对象传入,同时也将运行参数传入,我们来看一下executorOnExecutor方法吧。

    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        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();
    
        mWorker.mParams = params;
        exec.execute(mFuture);
    
        return this;
    }
    

    内部居然就有了详细的实现,也有我们非常熟悉的一个方法onPreExecute(),可以看出来,在我们调用了AsyncTask.execute(params)方法后,紧接着就会调用onPreExecute()方法,这个方法我们可以复写,并做一些任务执行前的一些操作。

    在这个方法里面,首先会对自身状态进行判断,如果是RUNNINGFINISHED状态就会抛出异常,也就是前面提示的每一个实例化的AsyncTask只能执行一次,如果执行多次会抛出异常。判断完毕后,将自身状态设置为RUNNING状态,并调用onPreExecute()方法,接着将参数传入mWorker中,执行mFuture,那说明mFuture即可以作为线程运行的结果,也可以作为一个线程执行的任务,根据上一篇关于线程的文章,我们就可以大胆猜测,mWorker实现了Callable接口,而mFuture其实是一个FutureTask的实例化对象。

    赶紧找到mWorker的声明。

    private final WorkerRunnable<Params, Result> mWorker;
    
    ......
    
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }
    

    找到声明处,紧接着找到了具体的类,果然和我们猜测一样,mWorker实现了Callable接口,但是只是一个抽象类,里面声明了一个泛型的成员变量,没有call()具体实现,我们直接去找mWorker实例化的地方,看一下call()方法的具体实现。

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
    
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(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);
                }
            }
        };
    }
    

    我们可以看见在AsyncTask构造函数里面就会对mWorkermFuture实例化,在mWorker对象中的call()方法里面,先设置了线程优先级,然后调用了我们熟悉的方法doInBackground(),并获取了运行的结果,调用了postResult()方法将结果传入。mFuture实例化的类获取了mWorkder实例化对象,并复写了done()这个方法,很容易理解这个done()方法是线程具体任务执行完毕后调用的,复写done()方法主要是保证获取到执行结果,我们看一下postResultIfNotInvoked()这个方法就明白了。

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }
    

    在这个方法里面,也会将执行结果传入postResult()方法。

    在这里我们看见了两个比较熟悉的方法,一个是doInBackground()一个是postResult(),我们先来看doInBackground()吧。

    protected abstract Result doInBackground(Params... params);
    

    是一个抽象方法,也就是说我们在继承或者直接实例化实时必须实现她。

    再来看一下postResult()

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

    将执行结果通过Handler传入了UI线程中的某个方法,想必大家都猜到了最终会调用到onPostExecute(Result result),但是目前为止我们只是获得了运行的结果,但是并没有获取到在执行过程中的执行状态。去看一看publishProgress一探究竟。

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

    原来这个方法也是将用户传入的执行状态直接通过Handler传到UI线程的某个方法,对,就是onProgressUpdate方法。可以看见最关键的两个异步消息的处理,结果和执行状态信息都是通过Handler传到UI线程,直接看一下Handler的声明。

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }
    

    Handler里面就处理了两类消息,一个是抛出运行结果消息,一个是抛出运行状态的消息。如果是运行状态,直接回调用AsyncTaskonProgressUpdate方法,和大家猜想是一样的。如果是运行结果,会调用finish方法,我们看看这个方法,为什么不是直接调用onPostExecute方法。

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

    很明显,如果在执行过程中,用户没有去取消,就会调用onPostExecute方法,如果取消了就会调用onCancelled方法。然后最终,会将AsyncTask的执行状态变为FINISHED状态,一旦置为这个状态,再去调用AsyncTask.execute(params)就会抛出异常,这个在调用该方法时候就会去判断,大家可以去上面看一下这块源代码。

    到目前为止,AsyncTask内部方法间的调度顺序大家通过源代码想必有了大体的认识。但是如果现在让大家自己写一个类似的类DemoTask,大家会怎么实现?根据上一篇的文章,我们会直接实例化一个Executor去执行FutureTask,并使用Handler来处理异步线程间通信。这样是正确的做法,但是一旦当我们自定义的DemoTask被大量时候的时候,我们并不能对它有一个统一的管理,所以我们有必要看一下官方对于这一块是怎么处理的。

    线程调度说明

    大家记得在调用AsyncTask.execute()方法时候,会有一个sDefaultExecutor实例化对象出现过吧,很明显,它是用来执行FutureTask的,而且是个常量,直接去看一下它的定义。

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
    
        public synchronized void execute(final Runnable r) {
            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);
            }
        }
    }
    

    代码很容易理解,它在执行时候会将FutureTask放入一个匿名Runnable的执行方法中,然后将这个匿名Runnable添加到自身一个成员变量,如果首次调用这个方法,会执行调用scheduleNext方法,使用线程池去运行这个匿名的Runnable。如果当前匿名Runnable中的FutureTask执行完毕,也会调用ScheduleNext方法。

    我们来看一下线程池THREAD_POOL_EXECUTOR声明的地方。

    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, 
                                    MAXIMUM_POOL_SIZE,                              KEEP_ALIVE,
                                    TimeUnit.SECONDS, 
                                    sPoolWorkQueue, 
                                    sThreadFactory);
    

    前面这些常量通过字面含义就能大体了解含义了,如果有同学想知道具体含义,可以去查看一下源代码,我们现在仅看一下sPoolWorkQueue这个参数。

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
    

    很明显,它是一个用来存储线程池中线程的队列,而且最多能容纳128个线程。而且由于这个线程池是全局的一个静态常量,所以在一个进程中所有我们实例化AsyncTask的具体执行任务都会被添加到这个线程池中。

    至此关于AsyncTask中线程的调度,也有了一个清晰的脉络。

    总结

    虽然AsyncTask是一个快过时的类,而且它不适合执行长时间后台操作,但是在许多场景我们依然会需要到它,而且通过源代码,我们可以对这种作者的思路做出一个很好的总结,对Handler的使用场景和使用方法,有了更深的认识。至此,我们也可以参考AsyncTask的思路,自定制适合自己的异步消息处理的机制。

    相关文章

      网友评论

          本文标题:AsyncTask 第三篇源代码篇

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