Async分析

AsyncTask概述

AsyncTask是安卓爲簡化子線程訪問ui操做提供的一個線程類工具類,可是AsncTask在Android系統的不斷升級中,通過了屢次修改,致使不一樣的API版本上使用AsyncTask具備不一樣的表現,接下來主要分析AsyncTask的使用與注意事項。android

AsyncTask是一個輕量級的一部任務類,它能夠在子線程執行任務,而後把執行結果分發到主線程進行處理,原理上來說,AsyncTask是對線程池和Handler的封裝,並且AsyncTask**並不適合執行特別耗時的後臺任務**,不然頗有可能形成內存泄漏。併發

AsyncTask使用

AsyncTask的定義以下:異步

  public abstract class AsyncTask<Params, Progress, Result>{.....}

AsyncTask定義了三個泛型參數,若是不須要傳遞具體的參數可使用Void代替,這三個泛型參數與AsyncTask定義的幾個核心方法有關:async

//主線程執行,異步任務執行前被調用   
protected void onPreExecute() {
}

//工做線程執行,用於執行任務,方法參數由泛型參考Params肯定,返回參數有Result參數規定
protected abstract Result doInBackground(Params... params);
//主線程執行,用於處理doInBackground的返回值
oprotected void onPostExecute(Result result) {
}

//須要在doInBackground方法中調用,用於更新任務的執行進度,方法參數有Progress泛型參數規定
protected final void publishProgress(Progress... values) {
//在主線程被調用,當在doInBackground調用publishProgress分發任務進度是,此方法就會被調用
protected void onProgressUpdate(Progress... values) {
}

其次,AsyncTask還提供了onCancel方法,它一樣在主線程執行,當調用AsyncTask的cancel方法時,任務被取消,onCancel被調用,而onPostExecute不會被調用ide

AsyncTask的使用注意事項

  1. AsyncTask的類必須在主線程被加載,這意味着第一次訪問AsycnTask必須在主線程,固然這個過程在Android4.1以上版本系統已經自動完成,在android5.0的源碼中ActivityThread的main方法中就掉用了AsyncTask.init()方法(具體爲何必須在主線程被加載,稍後分析)工具

  2. AsyncTask對象必須在住下次被建立oop

  3. execute方法必須在主線程被調用post

  4. 不要主動調用onPreExecute等方法ui

  5. 一個AsyncTask只能被執行一次,不然會有異常this

  6. AsyncTask在不一樣系統版本的表現

    • Android1.6以前:AsyncTask是串行執行的
    • Android1.6開始:AsyncTask開始使用線程池處理並行任務,他是並行的
    • Android3.0開始:爲了不AsyncTask帶來的併發錯誤,AsyncTask又開始使用單個線程來執行任務,但那時在3.0以後,可使用AsyncTask的executeOnExecutor方法來並行的執行任務。

AsyncTask的工做原理

瞭解AsyncTask的工做原理,先從它的execute方法開始分析:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return 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;
    }

execute調用了executeOnExecutor方法,executeOnExecutor方法會把mFuture交給sDefaultExecutor去執行,看到這裏,咱們須要先了解一下executeOnExecutor中的幾個變量: 
- mStatus: 用於記錄一個AsyncTask的運行狀態 
- mWorker:它的類型是WorkRunnbale在AsyncTask構造方法被建立,WorkRunnbale實現了Callable接口,mWorker做爲WorkRunnbale的子類,實現了來自Callable的call方法,並在call方法中調用了doInBackground方法。 
- mFuture:它的實際類型是FutureTask,是一個併發相關類,它的父類實現了Runnable和Future接口,它自己接收一個Callable類型對象,當它被提交給一個任務執行器是,它的run(來自Runnable)方法被調用,而在run中會調用Callable的call方法。

具體咱們能夠看AsyncTask的構造方法:

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

能夠看到mFuture接收的Callable對象正是mWorker。

