前言:
前面幾篇文章主要學習了線程以及線程池的創建與使用,今天來學習一下AsyncTask異步任務,學習下AsyncTask到底解決了什么問題?然而它有什么弊端?正所謂知己知彼百戰百勝嘛!
線程管理相關文章地址:
- Android線程管理之Thread使用總結
- Android線程管理之ExecutorService線程池
- Android線程管理之ThreadPoolExecutor自定義線程池
- Android線程管理之AsyncTask異步任務
- Android線程管理之ThreadLocal理解及應用場景
產生背景:
我們都知道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)
