Windows 10 UWP 5 of N : Application Life Management

  • 1282
  • 0
  • UAP
  • 2021-04-30

Windows 10 UWP 5 of N : Application Life Management

不論甚麼平台應用程式都會有生命週期,那..生命週期之所以重要就是要妥善的利用系統資源如:CPU、RAM、Network、Battery Power...等。

那麼在Windows 10 UWP應用程式的生命週期又有怎樣的變化呢?

LifeCycle

一樣的是總有三種狀態:

  • Running
  • Suspended
  • Not Running

public App()
{
	this.InitializeComponent();
	this.Suspending += OnSuspending;
	this.Resuming += OnResuming;
}

一開始APP都會是在Not Running的狀態! 接著App會先進行InitializeComponent初始化整體App的核心元件,然後會進入到OnLaunched的事件然後才會由Not Running的狀態轉換到 Running的狀態!

接者當User切換到別的APP的時候將會觸發 Suspending的事件然後在Suspending的事件中可以使用如下Code達到儲存資料的功能


private void OnSuspending(object sender, SuspendingEventArgs e)
{
    var deferral = e.SuspendingOperation.GetDeferral();
	//TODO: Save application state and stop any background activity
	deferral.Complete();
}

呼叫GetDeferral來給予多餘時間進行儲存的機制但Deferral只給予大約5秒鐘的時間進行資料儲存!

在Windows 10 UWP上在Foreground execution可以呼叫Extended Execution來稍微延長儲存的時間!

 


private async void OnSuspending(object sender, SuspendingEventArgs e)
{
        var deferral = e.SuspendingOperation.GetDeferral();
        using (var session = new ExtendedExecutionSession())
        {
        session.Reason = ExtendedExecutionReason.SavingData;
        session.Description = "Save data to cloud!";
        session.Revoked += Session_Revoked;
        var result = await session.RequestExtensionAsync();
		if (result == ExtendedExecutionResult.Allowed)
		{
			await SaveDataCloudAsync();
		}
	}
	deferral.Complete();
}

以下是幾點關於ExtendedExecutionSession的重點

屬性 型別 說明
Reason Enum

Unspecified        LocationTracking
SavingData

(必填屬性,目前只有三種)

Description String 說明字串
PercentProgress uInt32 百分比進度

 

方法 說明
RequestExetensionAsync 向系統要求更多時間(約3秒延遲被Suspended)
Dispose 釋放掉Session,建議使用Using包起來就好。

 

事件 說明
Revoked 給予小於1秒的時間清除session或其他資源釋放。

呼叫完成RequestExetensionAsync會給予Allow或著Denied!基本上一律會給予Allow的。

如果要測試Application life cycle在Visual Studio的Toolbar上開啟Debug location就可以找到

image

然後再執行APP的Debug的時候就可以展開Lifecycle的選項。

image

進去Suspend的時候就可以額外要求多餘的等待時間!

image

當然一樣的是如果超過約3秒的時間將會一樣被系統強制關閉!這個機制依然會參考系統的忙碌狀況,在不同硬體裝置上會有所差異。

 


APP如果被Suspended之後可以依然執行的唯一方法就是Background Execution

像是Trigger base Background Tasks就是APP註冊這些Trigger就可以被觸發Trigger被啟動的時候。

例如:

  • Push notification
  • Geofencing
  • BLE device
  • Timer
  • App Services

基本上與之前Universal App 8.1 時期差不多!多了不少的Trigger並將Phone以及Store的Trigger做整合。

原先Windows Store 8.1有以下的Triggers

  • SystemTrigger
  • TimeTrigger
  • MaintenanceTrigger
  • DeviceUseTrigger
  • DeviceServicingTrigger
  • PushNotificationTrigger

在Windows Phone 8.1有以下的Triggers

  • CachedFileUpdaterTrigger
  • DeviceConnectionChangeTrigger
  • GattCharacteristicNotificationTrigger
  • RfcommConnectionTrigger
  • LocationTrigger

然後以下是Windows 10新增的Trigger

  • AppointmentStoreNotificationTrigger
  • ContactStoreNotificationTrigger
  • BluetoothLEAdvertisementWarcherTrigger
  • BluetoothLEAdvertisementPublisherTrigger
  • DeviceWatcherTrigger
  • ActivitySensorTrigger
  • SensorDataThresholdTrigger
  • ToastNotificationHistoryChangedTrigger
  • ToastNotificationActionTrigger
  • ApplicationTrigger
  • SocketActivityTrigger

接者複習一下Trigger八~

假設需要一個TimerTrigger回報背景下載進度可以用以下方式來測試。

