Android 并发二三事之AsyncTask

来源:互联网 发布:投资组合优化模型 编辑:程序博客网 时间:2024/06/05 11:18

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 ,使其用法更加趋于简单。

0 0
原创粉丝点击