概述 前面一篇 About Windows 10 SDK Preview Build 17110 中,我們簡單介紹了 Multi-instance UWP Apps,今天結合開發過程詳細講解一下。 在 Windows 10 Version 1803 以前,UWP App 同一時間只能啟動一個實例,而 ...
概述
前面一篇 About Windows 10 SDK Preview Build 17110 中,我們簡單介紹了 Multi-instance UWP Apps,今天結合開發過程詳細講解一下。
在 Windows 10 Version 1803 以前,UWP App 同一時間只能啟動一個實例,而在 1803 開始,UWP App 可以通過開發者的配置選擇來支持多實例。如果一個多實例 UWP App 正在運行,這時一個激活請求發送過來,平臺不會直接激活當前的實例,而是會創建一個新的實例,運行在單獨的進程中。
開發過程
配置多實例支持
多實例特性需要在 Visual Studio 中安裝新的項目模板:Multi-Instance App Project Templates.VSIX, 安裝後,使用 C# 和 C++ 都可以創建項目。
兩個模板會被安裝:
- Multi-Instance UWP app -- 創建一個多實例的 App
- Multi-Instance Redirection UWP app -- 提供一個附加的邏輯,讓用戶可以選擇啟動新實例,或者選擇目前激活的實例。可以想象一下 Office 打開或編輯文件時的場景。
這兩個模板都會在 manifest 文件中添加 SupportsMultipleInstances,其中 desktop4 和 iot2 首碼標誌了項目只支持傳統桌面 Windows 和 IoT 系統。manifest 配置如下,我們只保留了新增的部分:
<Package ... xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2" IgnorableNamespaces="uap mp desktop4 iot2"> ... <Applications> <Application Id="App" ... desktop4:SupportsMultipleInstances="true" iot2:SupportsMultipleInstances="true"> ... </Application> </Applications> ... </Package>
實際運行時,每次點擊 App 的磁貼,都會啟動一個新的實例。如下圖中,App 顯示了啟動的時間,在任務欄和運行視窗可以看到,兩個實例同一時間在運行狀態。
多實例激活重定向
UWP App 對多實例的支持,可以讓同一 App 的多個實例可以同時在運行。它運行開發者自己定義,是每次開啟一個新的實例,還是重定向某個目前激活的應用。舉例來說,讓你想使用 App 編輯一個文件,而這個文件正在 App 中被編輯,這時就不應該再開啟一個新的實例,而是應該重定向當前正在編輯文件的實例。這就會用到 Multi-Instance Redirection UWP app 模板。
Multi-Instance Redirection UWP app 模板和我們上面看到的一樣,對 manifest 文件會做同樣的調整。同時該模板會增加一個 Program.cs 文件,在文件中包含一個 Main() 方法,靠這個方法來實現多實例激活的重定向操作。
我們來重點看看 Program.cs 文件中的 Main() 方法
- activatedArgs 中包含了應用啟動時我們定義的參數,我們根據這些參數,比如 key 來決定多實例的重定向方式;
- AppInstance.RecommendedInstance 系統推薦的實例,如果有,我們可以重定向到這個實例;
- 多實例間唯一性的標識 key 的生成方式,我們可以根據 activatedArgs 來自定義,在預設的示例代碼中,採用了隨機數判斷單雙數的方式;
- FindOrRegisterInstanceForKey(key) 會查詢當前對應 key 的實例,如果沒有則新註冊一個實例;
- 判斷實例是不是新註冊的,如果是則啟動,如果是查詢到的原有實例,則重定向到那個實例;
static void Main(string[] args) { // First, we'll get our activation event args, which are typically richer // than the incoming command-line args. We can use these in our app-defined // logic for generating the key for this instance. IActivatedEventArgs activatedArgs = AppInstance.GetActivatedEventArgs(); // In some scenarios, the platform might indicate a recommended instance. // If so, we can redirect this activation to that instance instead, if we wish. if (AppInstance.RecommendedInstance != null) { AppInstance.RecommendedInstance.RedirectActivationTo(); } else { // Define a key for this instance, based on some app-specific logic. // If the key is always unique, then the app will never redirect. // If the key is always non-unique, then the app will always redirect // to the first instance. In practice, the app should produce a key // that is sometimes unique and sometimes not, depending on its own needs. uint number = CryptographicBuffer.GenerateRandomNumber(); string key = (number % 2 == 0) ? "even" : "odd"; var instance = AppInstance.FindOrRegisterInstanceForKey(key); if (instance.IsCurrentInstance) { // If we successfully registered this instance, we can now just // go ahead and do normal XAML initialization. global::Windows.UI.Xaml.Application.Start((p) => new App()); } else { // Some other instance has registered for this key, so we'll // redirect this activation to that instance instead. instance.RedirectActivationTo(); } } }
對於 key 的構造和判斷,以及判斷後的處理,是多實例重定向的關鍵,我們先看看 FindOrRegisterInstanceForKey(key) 和 IsCurrentInstance 的註釋:
// // 摘要: // 如果另一個實例已註冊該密鑰,使用平臺註冊一個應用實例,或查找現有實例。 // // 參數: // key: // 作為實例密鑰的非空字元串。 // // 返回結果: // 表示已註冊密鑰的第一個應用的應用實例。 public static AppInstance FindOrRegisterInstanceForKey(string key); // // 摘要: // 應用的當前實例是否是該實例定義的特定密鑰的已註冊實例。 // // 返回結果: // 指示當前應用是否為該應用的已註冊實例的布爾值。 public bool IsCurrentInstance { get; }
後臺任務和多實例
關於後臺任務的多實例,官方有以下說明:
- 進程外的後臺任務支持多實例,通常,每個新觸發的結果會獨立在一個後臺任務的實例中;
- 進程內的後臺任務不支持多實例;
- 後臺音樂任務不支持多實例;
- 當應用註冊一個後臺任務時,它通常會首先檢查這個任務是否已經註冊了,如果已註冊,或刪除重新創建它,或維持當前的註冊。這也是多實例應用的典型特點。然而,多實例應用可能會選擇在每個實例的基礎上註冊一個不同的後臺任務名。這對導致多次註冊相同的觸發器,並且觸發器觸發時將會激活多個任務實例;
- 應用服務會為每一個應用服務後臺任務的連接啟動一個單獨的實例,這對多實例應用保持不變,即多實例應用的每個實例都會獲得自己的應用服務後臺任務實例;
其他註意事項
關於多實例應用,官方文檔還提示了一些額外的註意事項:
- 支持多實例應用的 UWP 應用,只能面向傳統桌面系統和 IoT;
- 為避免競爭條件和資源爭奪的問題,多實例應用需要採取措施,分區和同步許可權到對訪問進行設置,應用本地存儲和任何其他資源(如用戶文件,數據存儲等),以在多個實例間完成共用。標準的同步機制包括 mutexes,semaphores,events 等都是可用的;
- 如果應用的 Package.appxmanifest 文件中存在 SupportsMultipleInstances 欄位,那麼他的擴展中不需要再聲明 SupportsMultipleInstances;
- 如果你把 SupportsMultipleInstances 添加到除後臺任務,應用服務之外的的任何其他擴展中,並且托管該擴展的應用沒有在 Package.appxmanifest 中聲明 SupportsMultipleInstances,則會發生模式錯誤;
- 應用可以在 manifest 中使用 ResourceGroup 來把多個後臺任務分組到同一個宿主中, 這和多實例是衝突的,每個活動都會出現在單獨的宿主中。因為一個應用不能同時聲明 SupportsMultipleInstances 和 ResourceGroup;
多實例應用的介紹就到這裡,大家可以結合自己應用的實際場景,更加合理的設置 key 和判斷條件來使用多實例,謝謝!