標籤: 程式教學

Android APP 非同步下載圖片實作(背景服務)-APP開發教學

  此篇文章主要是示範教學如何開發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 開發有一定的經驗,如果對於這篇教學有任何問題,可以寄發郵件向我詢問。

程式執行結果

繪圖2

繪圖3

網智數位-軟體開發(軟件開發)

針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰

業務合作、軟體委外開發

業務窗口:allen@netqna.com

聯繫電話:0920-883-870

公司電話:02-55991310

公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓

skype: netqna

line:netqna

微信:netqna

黃先生 Allen

my_qrcode_1519621481105

Xamarin 跨平台程式開發-訊息傳遞 – 軟體開發 教學

   在 Xamarin 開發 IOS、Android、UWP APP時,常常會遇到訊息接受與訊息傳遞,那麼這邊我想簡單示範如何在 Xamarin 實作一個訂閱訊息程式開發技巧。

Android 執行結果畫面

螢幕快照 2018-07-25 下午10.27.43螢幕快照 2018-07-25 下午10.28.49螢幕快照 2018-07-25 下午10.29.10

   Xamarin 透過 MessagingCenter 這個靜態類別來支援訂閱/通知訊息,這個類別主要需要知道三個 Methods ,分別為 Subscribe<TSender, TArgs> 與 Send<TSender, TArgs>、Unsubscribe<TSender, TArgs>。

Subscribe<TSender, TArgs> 方法用來訂閱訊息,當有訊息接受到時,可以通知使用者,例如出現對話訊息、畫面顯示訊息。

Send<TSender, TArgs> 方法用來通知訊息,已告知那些訂閱訊息者。

Unsubscribe<TSender, TArgs> 原先進行訂閱訊息接受,現在進行取消訂閱,後續如果有訊息通知,都不進行接受訊息。

程式範例示範

image

public class MainPageViewModel
     {
         public ObservableCollection<string> Greetings { get; set; }


        public MainPageViewModel ()
         {
             Greetings = new ObservableCollection<string> ();


           MessagingCenter.Subscribe<MainPage> (this, “哈嘍", (sender) => {
                 Greetings.Add(“哈嘍");
             });


            MessagingCenter.Subscribe<MainPage, string> (this, “哈嘍", (sender, arg) => {
                 Greetings.Add(“哈嘍 " + arg);
             });

         }
     }

在上面程式範例 宣告 一個 MainPageViewModel Class,這個作為 ViewModel 用途,在建構式裡實作訂閱服務,這個機制可以減少程式物件耦合度。

下面這行程式碼,主要是用於實作一個名叫 “哈嘍”的訂閱服務  MessagingCenter.Subscribe<MainPage> (this, “哈嘍", (sender) => {  Greetings.Add(“哈嘍"); });


在底下的主程式 MainPage ,實作訂閱通知服務,主要程式邏輯,我透過註解程式說明

public partial class MainPage : ContentPage
     {
         public MainPage()
         {
             InitializeComponent();


            //這邊我綁定一個ViewModel
             BindingContext = new MainPageViewModel();


            //宣告一個按鈕物件,當點選按鈕時,觸發一個發送訊息
             var button1 = new Button { Text = “Say 哈嘍" };
             button1.Clicked += (sender, e) =>
             {
                 MessagingCenter.Send<MainPage>(this, “哈嘍");
             };


            //宣告一個按鈕物件,當點選按鈕時,觸發一個發送訊息,並傳遞內容參數,傳遞的內容是 Allen
             var button2 = new Button { Text = “Say Hi to Allen" };
             button2.Clicked += (sender, e) =>
             {
                 MessagingCenter.Send<MainPage, string>(this, “哈嘍", “Allen");
             };


            //宣告一個按鈕物件,當點選按鈕時,觸發一個取消訂閱服務,並取消對話視窗訊息!
             var button3 = new Button { Text = “取消訂閱服務,並取消對話視窗訊息!" };
             button3.Clicked += (sender, e) =>
             {
                 MessagingCenter.Unsubscribe<MainPage, string>(this, “哈嘍");
                 DisplayAlert(“取消訂閱服務",
                     “This page has stopped listening, so no more alerts; however the ViewModel is still receiving messages.",
                     “OK");
             };


            //訂閱服務(背後的ViewModel還是仍然保持本身實作的訂閱服務),並出現對話視窗訊息
             MessagingCenter.Subscribe<MainPage, string>(this, “哈嘍", (sender, arg) =>
             {
                 DisplayAlert(“訊息已接受", “參數訊息 為 " + arg, “OK");
             });


            //綁定接受到的訂閱訊息
             var listView = new ListView();
             listView.SetBinding(ListView.ItemsSourceProperty, “Greetings");


            //放置物件內容
             Content = new StackLayout
             {
                 Padding = new Thickness(0, 20, 0, 0),
                 Children = { button1, button2, button3, listView }
             };
         }


在 iPhone X 執行畫面如下

螢幕快照 2018-07-25 下午10.20.29螢幕快照 2018-07-25 下午10.20.46

網智數位-軟體開發(軟件開發)
針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna
黃先生 Allen
my_qrcode_1519621481105