Android 并发二三事之AsyncTask

Android 并发第四篇

前言:

本篇主要详解AsyncTask 的源码,关于 AsyncTask 的源码其实有太多人都写过了。这里为什么还要写,
是因为博主在并发系列中写AsyncTask的源码,是想通过从并发的角度去理解AsyncTask 为什么这样设计。
我们可以看到 AsyncTask 其中用到了 之前文章中设计到的 FutureTask, 以及Callable,线程池等等。
我们也可以借此复习一下。

一 AsyncTask 的使用:
代码:

private void requestAsy() {
        AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
            @Override
            protected String doInBackground(String... params) {

                List<ResponInfo> responInfos = getInfos();
                long price = 0;
                for(ResponInfo responInfo : responInfos) {
                    if(responInfo.getName().equals(params[0])) {
                        price = responInfo.getPrice();
                    }
                }

                return String.valueOf(price);
            }

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
            }

            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                Log.d(TAG,"Asy : "+s);

            }
        };
    //无参
        asyncTask.execute();
    //或 有参
        asyncTask.execute("BMW");
    //或 传入我们自定义的线程池
        asyncTask.executeOnExecutor(Executors.newFixedThreadPool(10));
    }

    private List<ResponInfo> getInfos() {
        List<ResponInfo> list = new ArrayList<>();
        list.add(new ResponInfo("BMW", 2000));
        list.add(new ResponInfo("BYD", 200));
        try {
            Thread.sleep(3000);

        }catch (Exception e){

        }
        return list;
    }

二 源码:

1、 execute() 方法:

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

execute 调用的也是 executeOnExecutor()方法,只不过传入的是代码内的默认线程池 sDefaultExecutor。
sDefaultExecutor 这个参数我们稍后再说,现在只要记得它是一个线程池就好了,可以传入 Runnable,或者 Callable。

2、 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;
    }

当我们调用executeOnExecutor() 方法,将不会用AsyncTask 自己的线程池,而改用我们所传入的线程池。

在源码中还有这样的方法:

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

设置默认的线程池。
不过在最新的版本中被隐藏了,因为有executeOnExecutor() 方法,就用不上这个方法了。

在 executeOnExecutor() 方法中我们看到其调用了onPreExecute() 方法,所以当我们重写 onPreExecute() 方法后, onPreExecute() 会执行在主
线程之中。

我们会将传入的参数传给 mWorker.mParams 。
然后调用线程池执行 mFuture。mFuture 是 FutureTask 根据第一篇文章中的介绍 mFuture 既是 Runnable, 又是 Future。
所以其可以作为一个任务(Runnable)提交给线程池,也可以作为一个Future,从线程池中获取结果。

3 、看构造方法源码:

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();
        //将结果post出来
                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);
                }
            }
        };
    }
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

mWorker 本身是一个Callable ,mFuture 是 FutureTask 。FutureTask 在第二篇文章中,有过介绍,其实现了RunnableFuture接口,
而RunnableFuture 接口同时继承了Runnable和Future 。所以FutureTask 可以作为一个任务被提交给线程池。这里FutureTask 包装了 mWorker。
mWorker 才是真正的任务主体。
mWorker以及 mFuture都是在构造函数中初始化的。

mWorker 作为任务主体,被提交到线程池中,其call() 方法将会执行在子线程之中。而在call() 方法中,调用了doInBackground()方法
所以doInBackground()方法将会执行在子线程之中。并且将mWorker.mParams 传给 doInBackground()方法,所以doInBackground()方法
的参数就是我们 在调用 execute() 和 executeOnExecutor()传入的参数,其类型便是我们在 new AsyncTask() 时的三个泛型中第一个。

在执行过耗时任务之后。会调用 postResult() 方法将结果post出来。

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

这里getHandler()方法返回的是InternalHandler。

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

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

发送了 MESSAGE_POST_RESULT 的消息后,在handleMessage() 中调用了result.mTask.finish(result.mData[0]);
result 是 new AsyncTaskResult(this, result) 。

而AsyncTaskResult 只是封装了真正的数据,以及当前 AsyncTask 对象。

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

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

