AsyncTask的使用

AsyncTask的簡單使用

##先大概認識下Android.os.AsyncTask類: android的類AsyncTask對線程間通信進行了包裝,提供了簡易的編程方式來使後臺線程和UI線程進行通信:後臺線程執行異步任務,並把操做結果通知UI線程。html

public abstract class AsyncTask<Params, Progress, Result> {
...
}
  • AsyncTask是抽象類.AsyncTask定義了三種泛型類型 Params,Progress和Result。
  • Params 啓動任務執行的輸入參數,好比HTTP請求的URL。
  • Progress 後臺任務執行的百分比。
  • Result 後臺執行任務最終返回的結果,好比String,Integer等。
  • AsyncTask的執行分爲四個步驟,每一步都對應一個回調方法,開發者須要實現這些方法。
    1. 繼承AsyncTask
    1. 實現AsyncTask中定義的下面一個或幾個方法
    • onPreExecute(), 該方法將在執行實際的後臺操做前被UI 線程調用。能夠在該方法中作一些準備工做,如在界面上顯示一個進度條,或者一些控件的實例化,這個方法能夠不用實現。
    • doInBackground(Params...), 將在onPreExecute 方法執行後立刻執行,該方法運行在後臺線程中。這裏將主要負責執行那些很耗時的後臺處理工做。能夠調用 **publishProgress()**方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。
    • onProgressUpdate(Progress...),在publishProgress方法被調用後,UI 線程將調用這個方法從而在界面上展現任務的進展狀況,例如經過一個進度條進行展現。
    • onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI 線程調用,後臺的計算結果將經過該方法傳遞到UI 線程,而且在界面上展現給用戶.
    • onCancelled(),在用戶取消線程操做的時候調用。在主線程中調用onCancelled()的時候調用。
    • 具體串行並行的調用待整理
  • 爲了正確的使用AsyncTask類,如下是幾條必須遵照的準則:
    1. Task的實例必須在UI 線程中建立
    1. execute方法必須在UI 線程中調用
    1. 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法,須要在UI線程中實例化這個task來調用。
    1. 該task只能被執行一次,不然屢次調用時將會出現異常

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)任務被取消時的方法。編程

  • 若是任務沒有被執行直接執行onCancelled(Result result)方法,且在此執行該任務時報異常。
  • 若是任務執行時被取消了,doInBackground()方法會一直執行到在次判斷是否取消的標示,而後調用onCancelled(Result result)方法。
  • task.cancel(boolean b),b 是否能夠打斷正在執行的任務
@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)

相關文章
相關標籤/搜索