AsyncTask(背景執行緒)
AsyncTask非同步任務,或稱異步任務,是一個相當常用的類別,是專門用來處理背景任務與UI的類別。
Android 4.0 之後,有明文規定所有的網路行為都不能在主執行緒(Main/UI Thread)執行 所以先放一個Button進去加入以下程式碼
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
for(int i = 0 ; i < 10000000 ; i++){
Log.d("Tag = " , "HI");
}
}
});
就會直接跳出錯誤訊息,因為主執行緒處理過多的問題,如果你不知道哪一條事主執行緒,可以加入這段程式碼
Thread.currentThread().getId()
然後試著印出來,你會發現他會寫1,1就是主執行緒的ID,也是UI Thread。 你可能會想,只是下載一張小圖片應該不會花費太久時間,那我寫在主執行緒就好了。
然後就在onClick裡面寫了以下的程式碼。
try {
URL url = new URL("http://i.imgur.com/Uki7N9T.jpg");
//取得圖片的URL
Bitmap bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
//透過BitmapFactory來下載URL的圖片
imageView.setImageBitmap(bitmap);
//設置圖片到ImageView之中
} catch (IOException e) {
e.printStackTrace();
}
透過BitmapFactory來下載圖片,在使用Try Catch來捕捉一些可能的例外,看起來滿正確的,但是實際執行會發現有一個例外。
android.os.NetworkOnMainThreadException
意思是,你不能在主執行緒做網路的事情
AsyncTask<Params, Progress, Result>
這是基本的架構,使用泛型來定義參數, 泛型意思是,你可以定義任意的資料型態給他。
Params : 參數,你要餵什麼樣的參數給它。
Progress : 進度條,進度條的資料型態要用哪種
Result : 結果,你希望這個背景任務最後會有什麼樣的結果回傳給你。
此外,AsyncTask會有四個步驟。
onPreExecute : 執行前,一些基本設定可以在這邊做。
doInBackground : 執行中,在背景做任務。
onProgressUpdate : 執行中,當你呼叫publishProgress的時候會到這邊,可以告知使用者進度。
onPostExecute : 執行後,最後的結果會在這邊。
參數說明 : 丟入網址(String),進度條用整數(Integer),拿到圖片(Bitmap)
private class GetImage extends AsyncTask<String , Integer , Bitmap>{
@Override
protected void onPreExecute() {
//執行前 設定可以在這邊設定
super.onPreExecute();
}
@Override
protected Bitmap doInBackground(String... params) {
//執行中 在背景做事情
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
//執行中 可以在這邊告知使用者進度
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
//執行後 完成背景任務
super.onPostExecute(bitmap);
}
}
String...是什麼意思,這東西的意思是你可以傳單一一個String, 或者是一個String陣列都可以。
假如你丟一個String進去,你只要取得第一個元素即可。
String urlStr = params[0];
接著在把剛才下載圖片的程式改寫到doInBackground之中。
protected Bitmap doInBackground(String... params) {
//執行中 在背景做事情
String urlStr = params[0];
try {
URL url = new URL(urlStr);
Bitmap bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
imageView.setImageBitmap(bitmap);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
這時候會跳出一個例外
Only the original thread that created a view hierarchy can touch its views.
意思是,你只能在UI Thread去修改UI,因為你現在是在背景,因此你必須回到UI Thread才能對UI做事情(這裡只有doInBackground不是由UI Thread處理)
最後做了一個Dialog去Loading圖片,以下為主程式碼
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
Button button;
ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
imageView = (ImageView) findViewById(R.id.imageView);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new GetImage().execute("http://i.imgur.com/Uki7N9T.jpg");
}
});
}
private class GetImage extends AsyncTask<String, Integer, Bitmap> {
private ProgressDialog progressDialog;
@Override
protected void onPreExecute() {
//執行前 設定可以在這邊設定
super.onPreExecute();
progressDialog=new ProgressDialog(MainActivity.this);
progressDialog.setMessage("Loading");
progressDialog.setTitle("Loading");
progressDialog.setCancelable(false);
progressDialog.setProgressStyle(progressDialog.STYLE_HORIZONTAL);
progressDialog.show();
}
@Override
protected Bitmap doInBackground(String... params) {
//執行中 在背景做事情
String urlStr = params[0];
try {
URL url = new URL(urlStr);
return BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onProgressUpdate(Integer... values) {
//執行中 可以在這邊告知使用者進度
super.onProgressUpdate(values);
progressDialog.setProgress(values[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
//執行後 完成背景任務
super.onPostExecute(bitmap);
progressDialog.dismiss();
imageView.setImageBitmap(bitmap);
}
}
}