result.mTask.finish(result.mData[0]);
这行代码实际上调用了 AsyncTask 中的finish() 方法。

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

finish() 方法中则调用了 onPostExecute() 方法。这个流程就是典型的,子线程发送消息通知主线程了,所以onPostExecute() 方法会执行在主线程中。

那么说到这里,整个流程差不多快说完了。但是我们还没有介绍 FutureTask 也就是mFuture 到底是用来做什么,为什么要用他呢?
按照之前的罗逻辑完全可以不用 mFuture 来包装 mWorker 。mWorker 就可以直接被提交给线程池来完成任务。

4 、下面是 FuturenTask 部分源码:

    //FuturenTask 包装了一个 Callable。
    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 {
            //在这里执行了 Callable 的 call() 方法。获取结果。
                    result = c.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);
        }
    }

    protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;
            U.putOrderedInt(this, STATE, NORMAL); // final state
            finishCompletion();
        }
    }

    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (U.compareAndSwapObject(this, WAITERS, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }
    //最后会调用done()方法。
        done();

        callable = null;        // to reduce footprint
    }

在 AsyncTask 的源码中重写了done() 方法,其中调用了 postResultIfNotInvoked() 方法。

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

postResultIfNotInvoked() 也会调用 postResult() 方法,但是其不一定会执行。
因为在mWorker 的 call() 方法中已经设置 为 true 了。

mTaskInvoked.set(true);

所以我在这里推测之所以这么写,是防止出现异常,导致出现没有回调结果的情况。在正常的情况下,postResultIfNotInvoked() 方法是不会
调用 postResult() 方法的。

5 、那么为什么要用 FutureTask 呢? (当然这是我认为,可能有错)

下看 AsyncTask 中的其他几个方法:

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

    public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }

    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        return mFuture.get(timeout, unit);
    }

当调用这想通过 get() 方法直接获取结果时,可以直接调用 AsyncTask.get() 等待结果的返回。
可以看到 AsyncTask 中的这几个方法,全部都是依托于 FutureTask 实现。当前线程阻塞,等待结果的返回。
这就是我猜测其用 FutureTask 的原因。可以直接获取结果。

6、 下面来看最开始放在一边的默认的线程池 : 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);
            }
        }
    }

可以看到的是 SerialExecutor 只是实现了 Executor 接口,它只是重写了execute() 方法。将Runnable 包装一下,放进了队列之中。
它并不是一个真正的线程池。将Runnable 放入队列中后,调用了 scheduleNext() 方法。

其实在 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);


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

THREAD_POOL_EXECUTOR 才是一个真正的线程池。关于 THREAD_POOL_EXECUTOR 到底是一个什么样的线程池,可以去看第一篇文章,
那里有详细的介绍。

那为什么 AsyncTask 要搞出 SerialExecutor 这么个鬼呢?

仔细研究其逻辑:

第一此调用时,会把Runnable 放到队列中,然后执行 scheduleNext() 方法,在 scheduleNext() 中判断队列是否存在,是否有数值。
有数值则调用线程池执行。如果这个时候又添加了一个任务有是被加到队列中,mActive 不为空,所以不会调用 scheduleNext() 方法执行任务。
所以这个任务会放在队列之中。这时上一个任务执行完了,会再次调用 scheduleNext() 方法,从队列中获取任务,如果有,则执行。

所以废了这么大的劲,其实就是想维护一个每次只能执行一个任务的线程池,所有的任务会依次执行。

当然如何你不想这样,希望每次可以执行很多任务,那就调用 executeOnExecutor() 方法,传入自定义的线程池,或者 THREAD_POOL_EXECUTOR 每次执行
多个任务。

三 总结:
OK , AsyncTask 的源码就分析到这里。其实到最后我们会发现,真正实现并发的其实还是线程池,AsyncTask 只不过是将其封装一下,暴露一些接口以及参数。方便于调用者使用。我觉得更应该值得学习应该是它封装的思想或者说是思路吧,即如何能通过封装,提供更好的使用,满足更多的需求。

四 下一篇

下一篇会介绍 Android 中另一个可以实现异步操作的类:HandlerThread。并且尝试自定义 HandlerThread ,使其用法更加趋于简单。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值