接着分析executeOnExecutor方法,任務執行的參數params賦值給了mWorker.mParams,而後把mFuture叫給了sDefaultExecutor執行,下面來分析一些這個sDefaultExecutor:

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就是一個SerialExecutor,它內部維護着一個mTasks隊列,用於存儲進程內AsyncTask提交的任務,看SerialExecutor的execute方法,接收的mFuture並非直接運行,而是封裝成爲一個Runnable插入到隊列中去,而後判斷mActive是否爲null,若是mActive=null,則會調用scheduleNext取出一個任務交給THREAD_POOL_EXECUTOR去真的執行任務,只有當一個任務執行完畢,纔會再一次調用scheduleNext方法,由此分析AsyncTask確實是串行執行任務的。

可見AsyncTask有兩個線程池: 
- SerialExecutor 用於對任務進行串行調度,而不是真的執行任務 
- THREAD_POOL_EXECUTOR真正的任務執行器,

上面分析到當mFutrue被提交到線程池中,他的run方法被調用,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);
                }
            };

在call方法中,調用了AsyncTask的doInBackground方法,而後調用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;
    }

這裏調用getHandler()獲取的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的子類,在調用getHandler()方法若是沒有初始化,則它會被初始化,這裏也說明了爲何AsyncTask必須在主線程調用的緣由,

各個說到postResult方法把執行結果封裝成AsyncTaskResult對象,而後發送了一個MESSAGE_POST_RESULT類型的消息,很顯然這個消息會在InternalHandler中被處理,處理代碼以下:

result.mTask.finish(result.mData[0]);

AsyncTask的finish被調用:

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

方法很簡單,若是任務被取消,則調用onCancelled方法,不然調用onPostExecute方法,最後把任務的狀態置爲FINISHED。

AsyncTask的取消

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

mCancelled一個保證原子操做的併發類,mCancelled被設置爲true,而AsyncTask中使用到它的就是isCancelled方法,而isCancelled方法被調用的地方有

finish方法
publishProgress方法

可見mCancelled的做用只是任務取消的時候onPostExecute和onProgressUpdate被調用,因此關在在於mFuture.cancel(mayInterruptIfRunning)方法,其實它的內部也只是調用了interrupt方法,而咱們知道這個interrupt用來中止線程並不可靠,也就是說AsyncTask的cancel方法內部並不能真正的讓任務中止執行。

關於AsyncTask帶來的內存泄漏

通常AsyncTask做用一個內部類被建立並調用,而內部類持有外部類的引用,讓AsyncTask去執行一個長時間任務時,這是即便退出當前界面,調用他的cancel方法,並不見得就能夠當即中止 
AsyncTask所執行的任務,只要任務在執行AsyncTask對象就不會被回收,而AsyncTask所持有的外部類應用也確定不會被回收,因此頗有可能就形成了內存泄漏,在Android中,AsyncTask也只被推薦用於短耗時的異步任務。

AsyncTask的任務承載量與兼容

爲了解決系統版本的差別性,在Support V4包中提供了AsyncTaskCompat類,實現以下:

public class AsyncTaskCompat {

    public static <Params, Progress, Result> AsyncTask<Params, Progress, Result> executeParallel(
            AsyncTask<Params, Progress, Result> task,
            Params... params) {
        if (task == null) {
            throw new IllegalArgumentException("task can not be null");
        }

        if (Build.VERSION.SDK_INT >= 11) {
            // From API 11 onwards, we need to manually select the THREAD_POOL_EXECUTOR
            AsyncTaskCompatHoneycomb.executeParallel(task, params);
        } else {
            // Before API 11, all tasks were run in parallel
            task.execute(params);
        }
        return task;
    }
}
    class AsyncTaskCompatHoneycomb {

    static <Params, Progress, Result> void executeParallel(
            AsyncTask<Params, Progress, Result> task,
            Params... params) {
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }

}

而THREAD_POOL_EXECUTOR的實現以下,它就是一個ThreadPoolExecutor。

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

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

ThreadPoolExecutor所能承載任務數是有限的,它能承載的任務數爲sPoolWorkQueue的size+MAXIMUM_POOL_SIZE,若是踢提交的任務數超過了這個數,默認ThreadPoolExecutor會拋出異常,因此在使用的時候必定要注意。

對於任務超載,ThreadPoolExecutor提供了一個方法用時實現超載時候的任務處理策略

executor.setRejectedExecutionHandler();

參照於文章:https://blog.csdn.net/zty19901005/article/details/50727106
相關文章
相關標籤/搜索