1. 程式人生 > >Android進階——效能優化之儘量多使用AsyncTask進行短時間網路通訊

Android進階——效能優化之儘量多使用AsyncTask進行短時間網路通訊

引言

對於我們Android 開發來說,網路操作應該是最普遍不過的操作了吧,因為沒有網路操作的APP應該就沒有存在的價值吧,往往網路操作這部分又通常是耗時的,所以為了良好的使用者體驗,我們必須把耗時操作放到非UI執行緒,而實現方式有很多種,比較常見的應該就是Handler+Thread 和AsyncTask這兩種了吧,這兩種方式各有利弊,尤其是涉及到與UI互動的時候使用起來更得注意額外處理一些邏輯,但是從一定程度上AsyncTask使用起來更簡捷方便些。

一、非同步任務AsyncTask概述

總所周知,在Android程式開始執行的時候會單獨啟動一個程序,預設情況下所有這個程式操作都在這個程序中進行。一個Android程式預設情況下只有一個程序,但一個程序中可以有多個執行緒,其中最重要的一個執行緒叫做UI執行緒(也叫Main Thread),除了UI執行緒外的執行緒都叫子執行緒(Worker Thread)。UI執行緒主要負責控制UI介面的顯示、更新、互動、元件的建立等。因此,UI執行緒中的操作延遲越短越好(流暢)。把一些耗時的操作(網路請求、資料庫操作、邏輯計算等)放到單獨的執行緒,可以避免主執行緒阻塞。在API 3之前,我們都是使用Thread+Handler的方式進行非UI和UI互動,API 3的時候 Android給我們提供了一種輕量級的非同步任務類AsyncTask,它可以線上程池中執行後臺任務,然後把執行的進度和最終結果傳遞給主執行緒並在主線中更新UI。AsyncTask封裝了Thread和Handler,通過AsyncTask可以更加方便地執行後臺任務以及在主執行緒中訪問UI,但是AsyncTask並不適合進行特別耗時的後臺任務,對於特別耗時的後臺任務,建議使用執行緒池。因為AsyncTask的設計是圍繞執行緒和處理器的一個輔助類,並不構成一個通用的執行緒框架。Asynctasks應該用於短作業(最多幾秒鐘)如果你需要保持執行緒執行很長一段時間,我們強烈建議您使用不同的java.util.concurrent包如執行API提供的執行緒池和Futuretask。

二、AsyncTask原型

Asynctask是一個抽象的泛型類,它提供了Params,Progress和Result這三個泛型引數,其中Params表示引數的型別,Progress表示後臺任務執行進度的型別,而Result則表示後臺任務返回資料的型別,如果AsyncTask確實不需要傳遞具體的引數,那麼這三個泛型引數可以用Void來代替。該類中實現非同步操作,並提供介面反饋當前非同步執行結果及進度,這些介面中有直接執行在主執行緒中的(如 onPostExecute,onPreExecute等)。

public abstract class AsyncTask<Params, Progress, Result>{
    public
AsyncTask() { mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked
result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); } return 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); } } }; } ... }
  • Params:傳遞給AsyncTask的型別,作為doInBackground的引數型別
  • Progress:作為onProgressUpdate和publishProgress的引數型別
  • Result:由doInBackground返回,作為onPostExecute的引數型別

三、AsyncTask的執行流程

  • onPreExecute():在主執行緒中執行,一般在非同步任務執行之前被呼叫,可以用於做些UI初始化工作。比如彈出進度載入框

  • doInBackground(Params… params):在執行緒池中執行用於執行非同步任務,params引數表示非同步任務輸入的引數(源自執行非同步任務呼叫execute(Params… params)方法時候傳入)。另外通常在此方法中通過呼叫publishProgress方法來更新任務的進度(publishProgress方法會呼叫onProgressUpdata方法),而且還會將計算結果返回到onPostExecute方法中。

  • onProgressUpdata(Progress…values):在主執行緒中執行,當後臺任務的執行進度發生改變時此方法會被呼叫。

  • onPostExecute(Result result):在主執行緒中執行,在一部任務執行之後,此方法會被呼叫,其中result引數是後臺任務的返回值,即doInBackground的返回值。比如可以利用返回的結果更新UI,或者關閉進度條對話方塊等。

