Android日记之AsyncTask源码解析

作者: 居居居居居居x | 来源:发表于2019-11-08 19:12 被阅读0次

前言

AsyncTask的使用方法请看Android日记之AsyncTask的基本使用,此篇的源码解析我们还是从使用方法开始进行解析,源码版本基于Android9.0。

AsyncTask的工作原理

启动AsyncTask的方法是execute(),我们从这里开始分析。

//sDefaultExecutor是线程池,第二个参数是执行异步的参数
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}


@MainThread
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()方法,这个方法会被最先执行,然后接下来线程池也会接着开始执行,就是exec.execute(mFuture),这里传入了一个mFuture对象,因为exec是sDefaultExecutor传进来的变量,我们就来看看sDefaultExecutor是哪里来的

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

这里先new出了一个SERIAL_EXECUTOR常量,然后将sDefaultExecutor的值赋值为这个常量,也就是说明,刚才在executeOnExecutor()方法中调用的execute()方法,其实也就是调用的SerialExecutor类中的execute()方法,我们就看看SerialExecutor类具体的execute()实现。

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

从这里可以看出,这里的方法开了一个Runnable,也就是说这里的方法开始是在子线程中开始进行的了,这里就可以看出AsyncTask的执行过程了,首先系统会把AsyncTask的Params参数封装成FutureTask对象,它是一个并发类对象,这里充当了Runnable的作用,然后将FutureTask交给SerialExecutor的execute()进行处理,进入了这里之后,首先会将FutureTask对象插入到任务队列mTasks中,接着会判断mActive == null,就是这时候如果没有正在执行的AsyncTask任务的话,那么就会调用scheduleNext()去执行下一个AsyncTask的任务,同时当一个任务执行完的话,就会接着执行下一个的任务,直到全部都被执行完毕为止,从这里也可以看出,AsyncTask是串行执行的。

接下来,我们还没找到doInBackground()是从哪里实现的,我们接着看。其实在AsyncTask里面中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler,SerialExecutor刚刚已经说过了,适用于任务的排队的,而THREAD_POOL_EXECUTOR就是去执行真正的任务。刚刚上面的代码Runnbale这里是有一个参数,这个参数其实就是FutureTask,也就是说这里的run()方法其实就是调用FutureTask的run()方法,而FutureTask的run()又会调用mWorker的call()方法,我们从AsyncTask的构造函数中中可以看到。

public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);
    
    //这里new了一个WorkerRunnable
    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
                //通过FutureTask的run方法开始执行doInBackground();
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
        }
    };
    
    //这里new了一个FutureTask,参数为mWorker,也就是会执行mWorker的call方法
    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);
            }
        }
    };
}

从上面那段代码可以看出有个变量为mTaskInvoked,被设置为true,就是代表当前任务以及被调用了,然后开始执行doInBackground()方法。返回值会传给postResult()里面。

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去进行发送。最后看AsyncTask的的结束逻辑,这里就比较简单了,如果AsyncTask被取消执行了,那么就调用onCancelled()方法,否者就是调用onPostExecute()方法,而这个返回结果就是doInBackground()这个方法返回的,到这里就基本全部分析完毕了。

使用AsyncTask的注意事项

使用AsyncTask是有几个注意事项的,有以下几点:

  • AsyncTask的类必须要在主线程进行开始加载,也就是说第一次访问AsyncTask必须发生在主线程,当然这个过程在Android 4.1以上已经被自动完成了。
  • AsyncTask的对象必须要在主线程中创建。
  • execute()方法必须在UI线程中调用。
  • 不要再程序中直接调用onPreExecute()onPostExecute()doInBackground()onProgressUpdate()方法。
  • 一个AsyncTask对象只能被执行一次,就是说execute()方法只能调用一次,否者会出现异常。
  • 在Android1.6之前,AsyncTask是串行执行任务的,1.6的时候,编程了并行执行任务,3.0的时候为了避免并行出现的错误,就开始采用了一个线程来执行串行任务。虽然是这样子,但是我们依然可以通过executeOnExecutor()来并发执行任务。

参考

相关文章

网友评论

    本文标题:Android日记之AsyncTask源码解析

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