Xamarin.Forms 的背景執行緒在 Android / iOS 背景模式下的執行情境測試
當應用程式一起動的時候,此時這個應用程式將要顯示到螢幕上,就會觸發特定的事件,讓應用程式知道現在應用程式的已經進入到前景模式;而例如,當使用者按下手機上的 Home 按鍵,此時,這個應用程式就會切換到背景模式,當然,也會觸發特定的事件。
如同前面所說的,在 Android 與 iOS 系統下,會觸發的事件與可以觸發的事件項目都不相同,底下的圖片為 Android 作業系統下的 Activity 的生命週期狀態;當 Activity 建立後,就會觸發 OnCreate 事件,啟動之後,就會觸發 OnStart 事件;當應用程式按下了 Home 按鍵,就會觸發 OnPause的事件,使用者選擇切換到該應用程式,要讓該應用程式重新顯示到螢幕上,此時,將會觸發 OnRestart 與 OnStart 事件。
若現在的作業系統為 iOS ,此時對於應用程式生命週期相關會使用到的事件,將會如下圖。當應用程式啟動之後,將會觸發 OnActivated 事件,此時的狀態名稱為 Running 或者 Active;若使用者按下了 Home 按鍵,將會觸發 OnResignActivation 事件,此時,可以稱進入到 Inactive 狀態下,緊接著會在觸發 DidEnterBackground 事件,進入到 Background / Suspended 模式下;現在若使用者選擇要把這個應用程式讓他回到螢幕上,這個時候,就會觸發了 WillEnterForeground 事件,如下面流程圖。
然而,在 Xamarin.Forms 中,也會有一個應用程式生命週期的運作模式與特定的事件,只不過在 Xamarin.Forms App 生命週期內就簡單多了,在 Xamarin.Forms 內只有三種生命週期事件
- OnStart - 會在應用程式啟動時呼叫。
- OnSleep - 會在每次應用程式被移到背景時呼叫。
- OnResume - 會在應用程式被傳送到背景後又再次繼續時呼叫。
這些事件可以從 Xamarin.Forms 專案內的 App 類別中來訂閱。
測試用的專案範例解說
這裡將會使用底下的 Xamarin.Forms 專案,進行 Android / iOS 兩個平台的不同生命週期狀態下的程式碼執行狀態來了解,在這裡,將會設計一個按鈕,當按下這個按鈕之後,將會執行 60 次的迴圈,每次迴圈將會休息兩秒鐘,並且將計數器變數加一,而該計數器屬性將會透過資料綁定的方式,將這個計數器值顯示到螢幕上。此時,也會使用 Console.WriteLine 方法,將現在迴圈的 Index 值顯示到螢幕上,所以,可以從 Visual Studio 2019 的輸出視窗中看到這個程式是否還有繼續在執行中;另外,這些相關執行日誌內容,也會寫到檔案中,以便當這個程式在實體手機上,不透過 Visual Studio 來執行,也可以看到這些 Log 執行過程內容。
這裡是這個應用程式的 XAML 設定內容。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XF5007.Views.MainPage"
Title="背景執行緒與背景模式">
<ScrollView
Orientation="Both"
>
<StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<Label Text="Welcome to Xamarin Forms and Prism!" />
<Label Text="{Binding AppLifeStatusRecord}"
FontSize="{OnPlatform 14, iOS=14}">
</Label>
<Label
Text="{Binding Counter}"
FontSize="30"
TextColor="Red"/>
<Button
Text="開始定時執行"
Command="{Binding StartCommand}"/>
<StackLayout
Orientation="Horizontal"
>
<Button Text="Read" Command="{Binding ReadCommand}"/>
<Button Text="Reset" Command="{Binding ResetCommand}"/>
</StackLayout>
</StackLayout>
</ScrollView>
</ContentPage>
這裡是這個頁面的 ViewModel,定義了三個按鈕的命令行為,在這裡將會透過 AppLifeStatusRecord 類別內的 ReadAsync / WriteAsync 這兩個方法,進行日誌的檔案讀寫需求。
public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
{
public event PropertyChangedEventHandler PropertyChanged;
public DelegateCommand StartCommand { get; set; }
public DelegateCommand ReadCommand { get; set; }
public DelegateCommand ResetCommand { get; set; }
public int Counter { get; set; }
public string AppLifeStatusRecord { get; set; }
private readonly INavigationService navigationService;
public MainPageViewModel(INavigationService navigationService)
{
this.navigationService = navigationService;
ReadCommand = new DelegateCommand(async () =>
{
AppLifeStatusRecord = await new AppLifeStatusService().ReadAsync();
});
ResetCommand = new DelegateCommand(async () =>
{
await new AppLifeStatusService().WriteAsync("", true);
AppLifeStatusRecord = "";
});
StartCommand = new DelegateCommand(async () =>
{
for (int i = 0; i < 60; i++)
{
await Task.Delay(2000);
Counter++;
Console.WriteLine($" === {i} ===");
await new AppLifeStatusService().WriteAsync($" Xamarin.Forms= {i} = > Timer - {DateTime.Now.Minute}:{DateTime.Now.Second}");
}
});
}
public void OnNavigatedFrom(INavigationParameters parameters)
{
}
public void OnNavigatedTo(INavigationParameters parameters)
{
}
public void OnNavigatingTo(INavigationParameters parameters)
{
}
}
如何訂用 Xamarin.Forms 的應用程式生命週期的相關事件
請在 Xamarin.Forms 專案中,找到 App.xaml.cs 節點,從這個節點內的 App 類別中,加入底下三個覆寫方法,所以,當應用程式在前景與背景模式下切換的時候,就會觸發這些事件。
protected override async void OnStart()
{
IsAppInForeground = true;
new AppLifeStatusService().WriteAsync($"Xamarin.Forms>OnStart - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
protected override async void OnSleep()
{
IsAppInForeground = false;
new AppLifeStatusService().WriteAsync($"Xamarin.Forms>OnSleep - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
protected override async void OnResume()
{
IsAppInForeground = true;
new AppLifeStatusService().WriteAsync($"Xamarin.Forms>OnResume - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
如何訂用 Xamarin.Android 的應用程式生命週期的相關事件
請在 Xamarin.Android 專案中,找到 MainActivity.cs 節點,從這個節點內的 MainActivity 類別中,加入底下覆寫方法,所以,當一個 Android 平台下的應用程式在前景與背景模式下切換的時候,就會觸發這些事件。
protected override void OnStart()
{
base.OnStart();
new AppLifeStatusService().WriteAsync($" Android>OnStart - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
protected override void OnResume()
{
base.OnResume();
new AppLifeStatusService().WriteAsync($" Android>OnResume - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
protected override void OnPause()
{
base.OnPause();
new AppLifeStatusService().WriteAsync($" Android>OnPause - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
protected override void OnStop()
{
base.OnStop();
new AppLifeStatusService().WriteAsync($" Android>OnStop - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
protected override void OnRestart()
{
base.OnRestart();
new AppLifeStatusService().WriteAsync($" Android>OnRestart - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
protected override void OnDestroy()
{
base.OnDestroy();
new AppLifeStatusService().WriteAsync($" Android>OnDestroy - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
如何訂用 Xamarin.iOS 的應用程式生命週期的相關事件
請在 Xamarin.iOS 專案中,找到 AppDelegate.cs 節點,從這個節點內的 AppDelegate 類別中,加入底下覆寫方法,所以,當一個 iOS 平台下的應用程式在前景與背景模式下切換的時候,就會觸發這些事件。
public override void OnActivated(UIApplication application)
{
base.OnActivated(application);
new AppLifeStatusService().WriteAsync($" iOS>OnActivated - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
public override void WillEnterForeground(UIApplication application)
{
base.WillEnterForeground(application);
new AppLifeStatusService().WriteAsync($" iOS>WillEnterForeground - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
public override void OnResignActivation(UIApplication application)
{
base.OnResignActivation(application);
new AppLifeStatusService().WriteAsync($" iOS>OnResignActivation - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
public override void DidEnterBackground(UIApplication application)
{
base.DidEnterBackground(application);
new AppLifeStatusService().WriteAsync($" iOS>DidEnterBackground - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
// not guaranteed that this will run
public override void WillTerminate(UIApplication application)
{
base.WillTerminate(application);
new AppLifeStatusService().WriteAsync($" iOS>WillTerminate - {DateTime.Now.Minute}:{DateTime.Now.Second} - 執行緒 {Thread.CurrentThread.ManagedThreadId}");
}
開始進行 Android 平台測試
當把這個測試範例專案在 Android 環境下啟動執行之後,可以從 Visual Studio 2019 的輸出視窗中看到底下的輸出日誌,這部分可以對照上面所提到的 應用程式生命週期 說明內容。
--> Xamarin.Forms>OnStart - 46:53 - 執行緒 1
--> Android>OnStart - 46:53 - 執行緒 1
--> Android>OnResume - 46:53 - 執行緒 1
下面螢幕截圖將會是這個應用程式的執行結果
現在要點選螢幕上的 [開始定時執行] 這個按鈕,當紅色數字跑到 3 的時候,請點選到 Home 按鍵,這個時候應用程式將會被推到不可見的背景模式,請等候一分鐘左右的時間,將這個 App 切換到可見的前景模式。當應用程式回到可見前景模式,此時看到紅色數字已經變成 40 了。
當這個應用程式切換到背景不可見模式下的時候,可以從 Visual Studio 2019 的輸出視窗內,看到還是有不斷的輸出日誌顯示出來,這就表示了,雖然這個 Android 應用程式切換到不可見的背景模式下,可是,他還是會繼續的執行。
=== 0 ===
--> Xamarin.Forms= 0 = > Timer - 43:52
=== 1 ===
--> Xamarin.Forms= 1 = > Timer - 43:54
=== 2 ===
--> Xamarin.Forms= 2 = > Timer - 43:56
--> Android>OnPause - 43:57 - 執行緒 1
--> Xamarin.Forms>OnSleep - 43:57 - 執行緒 1
--> Android>OnStop - 43:58 - 執行緒 1
=== 3 ===
--> Xamarin.Forms= 3 = > Timer - 43:58
=== 4 ===
--> Xamarin.Forms= 4 = > Timer - 44:00
=== 5 ===
--> Xamarin.Forms= 5 = > Timer - 44:20
=== 6 ===
--> Xamarin.Forms= 6 = > Timer - 44:40
=== 7 ===
--> Xamarin.Forms= 7 = > Timer - 44:60
=== 8 ===
--> Xamarin.Forms= 8 = > Timer - 44:80
=== 9 ===
--> Xamarin.Forms= 9 = > Timer - 44:10
=== 10 ===
--> Xamarin.Forms= 10 = > Timer - 44:12
...
=== 33 ===
--> Xamarin.Forms= 33 = > Timer - 44:58
=== 34 ===
--> Xamarin.Forms= 34 = > Timer - 45:0
--> Xamarin.Forms>OnResume - 45:2 - 執行緒 1
--> Android>OnRestart - 45:2 - 執行緒 1
=== 35 ===
--> Xamarin.Forms= 35 = > Timer - 45:2
=== 36 ===
--> Xamarin.Forms= 36 = > Timer - 45:5
=== 37 ===
--> Xamarin.Forms= 37 = > Timer - 45:7
=== 38 ===
--> Xamarin.Forms= 38 = > Timer - 45:9
開始進行 iOS 平台測試
當把這個測試範例專案在 iOS 環境下啟動執行之後,可以從 Visual Studio 2019 的輸出視窗中看到底下的輸出日誌,這部分可以對照上面所提到的 應用程式生命週期 說明內容。
--> Xamarin.Forms>OnStart - 45:55 - 執行緒 1
--> iOS>OnActivated - 45:55 - 執行緒 1
下面螢幕截圖將會是這個應用程式的執行結果
現在要點選螢幕上的 [開始定時執行] 這個按鈕,當紅色數字跑到 3 的時候,請點選到 Home 按鍵,這個時候應用程式將會被推到不可見的背景模式。現在,App 已經在不可見的背景模式下,請觀察 Visual Stuio 2019 的輸出視窗,應該不像是 Android 應用程式,此時輸出視窗內是沒有任何執行日誌輸出到輸出視窗內。
=== 0 ===
--> Xamarin.Forms= 0 = > Timer - 49:35
=== 1 ===
--> Xamarin.Forms= 1 = > Timer - 49:37
=== 2 ===
--> Xamarin.Forms= 2 = > Timer - 49:39
--> Xamarin.Forms>OnSleep - 49:40 - 執行緒 1
--> iOS>OnResignActivation - 49:40 - 執行緒 1
--> iOS>DidEnterBackground - 49:41 - 執行緒 1
=== 3 ===
--> Xamarin.Forms= 3 = > Timer - 49:41
請等候一分鐘左右的時間,將這個 App 切換到可見的前景模式,當應用程式回到可見前景模式,此時看到紅色數字已經變成 6 了,這裡的紅色數字也與 Android 平台下運作不相同。
底下是在 iOS 平台下,當 App 從不可見背景模式切換到可見的模式下,在 Visual Studio 2019 輸出視窗內再度寫入的內容。因此,可以知道,對 iOS App,當應用程式切換到不可見的背景模式下,這個 App 的任何執行緒是沒有且無法做任何事情的。
--> iOS>WillEnterForeground - 54:31 - 執行緒 1
=== 4 ===
--> Xamarin.Forms= 4 = > Timer - 54:31
--> Xamarin.Forms>OnResume - 54:31 - 執行緒 1
--> iOS>OnActivated - 54:31 - 執行緒 1
=== 5 ===
--> Xamarin.Forms= 5 = > Timer - 54:33
=== 6 ===
--> Xamarin.Forms= 6 = > Timer - 54:35
=== 7 ===
--> Xamarin.Forms= 7 = > Timer - 54:37
=== 8 ===
--> Xamarin.Forms= 8 = > Timer - 54:39
=== 9 ===
--> Xamarin.Forms= 9 = > Timer - 54:41
了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式
Xamarin.Forms 快速上手
電子書,請點選 這裡XAML in Xamarin.Forms 基礎篇
電子書,請點選 這裡當在進行 Xamarin.Forms 專案開發的時候,必須要能夠了解 Android 與 iOS 應用程式生命週期 Application Life Cycle 的特性,最為重要的是,這兩個平台上對於生命週期的運作方式是不太相同的。原則上,所有的行動裝置應用程式都會分成前景、背景兩種模式,所謂的前景 Foreground 模式,就是該應用程式顯示在螢幕上,而背景 Background 模式,就是這個應用程式無法顯示在螢幕上,因為現在螢幕需要顯示其他應用程式的內容,關於這部分的詳細介紹,可以參考 Android 活動開發週期 與 iOS 中的背景處理簡介 這兩份文件。