Android執行緒管理之AsyncTask非同步任務

總李寫程式碼發表於2016-08-02

前言:

     前面幾篇文章主要學習了執行緒以及執行緒池的建立與使用,今天來學習一下AsyncTask非同步任務,學習下AsyncTask到底解決了什麼問題?然而它有什麼弊端?正所謂知己知彼百戰百勝嘛!

     執行緒管理相關文章地址:

產生背景:

     我們都知道Android應用程式是單執行緒模型,在子執行緒無法直接操作UI主執行緒,必須通過Handler機制,想了解這方面的知識可以參考這篇文章:Android訊息傳遞之Handler訊息機制(一),所以基於這種考慮所以我們一般情況會採用Thread+Handler來處理比較耗時的操作,但是我們都知道每次new Thread()開銷比較大,而且缺乏管理,被稱為野執行緒,而且可以無限制建立,之間相互競爭,會導致過多佔用系統資源導致系統癱瘓,不利於擴充套件,比如如定時執行、定期執行、執行緒中斷,這時我們引入了執行緒池的概念,整個解決問題的模型就變成了Runnable+Executor+Handler,為了降低開發者的開發難度,AsyncTask應運而生,AsyncTask是對執行緒池的一個封裝,使用其自定義的 Executor 來排程執行緒的執行方式(併發還是序列),並使用 Handler 來完成子執行緒和主執行緒資料的共享。

AsyncTask介紹

   AsyncTask是android提供的輕量級的非同步類,可以直接繼承AsyncTask,在類中實現非同步操作,並提供介面反饋當前非同步執行的程度,最後反饋執行的結果給UI主執行緒.

AsyncTask主要引數、函式解析

 1.)AsyncTask是抽象類.AsyncTask定義了三種泛型型別 Params,Progress和Result
  •  Params 啟動任務執行的輸入引數,比如下載URL
  •  Progress 後臺任務執行的百分比,比如下載進度
  •  Result 後臺執行任務最終返回的結果,比如下載結果
  2.)繼承AsyncTask可以實現的函式
  •  onPreExecute()//此函式是在任務沒被執行緒池執行之前呼叫 執行在UI執行緒中 比如現在一個等待下載進度Progress,也可以不用實現
  •  doInBackground(Params... params)//此函式是在任務被執行緒池執行時呼叫 執行在子執行緒中,在此處理比較耗時的操作 比如執行下載,此函式是抽象函式必須實現
  •  onProgressUpdate(Progress... values)//此函式是任務線上程池中執行處於Running狀態,回撥給UI主執行緒的進度 比如上傳或者下載進度,也可以不用實現
  •  onPostExecute(Result result)//此函式任務線上程池中執行結束了,回撥給UI主執行緒的結果 比如下載結果,也可以不用實現
  •  onCancelled(Result result)/onCancelled()//此函式表示任務關閉
 3.)AsyncTask主要公共函式
  • cancel (boolean mayInterruptIfRunning)//嘗試取消這個任務的執行,如果這個任務已經結束或者已經取消或者不能被取消或者某些其他原因,那麼將導致這個操作失敗,當呼叫此方法時,此方法執行成功並且這個任務還沒有執行,那麼此任務將不再執行。如果任務已經開始,這時執行此操作傳入的引數mayInterruptIfRunning為true,執行此任務的執行緒將嘗試中斷該任務
  • execute (Params... params)//用指定的引數來執行此任務,這個方法將會返回此任務本身,所以呼叫者可以擁有此任務的引用。此方法必須在UI執行緒中呼叫
  • executeOnExecutor(Executor exec,Params... params)//用指定的引數,執行在指定的執行緒池中,這個方法將會返回此任務本身,所以呼叫者可以擁有此任務的引用。此方法必須在UI執行緒中呼叫
  • get ()//等待計算結束並返回結果
  • get (long timeout, TimeUnit unit)//等待計算結束並返回結果,最長等待時間為:timeOut(超時時間)
  • getStatus ()//獲得任務的當前狀態  PENDING(等待執行)、RUNNING(正在執行)、FINISHED(執行完成)
  • isCancelled ()//如果在任務正常結束之前取消任務成功則返回true,否則返回false

 AsyncTask示例:

  1.) 模擬一個下載檔案的需求
        String url = "www.xxx.jpg";
        AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {

            @Override
            protected void onPreExecute() {//此函式是在任務沒被執行緒池執行之前呼叫 執行在UI執行緒中 比如現在一個等待下載進度Progress
                super.onPreExecute();
                Log.e(TAG, "AsyncTask onPreExecute");
            }

            @Override
            protected String doInBackground(String[] params) {//此函式是在任務被執行緒池執行時呼叫 執行在子執行緒中,在此處理比較耗時的操作 比如執行下載
                String url = params[0];
                Log.e(TAG, "AsyncTask doInBackground url---->" + url);
                //模擬下載
                int i = 0;
                for (i = 20; i <= 100; i += 20) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e(TAG, "AsyncTask doInBackground result--progress-->" + i);
                    publishProgress(i);
                }
                String result = "download end";
                return result;
            }

            @Override
            protected void onProgressUpdate(Integer... values) {//此函式是任務線上程池中執行處於Running狀態,回撥給UI主執行緒的進度 比如上傳或者下載進度
                super.onProgressUpdate(values);
                int progress = values[0];
                Log.e(TAG, "AsyncTask onProgressUpdate progress---->" + progress);
            }

            @Override
            protected void onPostExecute(String s) {//此函式任務線上程池中執行結束了,回撥給UI主執行緒的結果 比如下載結果
                super.onPostExecute(s);
                Log.e(TAG, "AsyncTask onPostExecute result---->" + s);
            }

            @Override
            protected void onCancelled() {//此函式表示任務關閉
                super.onCancelled();
                Log.e(TAG, "AsyncTask onCancelled");
            }

            @Override
            protected void onCancelled(String s) {//此函式表示任務關閉 返回執行結果 有可能為null
                super.onCancelled(s);
                Log.e(TAG, "AsyncTask onCancelled---->" + s);
            }
        };
        asyncTask.execute(url);