先是開啟一專案,在UWP的專案類型只有

  • Blank
  • Class Library
  • Windows Runtime Component
  • Unit Test App
  • Download Windows Universal Tools

image

選擇Runtime專案類型並在Reference加入該Project

image

將Component專案加入APP專案後打開Manifest檔案,建議使用XML的編輯器開啟並加如以下Code

<Extensions>
	<Extension Category="windows.backgroundTasks" EntryPoint="SampleComponet.BackgroundTask.TimerTask">
        <BackgroundTasks>
        	<Task Type="timer"/>
    	</BackgroundTasks>
	</Extension>
</Extensions>

在Application的程式碼區段!EntryPoint就是該Component的NameSpace以及使用的TriggerTask的名稱所組合的路徑。

接著在Application中註冊BackgroundTrigger


private async void RegisterBackgroundTasks(String taskName, Type taskType, IBackgroundTrigger triggerInstance, IBackgroundCondition triggerCondiction = null)
        {
            if (BackgroundExecutionManager.GetAccessStatus() != BackgroundAccessStatus.Denied)
            {
                var backgroundAccessStatus = await BackgroundExecutionManager.RequestAccessAsync();
                foreach (var task in BackgroundTaskRegistration.AllTasks)
                {
                    if (task.Value.Name == taskName)
                    {
                        task.Value.Unregister(true);
                    }
                }
            }

            var backgroundTaskBuilder = new BackgroundTaskBuilder();
            backgroundTaskBuilder.Name = taskName;
            backgroundTaskBuilder.TaskEntryPoint = taskType.FullName;
            backgroundTaskBuilder.SetTrigger(triggerInstance);
            if (triggerCondiction != null)
                backgroundTaskBuilder.AddCondition(triggerCondiction);
            var taskRegistration = backgroundTaskBuilder.Register();
            System.Diagnostics.Debug.WriteLine(string.Format("{0} is registed!", taskRegistration.Name));
        }

一開始先必須將之前的BackgroundTask解除註冊在進行新的Trigger的註冊!

接者在去Component的Trigger實作IBackgroundTask


public sealed class TimerTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            taskInstance.Canceled += TaskInstance_Canceled;
#if DEBUG
            ShowToast("WTF");
#endif
        }

        private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {

        }

        private void ShowToast(String toastMessage)
        {
            var toastTemplate = ToastTemplateType.ToastText01;
            XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
            var toastTextElements = toastXml.GetElementsByTagName("text");
            toastTextElements[0].AppendChild(toastXml.CreateTextNode(toastMessage));
            var toast = new ToastNotification(toastXml);
            ToastNotificationManager.CreateToastNotifier().Show(toast);
        }
    }

BackgroundTaskInstance就是觸發的Task而且支援Deferral的機制處裡一些非同步的方法!

簡單測試BackgroundTask的方式就是一樣在Debug location的Tool中找到ALM的選單,然後切換到BackgroundTask的Class檔就會出現該trigger的選項出現

image

這樣就可以測試BackgroundTask的功能了!

 

Universal Windows App上的Background Task以資源管理分成以下兩類

類型 記憶體使用(MB) 執行時間 CPU 使否會售電池管理影響
Default 16 30 secs 10%
Long-Running ( Opportunistic ) 16 無限 10%

預設的Background Task都擁有25秒的執行時間和5秒的取消時間!當然這些執行的時間會受系統資源多寡影響。

ApplicationTrigger, MaintenanceTrigger, DeviceUseTrigger 都是Opportunistic的BackgroundTask.

然後介紹一下In-Process的Background Task。

預設的BackgroundTask的實際執行exe和Background Task的exe使兩個不同的exe檔案所以是不同的Process而記憶體的使用也是分開的!而在UWP有InProc的Background Task就是將App的exe和BackgroundTask的exe綁在同一個process下並且共享記憶體區塊!

使用InProc的BackgroundTask只需要在Manifest檔案的 Extension做以下變更。


<Extension Category="windows.backgroundTasks" EntryPoint="SampleComponet.BackgroundTask.TimerTask" Executable="App1.exe">
	<BackgroundTasks>
		<Task Type="timer"/>
	</BackgroundTasks>
</Extension>

加上Executable並給上執行的App的exe名稱就可以和Foreground Application在同一Process下執行。

 

***以上Code以及說明都有可能隨著Windows 10 的版本以及Visual Studio 2015版本有所調整!***

 

參考資料 Build 3-626 Windows App Lifecycle ( From Activation & Suspension to Background Execution and Multitasking )

 

下次再分享Windows 10 的新技術拉~