此篇文章主要是示範教學如何開發Android APP時,利用背景服務來進行比較耗時的工作,使得使用者可以不會覺得APP停頓當掉的錯覺。
背景服務程式(背景執行緒)主要在比較複雜的APP應用程式,它在執行運作時,例如從網路下載檔案、讀取後端資料庫的資料、讀取手機記憶卡的資料時,會需要較多時間,所以系統畫面沒辦法即時回應,甚至出現【應用程式沒有回應】的對話視窗,詢問使用者是否繼續等待或結束APP程式。
而透過在背後另外建立自己的執行緒,去獨立執行比較耗時的工作任務,跟主畫面的執行緒沒有關聯,完全不會影響主畫面的更新與停頓狀態.
這類性質的工作就適合利用 Android API 提供的 AyncTask 執行緒.
下面我在 res/laylout 設計一個簡單的APP主畫
面
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#607D8B">
<!-- 顯示圖片的元件 -->
<ImageView
android:id="@+id/imageview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 開始下載圖片的按鈕元件 -->
<Button
android:id="@+id/download_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_margin="6sp"
android:onClick="clickDownLoadButton"
android:text="背後執行下載圖片"
android:textSize="24sp" />
<LinearLayout
android:id="@+id/op_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/imageview"
android:layout_centerInParent="true"
android:background="@drawable/operation_drawable"
android:orientation="horizontal"
android:padding="6sp"
android:visibility="invisible">
<!-- 顯示前一張圖片 -->
<ImageButton
android:id="@+id/previous"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="clickMoveButton"
android:src="@drawable/ic_keyboard_arrow_left_white_48dp" />
<!-- 顯示下一張圖片 -->
<ImageButton
android:id="@+id/next"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="clickMoveButton"
android:src="@drawable/ic_keyboard_arrow_right_white_48dp" />
</LinearLayout>
</RelativeLayout>
程式解說:
上面主要畫面配置檔,提供一個按鈕,可以下載7張圖片,並將下載的圖片依序可以輪流顯示每張圖片,每張圖片都是透過背後執行緒進行下載,檔執行任務完成後,在將 LinearLayout容器顯示出來,這個容器很簡單放入兩個 ImageButton ,用來切換上一
張與下一張圖片。
現在回到 Java 主程式,宣告一個繼承
AppCompatActivity 的類別,
public class AsyncTaskDemo extends AppCompatActivity {
private ImageView imageview;
private LinearLayout op_panel;
private Bitmap[] images;
// 目前畫面顯示的圖片編號
private int position = 0;
// 顯示下載進度用的進度對話框
private ProgressDialog progressDialog;
程式解說:
在上面主要宣告幾個欄位 Fields , 用來對應之前的配置畫面元件,還有 Bitmap[] 陣列,存放下載的圖片,以及一個進度用的是從 PrgressDialog。
然後重點在 Button 圖片下載的程式碼邏輯:
public void clickDownLoadButton(View view) {
// 這邊可以讓下載按鈕消失
findViewById(R.id.download_btn).setVisibility(View.INVISIBLE);
// 建立下載圖片的AsyncTask物件
final DownloadImageTask downloadImageTask = new DownloadImageTask();
// 顯示 進度對話框
progressDialog = new ProgressDialog(this);
progressDialog.setTitle("Download");
progressDialog.setMessage("Please wait for download...");
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setProgress(0);
progressDialog.setCancelable(false);
// 加入取消工作的按鈕
progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 取消AsyncTask的工作,參數指定為true,表示取消正在執行的工作
// 呼叫這個方法的AsyncTask物件,在結束doInBackground方法後,
// 不會呼叫onPostExecute方法,改為呼叫onCancelled方法
downloadImageTask.cancel(true);
}
});
// 顯示進度對話框
progressDialog.show();
// 啟動AsyncTask物件
downloadImageTask.execute();
}
上面程式主要宣告一個 DownloadImageTask ,
這個程式主要是繼承了 AsyncTask 類別
// 呼叫這個物件的execute方法後就會執行這個方法
// 參數是AsyncTask泛型指定的第一個型態的陣列
@Override
protected Void doInBackground(Void... args) {
// 讀取陣列資源,下載的圖片名稱
Resources res = getResources();
String[] imageNames =
res.getStringArray(R.array.android_icons_array);
// 讀取儲存圖片的網路位置
String baseUrl = getString(R.string.base_url);
// 設定進度對話框的最大進度數量
progressDialog.setMax(imageNames.length);
for (int i = 0; i < imageNames.length; i++) {
// 判斷是否取消工作
if (isCancelled()) {
break;
}
// 下載圖片的完整網址
String url = baseUrl + imageNames[i];
// 從網際網路下載圖片
downloads.add(loadBitmap(url));
// 執行進度處理,參數會傳送給onProgressUpdate方法
publishProgress(i + 1);
}
// 如果已經下載圖片
if (downloads.size() > 0) {
// 建立儲存圖片的Bitmap陣列
images = new Bitmap[downloads.size()];
// 把List物件轉換為陣列
downloads.toArray(images);
}
return null;
}
// 執行進度處理,參數是AsyncTask泛型指定的第二個型態的陣列
// 在doInBackground方法中,呼叫publishProgress方法後,
// 就會執行這個方法並接收放在publishProgress方法中的參數
@Override
protected void onProgressUpdate(Integer... values) {
// 設定進度對話框的進度
progressDialog.setProgress(values[0]);
// 設定ImageView使用的ImageView物件
imageview.setImageBitmap(downloads.get(downloads.size() - 1));
}
// doInBackground方法結束後就會執行這個方法
// 參數是AsyncTask泛型指定的第三個型態的陣列
// 接收doInBackground方法的回傳值
@Override
protected void onPostExecute(Void result) {
// 結束進度對話框
progressDialog.dismiss();
// 設定ImageView使用的ImageView物件
imageview.setImageBitmap(images[0]);
// 顯示上下張圖片的操作按鈕
op_panel.setVisibility(View.VISIBLE);
}
// 呼叫cancel方法後執行這個方法
// 參數是AsyncTask泛型指定的第三個型態的陣列
// 接收doInBackground方法的回傳值
@Override
protected void onCancelled(Void result) {
if (images != null) {
// 設定ImageView使用的ImageView物件
imageview.setImageBitmap(images[0]);
// 顯示上下張圖片的操作按鈕
op_panel.setVisibility(View.VISIBLE);
}
}
完成上面所有程式,就完成一個非同步的下載圖片實作案例,這邊我是預期看這篇實作教學的本身對 Android 開發有一定的經驗,如果對於這篇教學有任何問題,可以寄發郵件向我詢問。
程式執行結果
網智數位-軟體開發(軟件開發)
針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna
黃先生 Allen