阅读 66

AsyncTask源码解疑

为什么AsyncTask只能执行一次,只能用一次execute?

我们点开execute(),可以看到调用了executeOnExecutor()

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

看到executeOnExecutor

@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;
    }
复制代码

可以看到这里有一个枚举状态的校验Status共有三种状态,对象初始化时默认为Status.PENDING,其他时候均没有赋值Status.PENDING

image
executeOnExecutor一旦被调用一次mStatus状态就会被改变,之后再调用就会抛对应状态的异常了。

为什么AsyncTask默认是串行的?

前面看到默认状态下调用execute方法是带着sDefaultExecutor去调用executeOnExecutor,我们跟一下sDefaultExecutor这个线程池是什么

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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);
            }
        }
    }
复制代码

可以看到,每次调用sDefaultExecutor#execute

  1. 先把runnable包一层(装饰模式)入队

  2. 如果目前没有正在执行的任务,即mActive,就尝试从队列中取一个交给另一个线程池去执行

  3. 每次执行完实际的任务,顺便尝试从队列中取一个交给另一个线程池去执行

由此可见sDefaultExecutor这个静态的线程池就是负责保证任务顺序执行的。 接下来我们看下execute方法是否真的使用了sDefaultExecutor这个线程池去执行任务,上面代码可以看到executeOnExecutor方法在执行onPreExecute()之后就使用sDefaultExecutor去执行任务mFuture,我们看下mFuture是什么:

public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        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);
                }
            }
        };
    }
复制代码

可以看到mFuture包着mWorker是在构造函数中创建的,看到mFuturerun()

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
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();
...省略代码...
复制代码

mFuture#run()执行了callable的call方法,而callable正是通过构造带进去的mWorker,其call()方法中赫然就有result = doInBackground(mParams),至此,我们知道了默认情况下用sDefaultExecutor来实现串行,如果你想并行,那就要自己做一个线程池直接调用executeOnExecutor()传进去。

此外,也不难看到AsyncTask四个回调方法中的onPreExecute()是一开始就在调用线程中执行了onPreExecute中又可能做一下跟UI有关的事情,所以规范上execute()必须要主线程调用(如果你的onPreExecute中不需要做一些跟UI有关的事情那就不强求一定要在UI线程调用),而doInBackground()则是在子线程中调用的。

如何确保结果回调onPostExecute()以及进度回调onProgressUpdate()在主线程中执行?

看到mWorker执行完成时调用了postResult(result)

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

这里的Handler跟一下不难发现

private Handler getHandler() {
        return mHandler;
    }
public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

 private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }
复制代码

除非你在AsyncTask创建时传入指定的Looper,不然都是默认Looper.getMainLooper() 看到具体实现InternalHandler

private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @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;
            }
        }
    }

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

可以明显看到InternalHandler在这里的作用时用来切线程,不出意外都是切回主线程的,

AsyncTask真的必须在主线程中加载吗?为什么大家都这么说?

旧的AsyncTask源码中sHandler的默认创建是这样的

private static final InternalHandler sHandler=new InternalHandler();
复制代码

不指定Looper的Handler用的是当先线程的Looper,所以如果你第一次使用在子线程中的话,类加载时,静态变量一并创建,那么sHandler的Looper就不在主线程了,后面的onPostExecute()以及onProgressUpdate()就不在主线程中执行了。

但是,我在28的源码中看到的已经不是这种默认创建的方法了,而是摆明车马指定了Looper.getMainLooper(),那么不管你第一次在哪里加载使用,都不影响切换回主线程了。

private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }
复制代码

如何取消一个AsyncTask任务?

我们看到AsyncTask源码中有一个cancel(boolean)方法

public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }
复制代码

跟一下mCancelled

可以看到这是一个线程安全的原子类,顾名思义,它记录了是否被取消的状态

继续跟一下mFuture.cancel(mayInterruptIfRunning)

public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              U.compareAndSwapInt(this, STATE, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }
复制代码

当入参为true的时候调用了执行线程的interrupt()

所以我们可以知道,当你调用了cancel()方法时,如果入参为false,那么只是改变一个状态而已,还需要在doInBackground()中适当的地方配合使用,如果入参为true,如果你的操作需要处理会帮你抛InterruptException的话,会帮你抛InterruptException。

关注下面的标签,发现更多相似文章
评论