這裡寫圖片描述
一般來說基本流程是 onPreExecute先執行,接著是doInBackground,最後才是onPostExecute。除了上述四個方法以外,AsyncTask還提供了onCancelled()方法,他同樣是在主執行緒中執行,當非同步任務被取消時,onCancelld()方法會被呼叫,這時候onPostExecute則不會被呼叫。

四、AsyncTask的使用步驟

1、繼承AsyncTask實現onPreExecute、doInBackground、onProgressUpdata、onPostExecute方法,並指定要傳入的相應引數型別

    /**
    *從自定義非同步任務的原型可以得知,這個非同步任務是需要在doInBackground裡執行一個需要傳入N個String型別作為形參的方法
    */
    protected class SendHttpTask extends AsyncTask<String, Void, Void> {
        @Override
        protected void onPreExecute() {
            LogUtil.showLog("",false);
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            LogUtil.showLog("",false);
            super.onPostExecute(aVoid);
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            LogUtil.showLog("",false);
            super.onProgressUpdate(values);
        }

        @Override
        protected Void doInBackground(String... params) {
            LogUtil.showLog("",false);
            String type = params[0];//傳入的第一個引數
            String content = params[1];//傳入的第二個引數
            sendHttp.sendHttp(type, content);
            return null;
        }
    }

2、直接new 建立物件並通過呼叫execute(Params… params)方法執行非同步任務,其中引數為泛型不定引數,型別不定,引數個數不定,取決於我們建立自定義AsyncTask時候傳入的引數型別,而且execute方法返回的是AsyncTask物件

private SendHttpTask sendCommonTask;
/**錯誤示範
 if (sendCommonTask == null) {
     sendCommonTask = new SendHttpTask();
 }
    sendCommonTask.execute("0", idEdtQuestion.getText().toString().trim());
*/
sendCommonTask= (SendHttpTask) new SendHttpTask().execute("0",idEdtQuestion.getText().toString().trim());

五、AsyncTask在使用的過程中的注意事項

  • AsyncTask的類必須是在主執行緒中載入,這就意味著第一次訪問AsyncTask必須發生在主執行緒,當然這個過程在Android 4.1及以上版本已經被系統自動完成了。在Android 5.0的原始碼中,可以檢視ActivityThread的mian方法,它會呼叫AsyncTask的init方法,這就滿足了AsyncTask的類必須在主執行緒中進行載入這個條件了。

  • AsyncTask的物件必須在主程中建立。

  • execute方法必須在UI執行緒呼叫。

  • 不要在程式中手動去呼叫onpreExecute(),doInBackground, onProgressUpdata和onPostExecute方法,這些方法是由系統自動呼叫的。

  • 一個AsyncTask物件只能執行一次,因為本質也是Thread嘛,即同一例項只能呼叫一次execute方法,否則會報執行時異常,比如樓上的錯誤示範。

  • 在Android 1.6之前,AsyncTask是序列執行任務的,Android 1.6的時候AsyncTask開始採用執行緒池來處理並行任務,但是從Android 3.0開始,為了避免AsyncTask所帶來的併發錯誤,AsyncTask有采用一個執行緒來序列執行任務。儘管如此,在Android 3.0之後的版本,我們仍可以通過AsyncTask的executeOnExecutor方法來並行執行認為。

  • 執行中可以隨時呼叫cancel(boolean)方法取消任務,如果成功呼叫isCancel()會返回true,並不會執行onPostExecute(),取而代之的是呼叫onCancelled()。從原始碼看,如果這個任務已經執行了這個時候呼叫cancel是不會真正的把task結束,而是繼續執行,只不過改變的是執行之後的回撥方法的onPostExecute還是onCancelled.

  • 當非同步任務中涉及到UI操作的時候,需要注意謹防記憶體溢位的問題,一般可遵循以下規範,如果作用於長時間的任務,且是內部類,那麼儲存了Context或者Activity的引用,會導致相關資源不會被GC及時回收有可能導致溢位,所以第一,在Activity生命週期結束前,可以通過呼叫cancel方法取消AsyncTask,第二,如果一定要寫成內部類的形式,對Context採用弱引用WeakRefrence,在使用之前判斷是否為空。