AsyncTask源碼分析之Android篇

在上一篇AsyncTask源碼分析中爲大家做了一些基礎知識的鋪墊,這篇就爲大家真正解析一下AsyncTask源碼:
首先爲大家介紹一些AsyncTask的一些特性:
1、AsyncTask在3.0版本以後默認是串行的
2、AsyncTask不能在非UI線程中創建
3、AsyncTask任務數量上限是128個
4、AsyncTask只適合短時間幾秒的耗時操作,不適合長時間的
耗時操作,原因是AsyncTask默認是串行的,如果你一個操作執行幾分鐘,那麼其他的任務就根本沒有辦法執行
5、每個AsyncTask創建一次只能使用一次

首先大家看一下:

   /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    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的構造方法,在上面的註釋中我們看到AsyncTask必須在UI線程中創建,卻沒有說爲什麼。只是初始化了兩個對象mWorker,mFuture.

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

mWorker實際上是一個實現了Callable接口的類罷了,mFuture也不過是一個FutureTask類。暫時沒有發現什麼用那我們接着往下看:

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

當你執行一個任務必然會調用execute方法,那這個方法最終調用的是executeOnExecutor(sDefaultExecutor, params)

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創建一次只能用一次,因爲AsyncTask是有狀態的,運行之後的狀態爲Running就會報錯,只有狀態爲Pending的時候才能繼續運行下去。params是傳進來的參數,sDefultExecutor是什麼呢?

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

這個sDefaultExector這個方法裏面所有的邏輯操作都是在子線程中執行的,並且是串行的,這也是爲什麼AsyncTask默認是串行執行的原因所在。我來分析一下這個串行執行的奧祕。

1、FutueTask對象通過offer()方法添加進ArrayQueue數組隊列中
2、mActivie == null
3、scheduleNext方法中
4、通過poll()方法取出,賦給mActivie,通過線程池ThreadPoolExecutor去執行

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

5、執行FutureTask裏面的run()方法裏面的內容,其實最終執行的是Callable()接口裏面call()方法裏面的內容,就是這裏傳入的mWorker對象裏面call()方法裏面的內容

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

大家驚訝的在這裏發現了我們熟悉的doInBackgroud(mParams)方法,所以doInBackgroud()內容才能在後臺操作,因爲這個方法是在子線程中執行的,接着講執行的結果返回了出去,調用了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;
    }

看到postResult方法裏面的內容,大家驚訝的發現了Handler的身影,我們仔細看一下getHandler()方法的源代碼:

private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }
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;
            }
        }
    }

大家此時突然發現這個獲得的Handler用的是主線程的Looper,這下大家明白爲什麼AsyncTask方法必須在主線程中創建了吧。在handleMessage方法裏面我們看到有兩種狀態一種是MESSAGE_POST_PROGRESS,調用的是更新進度的方法,爲什麼能夠直接更新UI,其實就是利用了Handler進行了線程的切換。還有另外一種狀態是MESSAGE_POST_RESULT,大家看看這個狀態最終調用了finish()

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

大家又看到了onPostExecte這個方法,現在大家對AsyncTask的執行過程有了基本的瞭解,我們回頭來解析完AsyncTask串行的祕密。

        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對象的run()方法之後,谷歌的工程師在這裏做了一個絕妙的處理,利用了Java當中捕獲異常當中的finally絕對會執行的特性,在finally中又再次調用了sheduNext方法,形成了一個如果ArrayQueue這個方法中如果有消息就絕對會執行完成的變異遞歸,如果沒有了,那麼mActive又會立刻爲null,一旦mActive爲null,又會再次走剛剛那套流程。但無論如果,每次都只有一個對象被執行。這就是AsyncTask串行的祕密。

另外爲大家講述一些AsyncTask常用的API:

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

其實這個就是調用了mFuture的cancle()方法,我們經常在一個Activity銷燬的時候調用這個方法,免得消息沒有處理完,AsyncTask因爲無法得到回收。造成內存泄漏

public static void setDefaultExecutor(Executor exec) {
        sDefaultExecutor = exec;
    }

其實這邊就是剛纔講的設置默認的Executor,通過設置這個可以將AsyncTask變成併發的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章