##先大概認識下Android.os.AsyncTask類: android的類AsyncTask對線程間通信進行了包裝,提供了簡易的編程方式來使後臺線程和UI線程進行通信:後臺線程執行異步任務,並把操做結果通知UI線程。html
public abstract class AsyncTask<Params, Progress, Result> { ... }
doInBackground方法和onPostExecute的參數必須對應,這兩個參數在AsyncTask聲明的泛型參數列表中指定,第一個爲doInBackground接受的參數,第二個爲顯示進度的參數,第第三個爲doInBackground返回和onPostExecute傳入的參數。android
public class MainActivity extends Activity { Button download; ProgressBar pb; TextView tv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pb = (ProgressBar) findViewById(R.id.pb); tv = (TextView) findViewById(R.id.tv); download = (Button) findViewById(R.id.download); download.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DownloadTask dTask = new DownloadTask(); dTask.execute(1000); } }); } class DownloadTask extends AsyncTask<Integer, Integer, String> { // 後面尖括號內分別是參數(例子裏是線程休息時間),進度(publishProgress用到),返回值 類型 @Override protected void onPreExecute() {//前期準備工做1.不能作耗時的任務2.本方法執行完後才能執行 doInBackground(Integer... params) tv.setText("開始執行任務"); } @Override protected String doInBackground(Integer... params) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 第二個執行方法,onPreExecute()執行完後執行 for (int i = 0; i <= 100; i++) { pb.setProgress(i);//能夠在主線程或子線程裏調用 ,子線程時內部使用了View的post方法, publishProgress(i); try { Thread.sleep(params[0]); } catch (InterruptedException e) { e.printStackTrace(); } } return "執行完畢"; } @Override protected void onProgressUpdate(Integer... progress) { // 這個函數在doInBackground調用publishProgress時觸發,雖然調用時只有一個參數 // 可是這裏取到的是一個數組,因此要用progesss[0]來取值 // 第n個參數就用progress[n]來取值 tv.setText(progress[0] + "%"); //super.onProgressUpdate(progress); } @Override protected void onPostExecute(String result) { // doInBackground返回時觸發,換句話說,就是doInBackground執行完後觸發 // 這裏的result就是上面doInBackground執行後的返回值,因此這裏是"執行完畢" setTitle(result); //super.onPostExecute(result); } } }
調用task.cancel(true);的方法在doInBackground()方法裏判斷任務是否取消,若是取消儘快結束後臺方法。複寫onCancelled(Result result)任務被取消時的方法。編程
@Override protected String doInBackground(Integer... params) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 第二個執行方法,onPreExecute()執行完後執行 String res = null; for (int i = 0; i <= 100; i++) { Log.d("jpc",i+" 是否取消了"+isCancelled()); if(isCancelled()){//添加是否取消的判斷,儘快結束doInBackground()方法 res = "在"+i+"時取消了"; Log.d("jpc",i+" 取消了"); break; }else{ pb.setProgress(i);//能夠在主線程或子線程裏調用 ,子線程時內部使用了View的post方法, publishProgress(i); try { Thread.sleep(params[0]); } catch (InterruptedException e) { e.printStackTrace(); Log.d("jpc",i+" "+e); res = e.getMessage(); } res = "執行完畢"; } } return res; } @Override protected void onCancelled(String result) { Log.d("jpc"," onCancelled 取消了"); tv.setText(result +" onCancelled"); }
public static void execute(Runnable runnable) public final AsyncTask<Params, Progress, Result> execute(Params... params) public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params)數組
public static void execute(Runnable runnable):API 11 3.0之後提供的。利用AsyncTask裏的串行線程池來執行任務,任務是依次執行,具體執行任務的線程來自線程池中。併發
private void executeTest1(){ for(int i = 0;i<200;i++){ final int index = i; Runnable runnable = new Runnable() { @Override public void run() { Log.d("jpc",index + " 開始執行 Thread ID = "+Thread.currentThread().getId()+ " Thread Name = "+Thread.currentThread().getName()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } Log.d("jpc",index + " 結束執行 Thread ID = "+Thread.currentThread().getId()+ " Thread Name = "+Thread.currentThread().getName()); } }; AsyncTask.execute(runnable); } }
public final AsyncTask<Params, Progress, Result> execute(Params... params):利用串行的線程池依次執行任務,返回任務自己能夠用來取消任務等操做。異步
**public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params)**API 11 3.0之後提供的。 這個接口容許開發者提供自定義的線程池來運行和調度Thread,若是你想讓全部的任務都能併發同時運行,那就建立一個沒有限制的線程池(Executors.newCachedThreadPool()),並提供給AsyncTask。這樣這個AsyncTask實例就有了本身的線程池而沒必要使用AsyncTask默認的(AsyncTask.THREAD_POOL_EXECUTOR ,核心線程數有限制超過等待,任務隊列128有限制超過報錯)。async
private void executeTest2(){ for(int i = 0;i<200;i++){ MyTask task = new MyTask(); task.execute(i); } } @SuppressLint("NewApi") private void executeTest3(){ for(int i = 0;i<200;i++){ MyTask task = new MyTask(); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,i); } } @SuppressLint("NewApi") private void executeTest4(){ for(int i = 0;i<2000;i++){ MyTask task = new MyTask(); task.executeOnExecutor(Executors.newCachedThreadPool(),i); } } class MyTask extends AsyncTask<Integer, Void, String>{ @Override protected String doInBackground(Integer... params) { Log.d("jpc",params[0] + " 開始執行 Thread ID = "+Thread.currentThread().getId()+ " Thread Name = "+Thread.currentThread().getName()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } Log.d("jpc",params[0] + " 結束執行 Thread ID = "+Thread.currentThread().getId()+ " Thread Name = "+Thread.currentThread().getName()); return params[0]+" 完成"; } }
有時候 AsyncTask.execute()的任務不必定立刻執行 AsyncTask主要有二個部分:一個是與主線各的交互,另外一個就是線程的管理調度。雖然可能多個AsyncTask的子類的實例,可是AsyncTask的內部Handler和ThreadPoolExecutor都是進程範圍內共享的,其都是static的,也即屬於類的,類的屬性的做用範圍是CLASSPATH,由於一個進程一個VM,因此是AsyncTask控制着進程範圍內全部的子類實例。 與主線程交互 與主線程交互是經過Handler來進行的 線程任務的調度 內部會建立一個進程做用域的線程池來管理要運行的任務,也就就是說當你調用了AsyncTask#execute()後,AsyncTask會把任務交給線程池,由線程池來管理建立Thread和運行Therad。 因此全部的AsyncTask並不都會運行在單獨的線程中,而是被SERIAL_EXECUTOR順序的使用線程執行。由於應用中可能還有其餘地方使用AsyncTask,因此某個AsyncTask也許會等待到其餘任務都完成時才得以執行而不是調用executor()以後立刻執行。 那麼解決方法其實很簡單,要麼直接使用Thread,要麼建立一個單獨的線程池(Executors.newCachedThreadPool())。或者最簡單的解法就是使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),這樣起碼不用等到前面的都結束了再執行。ide
生命週期 關於AsyncTask存在一個這樣普遍的誤解,不少人認爲一個在Activity中的AsyncTask會隨着Activity的銷燬而銷燬。而後事實並不是如此。AsyncTask會一直執行doInBackground()方法直到方法執行結束。一旦上述方法結束,會依據狀況進行不一樣的操做。函數
若是cancel(boolean)調用了,則執行onCancelled(Result)方法 若是cancel(boolean)沒有調用,則執行onPostExecute(Result)方法 AsyncTask的cancel方法須要一個布爾值的參數,參數名爲mayInterruptIfRunning,意思是若是正在執行是否能夠打斷, 若是這個值設置爲true,表示這個任務能夠被打斷,不然,正在執行的程序會繼續執行直到完成。若是在doInBackground()方法中有一個循環操做,咱們應該在循環中使用isCancelled()來判斷,若是返回爲true,咱們應該避免執行後續無用的循環操做。 總之,咱們使用AsyncTask須要確保AsyncTask正確地取消。post
很差好工做的cancel() 有時候起做用。 若是你調用了AsyncTask的cancel(false),doInBackground()仍然會執行到方法結束,只是不會去調用 onPostExecute()方法。可是實際上這是讓應用程序執行了沒有意義的操做。那麼是否是咱們調用cancel(true)前面的問題就能解決呢?並不是如此。若是mayInterruptIfRunning設置爲true,會使任務儘早結束,可是若是的doInBackground()有不可打斷的方法會失效,好比這個BitmapFactory.decodeStream() IO操做。可是你能夠提早關閉IO流並捕獲這樣操做拋出的異常。可是這樣會使得cancel()方法沒有任何意義。
內存泄露 還有一種常見的狀況就是,在Activity中使用非靜態匿名內部AsyncTask類,因爲Java內部類的特色,AsyncTask內部類會持有外部類的隱式引用。因爲AsyncTask的生命週期可能比Activity的長,當Activity進行銷燬AsyncTask還在執行時,因爲AsyncTask持有Activity的引用,致使Activity對象沒法回收,進而產生內存泄露。
結果丟失 另外一個問題就是在屏幕旋轉等形成Activity從新建立時AsyncTask數據丟失的問題。當Activity銷燬並創新建立後,還在運行的 AsyncTask會持有一個Activity的非法引用即以前的Activity實例。致使onPostExecute()沒有任何做用。
Android實戰技巧:深刻解析AsyncTask [The Hidden Pitfalls of AsyncTask] (http://blog.danlew.net/2014/06/21/the-hidden-pitfalls-of-asynctask/) [Android中糟糕的AsyncTask] (http://www.open-open.com/lib/view/open1417955629527.html)