Windows 10 特刊 2015
第 30 卷,第 11 期
本文章是由機器翻譯。
應用程式生命週期 - 透過背景工作和延伸執行讓應用程式維持運作
由 Shawn Henry |Windows 2015
以前的應用程式生命週期很簡單。當使用者啟動應用程式時,它可以大膽系統上執行的: 耗用資源、 彈出 windows 和一般執行它而不考慮其他人高興。時至今日,事項強悍。在行動優先的世界中,應用程式僅限於定義應用程式生命週期。這個生命週期會指定當應用程式可以執行,但它不能在大多數情況 — 如果應用程式不是使用者進行的作業的目前項目,它有不允許執行。
在大部分情況下,這是很好 — 使用者知道應用程式不是耗用電源或 sapping 效能。針對應用程式,作業系統會強制執行什麼一直都很好的作法,應用程式輸入默認穩定狀態時不在作用中的使用。因為應用程式模型通用 Windows 平台 (UWP) 必須從最低結束,這類裝置電話和 Internet of Things (IoT) 裝置調整到最強大的桌上型電腦和 Xboxes,這一點特別重要。
針對複雜的應用程式現代化的應用程式模型看來似乎很嚴格一開始,但這篇文章將說明,有好幾種應用程式可以展開方塊及執行 (完成工作和傳遞通知) 甚至時不會在前景。
應用程式生命週期
在傳統 Win32 和.NET 桌面開發應用程式通常是在兩種狀態之一: 「 執行中 」 或 「 不在執行,"和大部分所執行的時間。這似乎是明顯但考慮的立即訊息應用程式如 Skype 或像 Spotify 的音樂應用程式: 使用者啟動、 沒有某些動作 (傳送訊息或搜尋音樂) 然後寧靜以及執行其他動作。同時 Skype 坐落於等候訊息中的背景與 Spotify 會持續播放音樂。這樣可避免花費大部分時間以外執行的狀態 (例如 Windows 建置的應用程式上 UWP)、 現代應用程式。
Windows 應用程式永遠都處於這三個狀態之一: 執行中、 已擱置或未執行中所示 [圖 1。當使用者啟動 Windows 應用程式時,例如藉由點選 [開始] 功能表上的磚上應用程式就會啟動並進入執行狀態。只要使用者與應用程式互動時,它會保持在執行中狀態。如果使用者導覽離開應用程式或將它最小化,就會引發已擱置的事件。這是應用程式以序列化它可能已經繼續或重新啟動,例如目前頁面的部分填寫表單資料的應用程式時需要的任何狀態的好機會。當應用程式處於暫停狀態時,其處理序和執行緒會暫停 windows,而且無法執行。當使用者巡覽回到應用程式時、 應用程式執行緒會凍結,而且應用程式繼續執行的狀態。
圖 1] Windows 應用程式生命週期
如果應用程式處於擱置的狀態,但它所使用 (通常是記憶體) 的資源,所需的其他項目,例如執行另一個應用程式、 應用程式會移到未執行狀態。
從應用程式執行觀點來看暫停和未執行狀態之間的差異是應用程式是否允許常駐在記憶體中。應用程式只是暫停時,其執行已凍結,但其狀態資訊的所有存留在記憶體中。應用程式未執行時,它會從記憶體中移除。因為應用程式移動從擱置到未執行時不引發任何事件時,務必要序列化的所有狀態資訊需要從記憶體萬一重新啟動後從未執行的應用程式。
[圖 2 顯示會受到什麼影響資源使用量與應用程式轉換整個生命週期。啟動應用程式時,它開始耗用記憶體,通常達到穩定狀態相當穩定的看法。應用程式暫停時,其記憶體耗用量通常中斷 — 發行緩衝區和使用的資源和 CPU 耗用量歸零時 (由作業系統強制執行)。當應用程式會從移動暫止以未執行時、 記憶體和 CPU 使用前往零、 一次由作業系統強制執行。
[圖 2 應用程式生命週期和資源使用狀況
許多桌面系統上使用大量 RAM 和大型分頁檔通常不會 — 但並非不可能 — 應用程式會從記憶體,但是這項轉換是行動和其他資源受限的裝置上更為普遍。基於這個原因,是測試 UWP 應用程式在各種裝置上。Windows 模擬器隨附於 Visual Studio 可以對這方面十分有幫助它容許開發人員使用的目標裝置與小 512 MB 的記憶體。
擴充的執行
我所描述的應用程式生命週期是有效率 — 如果都不會使用應用程式不耗用資源 — 但它可能會導致應用程式需要無法完成之案例。例如身分證或通訊的應用程式的典型使用案例是登入和同步處理一堆定域機組資料 (連絡人、 摘要、 交談記錄等等)。與基本應用程式生命週期所述,若要下載連絡人、 使用者需要保留應用程式開放且在前景的整個期間! 另一個範例可能是瀏覽或適用性應用程式需要追蹤使用者的位置。當使用者切換到不同的應用程式或將裝置放在其 pocket,這就無法再運作。需要有一項機制可讓應用程式執行較長的時間。
通用的 Windows 平台引進擴充以進行這種情況下執行的概念。有兩種情況下可以使用擴充的執行的地方:
- 在一般的前景執行的應用程式在執行中狀態時任何時間點。
- 應用程式接收到的暫止的事件之後 (OS 是要將應用程式移至暫止狀態) 應用程式的暫止的事件處理常式中。
這兩種情況的程式碼相同,但應用程式的行為就有點以不同的方式在每個。在第一個案例中,應用程式保持在執行中狀態,即使通常會觸發暫止的事件發生 (例如巡覽離開應用程式使用者)。雖然執行延伸模組是作用中應用程式將不會收到的暫止的事件。處置延伸模組之後,應用程式再次適合暫止。
與第二個案例中,如果應用程式轉換為 「 擱置 」 狀態,它會保留在擱置狀態延伸模組的期間。延伸模組過期之後應用程式將進入暫停的狀態無進一步通知。
[圖 3 示範如何使用擴充的執行來擴充 uspending 應用程式的狀態。首先,建立新 ExtendedExecutionSession 並提供原因和描述。這兩個屬性會使用配置正確設定該應用程式 (也就是記憶體、 CPU 和執行時間允許的數量) 的資源和公開給使用者的相關應用程式在背景中的執行中的資訊。如果就會呼叫 Windows 可以不再支援延伸,比方說,如果像前景應用程式或傳入的 VoIP 呼叫另一個高優先順序工作需要資源撤銷事件處理常式攔截程序接著應用程式。最後,應用程式要求延伸模組並,如果成功,便會開始儲存作業。如果拒絕延伸模組,應用程式執行暫止的作業方式還沒有收到等一般的擱置事件延伸模組。
[圖 3 擴充 OnSuspending 處理常式中執行
private async void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
using (var session = new ExtendedExecutionSession())
{
session.Reason = ExtendedExecutionReason.SavingData;
session.Description = "Upload Data";
session.Revoked += session_Revoked;
var result = await session.RequestExtensionAsync();
if (result == ExtendedExecutionResult.Denied)
{
UploadBasicData();
}
// Upload Data
var completionTime = await UploadDataAsync(session);
}
deferral.Complete();
}
[圖 4 顯示這對應用程式生命週期的影響 ; 相比較 [圖 2。當應用程式取得擱置事件時,它會開始釋出或序列化資源。如果應用程式會判斷它需要更多時間和採用延伸模組,它會保留在擱置狀態直到延伸模組已被撤銷。
[圖 4 擴充的執行期間的資源使用狀況
執行延伸模組如案例 2 稍早所述,不需要要求只在暫止的處理常式。它可以要求在任何時間點在應用程式在執行中狀態時。這是適用於當應用程式知道事先它必須如同先前所述的瀏覽應用程式繼續在背景中執行。程式碼非常類似上述範例中,並顯示在 [圖 5。
[圖 5 擴充一般執行期間執行
private async void StartTbTNavigationSession()
{
using (var session = new ExtendedExecutionSession())
{
session.Reason = ExtendedExecutionReason.LocationTracking;
session.Description = "Turn By Turn Navigation";
session.Revoked += session_Revoked;
var result = await session.RequestExtensionAsync();
if (result == ExtendedExecutionResult.Denied
{
ShowUserWarning("Background location tracking not available");
}
// Do Navigation
var completionTime = await DoNavigationSessionAsync(session);
}
}
[圖 6 示範這種情況下的應用程式開發週期。同樣地,它是非常類似。差別在於時已被撤銷執行延伸模組,, 應用程式將可能快速轉換透過暫停狀態成沒有在執行中狀態。這是因為延伸模組在此情況下,通常只會因資源壓力的情況可減輕它必須釋放資源 (亦即從記憶體移除應用程式) 撤銷。
[圖 6 擴充的執行期間的資源使用狀況
背景工作
還有另一個應用程式可以在背景中執行的方法,而且以背景工作。背景工作是在應用程式中實作 IBackgroundTask 介面的個別元件。這些元件可以執行不含重量級的 UI 架構,以及通常在不同的處理序中執行 (雖然它們也可以執行同處理序的主要應用程式可執行檔)。
背景工作及其相關聯的觸發程序引發時執行。即使應用程式不執行觸發程序就會是可以引發並啟動應用程式的系統事件。例如 TimeTrigger 可以產生特定的時間間隔 (例如每隔 30 分鐘)、 應用程式的背景工作會在此情況下啟動時引發觸發程序每隔 30 分鐘。有許多 Windows,包括這些背景觸發程序型別所支援的觸發程序類型: TimeTrigger、 PushNotificationTrigger、 LocationTrigger、 ContactStoreNotificationTrigger、 BluetoothLEAdvertisementWatcherTrigger、 UserPresent、 InternetAvailable 和 PowerStateChange。
使用背景工作是三個步驟的程序: 元件必須建立,然後在應用程式資訊清單中宣告,然後在執行階段註冊。
背景工作通常會實作為與 UI 專案相同的方案中的不同 Windows 執行階段 (WinRT) 元件專案。這可讓背景工作啟動個別的處理序並減少額外負荷元件所需的記憶體中。IBackgroundTask 的簡單實作所示 [圖 7。IBackgroundTask 是定義一個方法的簡單介面執行。這是背景工作的觸發程序引發時呼叫的方法。方法的唯一參數是包含啟用 (例如快顯通知所使用的動作相關聯的推播通知的裝載) 和事件處理常式來處理週期事件,例如取消作業的相關內容的 IBackgroundTaskInstance 物件。Run 方法完成時,會終止背景工作。基於這個理由,如同先前所示的暫止的處理常式是使用延遲物件 (也關閉 IBackgroundTaskInstance 擱置) 如果您的程式碼是非同步的。
[圖 7 A BackgroundTask 實作
public sealed class TimerTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
var deferral = taskInstance.GetDeferral();
taskInstance.Canceled += TaskInstance_Canceled;
await ShowToastAsync("Hello from Background Task");
deferral.Complete();
}
private void TaskInstance_Canceled(IBackgroundTaskInstance sender,
BackgroundTaskCancellationReason reason)
{
// Handle cancellation
deferral.Complete();
}
}
在應用程式資訊清單也必須註冊的背景工作。此註冊會告訴 Windows 的觸發程序類型、 進入點以及可執行的主控件的工作,如下所示:
<Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTasks.TimerTask">
<BackgroundTasks>
<Task Type="timer" />
</BackgroundTasks>
</Extension>
這也可以不使用 Visual Studio 隨附的資訊清單設計工具一頭栽進 XML 即可完成。
最後,工作也必須在執行階段註冊並顯示此做法 [圖 8。在此我使用 BackgroundTaskBuilder 物件來註冊工作就會引發每隔 30 分鐘、 TimeTrigger 如果網際網路功能。這是理想的觸發程序的常見作業更新磚或定期同步處理少量的資料類型。
[圖 8 背景工作註冊
private void RegisterBackgroundTasks()
{
BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
builder.Name = "Background Test Class";
builder.TaskEntryPoint = "BackgroundTaskLibrary.TestClass";
IBackgroundTrigger trigger = new TimeTrigger(30, true);
builder.SetTrigger(trigger);
IBackgroundCondition condition =
new SystemCondition(SystemConditionType.InternetAvailable);
builder.AddCondition(condition);
IBackgroundTaskRegistration task = builder.Register();
task.Progress += new BackgroundTaskProgressEventHandler(task_Progress);
task.Completed += new BackgroundTaskCompletedEventHandler(task_Completed);
}
背景工作的最大優點也可以是其詛咒: 因為背景工作在背景執行的 (當使用者可能會進行任何重要在前景或裝置處於待命狀態),也就是嚴格限制記憶體和他們可以使用的 CPU 時間量。背景工作,例如登錄在 [圖 8 只有 30 秒會執行且不能使用超過 16 MB 的記憶體在裝置上具有 512 MB 的記憶體內部 ; 隨著裝置上的記憶體數量的記憶體上限。在開發和實作背景工作時, 務必納入考量。背景工作應該在發行應用程式之前測試的各種裝置,尤其是低階裝置上。有幾個也請注意其他事項:
- 如果電池保護裝置是可用和作用中 (通常當電池是低於特定的電量臨界值),背景工作將無法執行,直到電池過去的保護裝置的電池臨界值補滿為止。
- 在舊版 Windows 中,「 固定 」 到鎖定之前執行在背景中和在某些情況下,允許它們所需的應用程式有可能是已註冊、 裝置全的背景工作的數目上限。這種不會再 Windows 10 但應用程式必須一律呼叫 BackgroundExecutionManger.RequestAcessAsync 來宣告其想要在背景執行。
更好的方法進行連絡同步處理
有許多背景作業可完成與其中一個擴充的執行或背景工作。通常最好是盡可能使用背景工作 — 它們更可靠、 更有效率。
例如,先前所述的案例 — 第一次啟動應用程式時同步處理資料 — 也可以完成,而且更有效地利用背景工作,在此情況下使用 Windows 10 的新的觸發程序: 應用程式觸發程序。
(如 DeviceUseTrigger) ApplicationTrigger 屬於特殊類別的直接從應用程式的前景部分觸發的觸發程序。這是藉由明確呼叫觸發程序物件上的 [RequestAsync。ApplicationTrigger 是應用程式要在前景,如果應用程式不再出現於前景,而且無法相依於正在緊密結合在一起前景作業應該視繼續在背景啟動作業的情況下特別有用。以下將示範可以在大部分情況下用來擴充執行取代 ApplicationTrigger 工作的範例:
var appTrigger = new ApplicationTrigger();
var backgroundTaskBuilder = new BackgroundTaskBuilder();
backgroundTaskBuilder.Name = "App Task";
backgroundTaskBuilder.TaskEntryPoint = typeof(AppTask).FullName;
backgroundTaskBuilder.SetTrigger(appTrigger);
backgroundTaskBuilder.Register();
var result = await appTrigger.RequestAsync();
總結
這篇文章提供 Windows 10 中的應用程式生命週期和背景執行的概觀和導入了幾個新的應用程式可用來在背景執行的機制: 擴充執行和背景工作。背景工作會比較好的選擇低階和記憶體限制的裝置 (例如手機) 而擴充的執行更適用於高階裝置 (例如桌上型電腦)。
Shawn Henry是通用的 Windows 平台小組的資深專案經理。在 Twitter 上達到他: @shawnhenry。
感謝以下的微軟技術專家對本文的審閱: Anis Mohammed Khaja Mohideen 和 Abdul Hadi Sheikh
Abdul Hadi Sheikh 是通用的 Windows 平台小組的軟體開發人員
Anis Mohammed Khaja Mohideen 是通用的 Windows 平台小組的資深軟體開發人員