AsyncTask小案例
△關於安卓裏的異步任務
→安卓裏的異步任務,可以當做java裏面的多線程來看待了,在java語言裏,想要啓動多線程就只有兩個方法:繼承Thread類並重寫run()方法,或者實現Runnable接口並且重寫run()方法。
→安卓裏面,想要啓動多個線程可以通過:1,java原來有的兩個方法(Thread類和Runnable接口);2,Handler類提供方法啓動異步任務;3,AsyncTask類也能實現異步任務;4,IntentService類(Service的子類)也能實現異步任務,雖然這個和上述的相比有些勉強。
△爲什麼需要使用AsyncTask
Handler類提供很完整的異步任務機制,但是他的使用相對複雜,對於剛剛入手安卓的人來說,不好理解,AsyncTask的底層也是通過Handler來實現,通過封裝,相對來說比較能讓新手使用,簡單方便。
雖說AsyncTask封裝的很好,能讓你幾乎在不瞭解安卓多線程工作的具體情況之下,照樣使用異步任務,但是如果想理解好異步任務,還得說說安卓的某些線程的知識。理解這些知識以後,對於理解AsyncTask使用的注意事項,也更方便。
→主線程與UI線程:安卓應用在啓動時,就是啓動一個進程,這個進程可能會有若干線程,其中有個就是所謂“主線程”,他又叫做UI線程,首先記住:UI線程==主線程。
→安卓的單線程模型:詳細情況你可以在網上查到,我在這裏說說一定要記住的:
(1)不要阻塞UI線程:別在UI線程裏面進行耗時操作(一般就是不要執行超過五秒鐘的操作),否則將會出現ANR對話框(應用程序沒有響應 )。
(2)只能夠在主線程裏更新UI:所有對UI的更新(比如修改TextView顯示內容,修改ImageView的圖片),都只能在主線程裏進行,因爲安卓對UI的操作是線程不安全的,如果你在非UI線程裏更新UI,會拋異常。
△AsyncTask的簡介
→他是個類,而且還是個抽象類,一個安卓封裝好的用來簡化異步任務的類,只有繼承了並指定泛型參數,覆寫指定的方法後才能使用,使用他,你能在幾乎不瞭解安卓系統多線程的工作過程的前提下,使用安卓多線程的功能。
→類原型:public abstract class AsyncTask<Params, Progress,Result>,三個泛型參數的功能分別是:
(1)Params:啓動後臺任務時的輸入參數,比如你要下載一些文件,那參數是這些文件的URI。
(2)Progress:任務執行過程中更新進度的參數,比如傳給進度條的參數。
(3)Result:任務執行完成之後所返回的結果。
→主要方法:
(1)protected abstract Result doInBackground(Params... params);這是必須要實現的方法,所有異步任務都在裏面這些執行,同時,該方法的所有代碼都運行在非UI線程裏,根據剛纔所說到的單線程的模型,這個方法裏面絕對不能進行任何對UI的操作。
(2)protected void onPreExecute();異步任務執行前的準備操作,該方法在異步任務正式啓動之前被回調(doInBackground()方法執行之前回調),該方法的所有代碼都執行在UI線程裏面,這裏可以進行UI操作,比如,將進度條的進度值設置爲零。但是這裏不能進行耗時操作,因爲是在UI線程。
(3)protected void onProgressUpdate(Progress... values);任務執行過程中更新UI的操作,當你在"doInBackground()"方法裏面調用函數"publishProgress(Progress... values)",實際執行的代碼是"onProgressUpdate(Progress... values)"方法裏面所寫代碼。這個方法裏的所有代碼都運行在UI線程裏,可以進行更新UI操作,但是不能進行耗時操作。
(4)protected void onPostExecute(Result result);當異步任務正常結束後(doInBackground()方法運行結束之後),這個方法將被回調,該方法的所有代碼都運行在UI線程,可以在這進行完成任務相關提示,這裏不能進行耗時操作,注意:如果異步任務是被取消掉的,這個方法不會執行。
AsyncTask類方法小結
(1)異步任務各個方法執行順序:onPreExecute()→doInBackground()→onProgressUpdate()(如果調用過publishProgress方法)→onPostExecute()。
(2)除了doInBackground()方法裏的代碼是運行在子線程裏面的,其與方法的代碼是跑在UI線程裏面。
△AsyncTask使用注意
→一個AsycnTask實例只能被執行一次(只能夠execute()一次),第二次會出現異常。
→剛纔提到所重寫的四個方法,都不能有開發者所調用,都不能有開發者所調用,都不能有開發者所調用,重要的事情說三遍,只能重寫,他們都有系統調用。
△示例代碼
<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"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_print_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="正下載第0個文件"
android:gravity="center_horizontal"
android:textSize="24sp" />
<Button
android:id="@+id/btn_star_asyncTask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="啓動一個異步任務"
android:onClick="asyncTask"/>
<Button
android:id="@+id/btn_pauseAsyncTask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消當前異步任務"
android:onClick="asyncTask"/>
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progressBar" />
</LinearLayout>
再來看看窗體代碼:
package com.example.masynctask;
import android.app.Activity;
import android.os.AsyncTask;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.io.File;
import java.io.InputStream;
public class MainActivity extends Activity {
//準備數組待會傳遞異步任務
private String[] strings = new String[]{"一號文件","二號文件","三號文件","四號文件"};
private TextView textView = null;//這個用來更新窗體
private ProgressBar progressBar = null;//進度
private MAsyncTask mAsyncTask = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView)findViewById(R.id.tv_print_content);
progressBar = (ProgressBar)findViewById(R.id.progressBar);
}
public void asyncTask(View view){
switch(view.getId()) {
case R.id.btn_star_asyncTask:
mAsyncTask = new MAsyncTask();
mAsyncTask.execute(strings);
break;
case R.id.btn_pauseAsyncTask:
mAsyncTask.cancel(true);
break;
}
}
// 第一參數:執行時傳遞進去的類型
// 第二參數:更新那時用的參數
// 第三參數:結束那是傳遞進去"onPostExecute"方法的參數
//AsyncTask裏面的泛型,和上述所介紹哪些方法裏的泛型,是對應的。
private class MAsyncTask extends AsyncTask<String,Integer,Integer>{
@Override
protected Integer doInBackground(String... params) {
Log.i("test", "doInBackGround");
int i = 0;
while(i<params.length) {
publishProgress(i,params.length);
try {
Thread.sleep(4000L);
} catch (Exception e) {
e.printStackTrace();
}
i++;
if (isCancelled()){
break;
}
}
Log.i("test","循環結束");
return params.length;
}
// 這個方法將在子線程開始前執行
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.i("test","onPreExecute");
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// Log.i("test","正在下載第"+values[0]+"個文件");
textView.setText("正下載第"+values[0]+"文件");
progressBar.setProgress( (int)((values[0]+1)/(float)values[1]*100) );
}
// 這個方法將執行在異步任務結束之後
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
Log.i("test", "onPostExecute");
}
@Override
protected void onCancelled(Integer integer) {
super.onCancelled(integer);
Log.i("test", "onCancelled");
}
}