Android:非同步處理之AsyncTask的應用(二),androidasynctask
前言
在上一篇文章中《Android:非同步處理之Handler+Thread的應用(一)》,我們知道Android的UI主線程主要負責處理使用者的按鍵事件、使用者的觸屏事件以及螢幕繪圖事件等;既然UI老人家都這麼忙了,我們這些開發人員肯定不能不識趣的去添亂阻塞UI線程什麼的,否則UI介面萬一停止回應了呢——這不是招罵的節奏嗎?!所以我們知道用Handler+Thread的方法,在子線程中處理耗時的任務,任務完成後通過Handler通知UI主線程更新UI介面,皆大歡喜有木有。
可是這樣,還是有某些人覺得用Handler+Thread的代碼會比較繁瑣,當然這個某些人裡麵包括我們偉大的Google。所以AsyncTask(非同步任務)在Android 1.5中橫空出世;相對於Handler來說,由於比較好的封裝,AsyncTask顯得更加輕量級一點,適用於簡單的非同步處理;當然使用起來也比較簡潔,果然是Google的親兒子!
概述
AsyncTask是一個抽象類別,通常是被繼承的命。AsyncTask的內部會維持一個靜態線程池,每個背景工作自然也會被提交到線程池中運行,同時也使用Handler+Thread的機制來調用AsyncTask的各個回調方法;回調方法是在主線程啟動並執行,所以該幹什麼我們都懂(~ o ~)~zZ(趕緊跟UI介面套近乎呀)。
我們知道AsyncTask<Params, Progress, Result>是抽象類別,我們可以在這裡面看出它支援三種泛型:
1、Params:我們的AsyncTask要開始幹活時,我們給他的輸入的參數的類型,也就是傳遞給背景參數
2、Progress:AsyncTask向我們報告它幹活進度的參數類型,舉個例子就是下載進度的百分比
3、Result:後台執行任務完成,返回的結果的參數類型
如果某個泛型我們不需要指定,我們可以大大方方的指定Void,沒事AsyncTask不會傷心滴。
當然Google也幫我們將AsyncTask的背景工作啟動並執行五種狀態,分別是:1、準備運行,2、正在後台運行,3、進度更新,4、完成背景工作,5、取消任務。每種狀態在AsyncTask中各有相應的回調方法。
1、準備運行:onPreExecute(),在任務開啟時該回調方法立即在UI線程中被調用,同時也是在執行後台耗時操作前被調用;通常該方法用於完成一些初始化工作,比如在介面上顯示進度條等。
2、正在後台運行:doInBackground(Params...),該回呼函數由後台線程在onPreExecute()方法執行結束後立即調用,重寫該方法就是後台線程將要完成的耗時任務;由於是由後台線程調用,所以我們不能直接在這裡更新UI介面,應該使用publishProgress(Progress...)觸發回調方法onProgressUpdate(Progress...)進行進度更新;任務計算的結果必須由該函數返回,並被傳遞到onPostExecute()中。
3、進度更新:onProgressUpdate(Progress...),在doInBackground()中調用publishProgress()方法更新任務的執行進度,將會在主線程中觸發該方法,一般用於動態地顯示一個進度條。
4、完成背景工作:onPostExecute(Result),當doInBackground()完成後,系統會自動調用onPostExecute()方法,並將doInBackground()的傳回值傳遞給該方法。
5、取消任務:onCancelled (),在調用AsyncTask的cancel()方法時調用。
案例
參考代碼:
public class MainActivity extends ActionBarActivity implements OnClickListener{ private Button startdownload; private ProgressBar probar; private TextView tv; private DownTask task; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startdownload = (Button) findViewById(R.id.startdownload); probar = (ProgressBar) findViewById(R.id.probar); tv = (TextView) findViewById(R.id.tv); startdownload.setOnClickListener(this); } @Override public void onClick(View v) { task = new DownTask();//同一個AsyncTask的execute只能調用一次 task.execute("輸入參數,可為空白");//調用execute後將會回調onPreExecute方法 } class DownTask extends AsyncTask<String, Integer, String>{ @Override//該方法非在主線程運行,可進行耗時操作,不可更新UI介面,其他方法為主線程運行 protected String doInBackground(String... params) {//params為execute輸入的參數 for(int i = 1; i <= 100; i++){ try {//類比下載操作 Thread.sleep(333); publishProgress(i);//傳遞參數i並觸發onProgressUpdate回調方法 } catch (InterruptedException e) { e.printStackTrace(); } } String result = "任務已完成"; return result;//將調用onPostExecute,並將result傳給該回調方法 } @Override protected void onPreExecute() {//該回調方法執行完畢後,將會調用doInBackground probar.setMax(100); probar.setProgress(0); tv.setText("開始下載"); } @Override protected void onPostExecute(String result) {//doInBackground結束後回調該方法,結束。 Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show(); tv.setText("下載完成"); } @Override protected void onProgressUpdate(Integer... values) {//通知UI介面更新 probar.setProgress(values[0]); } }}
布局檔案:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/startdownload" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="開始下載"/> <ProgressBar android:id="@+id/probar" android:layout_width="match_parent" android:layout_height="wrap_content" style="@android:style/Widget.ProgressBar.Horizontal" /> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="20sp" /></LinearLayout>
代碼講解:
1、點擊Button後先執行個體化一個AsyncTask的繼承子類,此時將會建立一個task。接下來變執行execute(params)方法啟動非同步任務。(同一個AsyncTask的執行個體只能執行execute一次,多次執行會拋出錯誤)。
2、在execute()被執行後,將會觸發onPreExecute()回調方法,設定進度條的初始屬性。在onPreExecute()執行完畢後,將會在後台線程開始執行doInBackground(params),該方法接收execute傳入的參數,進行耗時操作,這裡是類比網路檔案下載任務。
3、doInBackground()在後台線程運行中,如果需要與UI主線程互動更新進度,可以調用publishProgress(values)方法,將會觸發位於UI主線程啟動並執行onProgressUpdate(values)的回調方法,代碼中在這裡更新進度條的進度。
4、 當背景工作執行完成後,調用onPostExecute(Result),傳入的參數是doInBackground()中返回的對象。
注意事項
1、不要在同一個AsyncTask執行個體中多次執行execute(),正確的方法是new一個AsyncTask執行一次execute()。
2、耗時任務一定要在doInBackground()中處理,不要在其他回調方法中處理耗時任務以免引起UI主線程的阻塞。
3、不要再doInBackground()中更新UI介面,應該通過publishProgress()調用回調方法更新UI。
4、onCancelled()只能觸發AsyncTask的cancel()方法,並無法取消正線上程池啟動並執行線程任務,但可以通過標誌位來停止線程任務。
5、在不同的android版本中,AsyncTask多任務運行,有些是可以並行有些則是順序執行,不過在高版本Android中,可以通過指定參數設定線程池執行規則。
6、AsyncTask適合處理短時間的操作,長時間的操作,比如下載一個很大的視頻,這就需要你使用自己的線程來下載,不管是斷點下載還是其它的。
作者:enjoy風鈴
出處:http://www.cnblogs.com/net168/
本文著作權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文串連,否則下次不給你轉載了。
android 開發菜鳥 自己用非同步AsyncTask進行網路下載 ,mMyAsynvTaskexecute(mUrl)不執行
AsyncTask在不同的SDK版本,執行策略不一樣的,
Android executes AsyncTask tasks before Android 1.6 and again as of Android 3.0 in sequence by default.
即:Android從3.0開始,AsyncTask為順序執行方式。
這就導致AsyncTask沒有被執行到,是因為其他的AsyncTask還沒執行完。
解決方式:
1、換用Thread+handler或者runonuithread的方式。
2、使用AsyncTask中的
You can tell Android to run it in parallel with the usage of the executeOnExecutor() method, specifyingAsyncTask.THREAD_POOL_EXECUTOR as first parameter.
如:
MyAsynvTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
android AsyncTask的方法在哪幾個線程中調用
這個簡單, 一般要覆蓋三個方法,
1、onPreExecute(),
高負載代碼執行之前調用 ,通常用來顯示一個進度條,在主線程中執行
2、doInBackGround() :
onPreExecute() 執行完後調用,此方法通常就是放高負載代碼的,比如遠程請求,巨大資料載入等,你不用建立線程來封裝此方法 AsyncTask(或子類)會自動在新線程中調用此方法
3、onPostExecute(Result),
在doInBackground完成之後調用,一般是設定結果,取消第一個方法顯示的進度條。
onProgressUpdate() 一般用來更新第一個方法顯示的進度條,什麼下載了50% 51% 。。。
總之,子類化AsyncTask 你不用顧及線程問題, 主線程中直接new AsyncTask的子類,並調用execute就行了,一定要在主線程中調execute。 還有,這些是AsyncTask的生命週期方法,你自己不要調用。