執行結果

2.)如何關閉一個AsyncTask
boolean mayInterruptIfRunning傳true的情況還是傳false的情況
   if (!asyncTask.isCancelled()) {
        boolean isCancel = asyncTask.cancel(true);
        Log.e(TAG, "AsyncTask isCancel---->" + isCancel);
   }

執行結果:測試發現執行結果一樣

通過上面執行結果可以看出,無論mayInterruptIfRunning傳入true或者false執行的結果都一樣,也就是說當我們呼叫cancel (boolean mayInterruptIfRunning)函式之後,在doInBackground()return後 ,我們將會呼叫onCancelled(Object) 不在呼叫onPostExecute(Object),但是根據執行結果看,我們通過這個函式並沒有真正的終止子執行緒繼續執行,只是捨棄了執行結果,AsyncTask不會不考慮結果而直接結束一個執行緒。呼叫cancel()其實是給AsyncTask設定一個"canceled"狀態。這取決於你去檢查AsyncTask是否已經取消,之後決定是否終止你的操作。對於mayInterruptIfRunning——它所作的只是向執行中的執行緒發出interrupt()呼叫。在這種情況下,你的執行緒是不可中斷的,也就不會終止該執行緒,我們可以在doInBackground(Params... params)中定期檢查isCancelled()狀態,如果檢查到已經關閉,直接終止耗時操作。比如上面的下載可以改成

            @Override
            protected String doInBackground(String[] params) {//此函式是在任務被執行緒池執行時呼叫 執行在子執行緒中,在此處理比較耗時的操作 比如執行下載
                String url = params[0];
                Log.e(TAG, "AsyncTask doInBackground url---->" + url);
                //模擬下載
                int i = 0;
                for (i = 20; i <= 100; i += 20) {
                    if (isCancelled()) {
                        break;
                    }
                    try {
                        Thread.currentThread().sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e(TAG, "AsyncTask doInBackground result--progress-->" + i);
                    publishProgress(i);
                }
                if (isCancelled()) {
                    return "download cancel";
                }
                String result = "download end";
                return result;
            }

執行結果

 AsyncTask需要注意的地方:

 1.)生命週期

    AsyncTask不與任何元件繫結生命週期,所以在Activity/或者Fragment中建立執行AsyncTask時,最好在Activity/Fragment的onDestory()呼叫 cancel(boolean);

 2.)記憶體洩漏

      如果AsyncTask被宣告為Activity的非靜態的內部類,那麼AsyncTask會保留一個對建立了AsyncTask的Activity的引用。如果Activity已經被銷燬,AsyncTask的後臺執行緒還在執行,它將繼續在記憶體裡保留這個引用,導致Activity無法被回收,引起記憶體洩   露。

 3.) 結果丟失

     螢幕旋轉或Activity在後臺被系統殺掉等情況會導致Activity的重新建立,之前執行的AsyncTask會持有一個之前Activity的引用,這個引用已經無效,這時呼叫onPostExecute()再去更新介面將不再生效。

4.)並行還是序列

     在Android 1.6之前的版本,AsyncTask是序列的,在1.6至2.3的版本,改成了並行的。在2.3之後的版本又做了修改,可以支援並行和序列,當想要序列執行時,直接執行execute()方法,如果需要並行執行,則要執行executeOnExecutor(Executor)

相關文章