Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> AsyncTask你真的用對了嗎?

AsyncTask你真的用對了嗎?

編輯:關於Android編程

前言

在之前的文章深入探究了Handler,我們知道了Android的消息機制主要靠Handler來實現,但是在Handler的使用中,忽略內存洩露的問題,不管是代碼量還是理解程度上都顯得有點不盡人意,所以Google官方幫我們在Handler的基礎上封裝出了AsyncTask。但是在使用AsyncTask的時候有很多細節需要注意,它的優點到底體現在哪裡?還是來看看源碼一探究竟。

怎麼使用

來一段平常簡單使用AsyncTask來異步操作UI線程的情況,首先新建一個類繼承AsyncTask,構造函數傳入我們要操作的組件(ProgressBarTextView

class MAsyncTask extends AsyncTask{
        private ProgressBar mProgressBar;
        private TextView mTextView;

        public MAsyncTask(ProgressBar mProgressBar, TextView mTextView) {
            this.mProgressBar = mProgressBar;
            this.mTextView = mTextView;
        }

        @Override
        protected void onPreExecute() {
            mTextView.setText("開始執行");
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(Void... params) {
            for(int i = 0; i <= 100; i++){
                publishProgress(i);//此行代碼對應下面onProgressUpdate方法
                try {
                    Thread.sleep(100);//耗時操作,如網絡請求
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "執行完畢";
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            mProgressBar.setProgress(values[0]);
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(String s) {
            mTextView.setText(s);
            super.onPostExecute(s);
        }
    }

在Activity中創建我們新建的MAsyncTask實例並且執行(無關代碼省略):

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
        asyncTask.execute();//開始執行
        ...
    }
}

看看原理

在上面的代碼,我們開了個單一的線程來執行了一個簡單的異步更新UI的操作(哈哈,可能會覺得AsyncTask有些大材小用了哈),現在來看看AsyncTask具體是怎麼實現的,先從構造方法開始:

public abstract class AsyncTask 

AsyncTask為抽象類,並且有三個泛型,我覺得這三個泛型是很多使用者不懂的根源:

params:參數,在execute() 傳入,可變長參數,跟doInBackground(Void… params) 這裡的params類型一致,我這裡沒有傳參數,所以可以將這個泛型設置為Void Progress:執行的進度,跟onProgressUpdate(Integer… values)values的類型一致,一般情況為Integer Result:返回值,跟String doInBackground 返回的參數類型一致,且跟onPostExecute(String s)s參數一致,在耗時操作執行完畢調用。我這裡執行完畢返回了個字符串,所以為String

看了這三個泛型,我們就基本上了解了AsyncTask的執行過程,主要就是上面代碼重寫的那幾個方法,現在來仔細看,首先在繼承AsyncTask時有個抽象方法必須重寫:

@WorkerThread
protected abstract Result doInBackground(Params... params);

顧名思義,這個方法是在後台執行,也就是在子線程中執行,需要子類來實現,在這個方法裡面我們可以調用publishProgress來發送進度給UI線程,並且在onProgressUpdate方法中接收。

根據調用順序,我們一般會重寫這幾個方法:

//在doInBackground之前調用,在UI線程內執行
@MainThread
protected void onPreExecute() {
}
//在執行中,且在調用publishProgress方法時,在UI線程內執行,用於更新進度
@MainThread
protected void onProgressUpdate(Progress... values) {
}
//在doInBackground之後調用,在UI線程內執行
@MainThread
protected void onPostExecute(Result result) {
}

我們來看看這個publishProgress方法是怎麼來調用onProgressUpdate方法的:

@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult
(this, values)).sendToTarget(); } }

使用obtainMessage是避免重復創建消息,調用了getHandler()然後發送消息,這裡是一個單例

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

返回了一個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_PROGRESS後我們發現其實內部就是調用了Handler來實現這一切,包括執行結束時調用finish方法,這個我們後面再說。從頭來看一下AsyncTask的執行過程,來到execute方法:

/**
This method must be invoked on the UI thread.(這行注釋為Google官方注釋)
*/
@MainThread
public final AsyncTask execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

注意!此方法必須在UI線程調用,這裡就不做測試了。在這裡又調用executeOnExecutor:

@MainThread
public final AsyncTask executeOnExecutor(Executor exec,
        Params... params) {
    ......

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

我們發現在UI線程先調用了onPreExecute(),將傳入的參數賦值給mWorker.mParams,然後調用了參數exec的execute方法,並且將mFuture作為參數傳入,這裡就設計到了三個對象:sDefaultExecutor(在executeOnExecutor中傳入)、mWorkermFuture,來看看它們的賦值在哪裡:

sDefaultExecutor:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
......
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
......
private static class SerialExecutor implements Executor {
   final ArrayDeque mTasks = new ArrayDeque();
   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的賦值默認就是SERIAL_EXECUTOR,也就是一個順序執行的線程池,內部實現有一個任務隊列。

mWorker

private final WorkerRunnable mWorker;

public AsyncTask() {
        mWorker = new WorkerRunnable() {
            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);
            }
        };
        ......
}

private static abstract class WorkerRunnable implements Callable {
        Params[] mParams;
}

在AsyncTask的構造方法中,給mWorker賦值為一個Callable(帶返回參數的線程,涉及到java並發的一些基礎知識,這裡不贅述),並且在call方法中執行了doInBackground方法,最後調用postResult方法

mFuture

private final FutureTask mFuture;
public AsyncTask() {
        ......
        mFuture = new FutureTask(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為FutureTask類型,這裡將mWorker傳入,在mWorker執行完畢後調用postResultIfNotInvoked方法,我們先看看這個方法:

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

其實這個方法也最後調用了postResult,在這之前做了個有沒調用的判斷,確保任務執行完畢後調用此方法。來看看postResult方法:

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

又看到了熟悉的obtainMessagesendToTarget發送消息,這次消息內容變為MESSAGE_POST_RESULT,再來看看我們剛才已經提到的InternalHandlerhandleMessage方法:

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

最後根據消息類型,這裡調用了result.mTask.finish,result類型為AsyncTaskResult

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

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

mTask的類型為AsyncTask,找到AsyncTask的finish方法:

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

最後如果沒有取消的話調用了onPostExecute,也就是我們之前重寫的那個方法,在執行完畢後調用,並且此方法也在子線程。

多線程並發

正如開題所說,AsyncTask本質上就是對Handler的封裝,在執行之前,執行中,執行完畢都有相應的方法,使用起來也一目了然,不過這還並不是AsyncTask的最大的優點,AsyncTask最適合使用的場景是多線程,開始在代碼中已經看到了在AsyncTask內部有自己維護的線程池,默認的是SERIAL_EXECUTOR

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

按照順序執行,一個任務執行完畢再執行下一個,還提供有一個支持並發的線程池:

//獲取CPU數目
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;
//空閒線程超時時間(單位為S)
private static final int KEEP_ALIVE = 1;
//線程工廠
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};
//阻塞隊列,用來保存待執行的任務(最高128個)
private static final BlockingQueue sPoolWorkQueue =
        new LinkedBlockingQueue(128);

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

聲明為static,多個實例同用一個線程池,這個是Googl官方自帶的一個根據cpu數目來優化的線程池,使用方法如下:

for(int i = 0; i < 100; i++) {//模擬100個任務,不超過128
    MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
    asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

executeOnExecutor中我們還可以傳入自己自定義的線程池:

//跟默認一樣的按順序執行
asyncTask.executeOnExecutor(Executors.newSingleThreadExecutor());
//無限制的Executor
asyncTask.executeOnExecutor(Executors.newCachedThreadPool());
//同時執行數目為10的Executor
asyncTask.executeOnExecutor(Executors.newFixedThreadPool(10));

總結

AsyncTask使用起來的確很簡單方便,內部也是Android的消息機制,並且很快捷的實現了異步更新UI,特別是多線程時也可以很好的表現,這個是我們單獨使用Handler時不具備的,但是在使用過程中注意內部方法的調用順序以及調用的時機,比如asyncTask.execute() 要在UI主線程中調用,在子線程中調用是不可以的,還有就是在使用時根據情況來決定到底應該用哪種線程池。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved