Windows 10, version 1803 (10.0; Build 17134) 開始,UWP app 支援 multiple instances。
怎麽使用它呢?對於既有的 App 需要做那些調整?藉由這篇來介紹讓大家有些概念。
本篇參考 Create a multi-instance Universal Windows App 來介紹。 支援 multi-instance 的做法可透過安裝 Multi-Instance App Project Templates.Vsix 範本來得到兩種類型:
- Multi-instance Redirection UWP App
建立 multi-instance app 並帶有額外邏輯控制是否啓動新的 instance 或從已經啓動的 instances 選擇來使用; 例如:希望一次只有一個 instance 可以編輯相同檔案,當開啓檔案時則從已經開啓的 instances 來使用,不再建立 instance。
- Multi-instance UWP App
每次開啓 app 都是建立新 instance。例如:圖片瀏覽器就非常適合。
讓 App 支援 multi-instancing 幾個步驟:
- Package.appxmanifest 加入宣告:
在 namespace prefix 有新的 tag: desktop4 與 iot2,代表只有 Desktop 與 IoT 設備才支援 multi-instancing。 加入 SupportsMultipleInstances,開發過 Background Task 應該知道 SupportsMultipleInstances 宣告,同樣地在 App Service 的宣告上也支援。參考如下:
<?xml version="1.0" encoding="utf-8"?> <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" Executable="$targetnametoken$.exe" EntryPoint="multi_redirect_instance.App" desktop4:SupportsMultipleInstances="true" iot2:SupportsMultipleInstances="true"> </Application> </Applications> </Package>
- 建立專用的 Program.cs 取代預設的處理機制:
預設 App 被啓動時會自動產生一份 App.g.i.cs,負責啓動前需要處理的事情,如下:
#if !DISABLE_XAML_GENERATED_MAIN public static class Program { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks","")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] static void Main(string[] args) { global::Windows.UI.Xaml.Application.Start((p) => new App()); } } #endif
public static class Program { static void Main(string[] args) { // 抓取啓動時給與的參數,例如:從 cmd 來的參數 IActivatedEventArgs activatedArgs = AppInstance.GetActivatedEventArgs(); // 判斷是否有系統推薦的 instance,有的話則直接使用,沒有再走自己的邏輯 if (AppInstance.RecommendedInstance != null) { AppInstance.RecommendedInstance.RedirectActivationTo(); } else { // 定義 key 為 instance 注冊,是常見的做法,可按照需求做調整 // key 是唯一值,每次的 instance 都是新的 // key 不是唯一值,instance 就可以重覆使用(redirect) uint number = CryptographicBuffer.GenerateRandomNumber(); string key = (number % 2 == 0) ? "even" : "odd"; var instance = AppInstance.FindOrRegisterInstanceForKey(key); if (instance.IsCurrentInstance) { // 如果成功注冊,則代表為新的 instance global::Windows.UI.Xaml.Application.Start((p) => new App()); } else { // 有其他的 instance 注冊相同的 key,則可以 redirect activation 到該 instance instance.RedirectActivationTo(); } } } }
兩個步驟就讓 UWP app 支援 multiple-instancing,下方繼續介紹幾個重要的元素。
[重要元素]
系統會暫存該 app 已經開啓的 instances。
當 app process 在 Main method 被建立時,能利用 AppInstance 選擇是否繼續啓動該 instance 或是導向已經存在的 instance。
另外,shell 能提供推薦的 instance (RecommendedInstance),鼓勵我們選擇是否導向該 instance。
下面兩點要注意:
- AppInstance 目的只在 Main method 中使用,如果到其他地方使用有可能拿到的屬性是 null 或是使用方法是 failed;
- 任何 instances 被回傳之前,必須利用 FindOrRegisterInstanceForKey 來注冊;
- AppInstance 只能在有宣告 SupportsMultipleInstances 的專案,詳細説明:Package manifest schema reference for Windows 10;
type | name | description |
Properties | IsCurrentInstance | app 當前的 instance 是否為已注冊特定 key 的 instance。 |
Key | 當前 instance 的 key。 | |
RecommendedInstance | shell 建議應用程式啟動的應用程式的實例被重定向。 | |
Methods | FindOrRegisterInstanceForKey(String) | 搭配 key 注冊 instance 到系統或是找到已注冊該 key 的 instance。 |
GetActivatedEventArgs() | 取得當前的 IActivatedEventArgs,如同在 OnActivated 事件收到的内容。 | |
GetInstances() | 取得當前 app 已經注冊的 instances。 | |
RedirectActivationTo() | 重新導向 activation 到另一個特定的 instance。 | |
Unregister() | 更新系統的暫存該 instance。 |
Background tasks 與 multi-instancing
- Out-of-proc background tasks(跨處理序背景工作)支援 multi-instancing。一般而言,每個 new trigger results 都會產生 background task 的新 instance。雖然從技術面來説多個 background tasks 會在相同的 host process 被處理,但是系統還是會建立多個 instances。
- In-proc background tasks 不支援 multi-instancing。
- Background audio tasks 不支援 multi-instancing。
- 當 app 注冊 background task 時,通常先檢查該工作是否已經注冊,然後刪除已經存在再重新注冊,或是不執行任何動作。這是一般的 multi-instancing apps 的處理方式。但 multi-instancing app 可能跟著 instance 注冊不同的 background task name,這樣一來,將造成相同的 trigger 有多個注冊,而且 multiple background task instance 將同時被啓動。
- App-service 為每個連綫建的 background task 建立不同的 instance。讓 multi-instance apps 可以擁有自己的 app-service background task。更多介紹可以參考 UWP - 介紹 App Service 與新功能。
- Multi-instancing 支援 UWP apps 運行在 desktop 與 Internet of Things(IoT)。
- 為避免資源互相爭用問題,multi-instancing apps 必須有機制來分割/同步設定,操作 app-local storage 與其他資源。有些標準的做法,例如: mutexes, semaphores, events ... 等。
- 如果 Package.appxmanifest 有宣告 SupportsMultipleInstances,它的 extensions 不需要再宣告一次。
- 除了 background task 或 app service 外,在其他 extensions 宣告了 SupportsMultipleInstances,但主專案沒有宣告則會造成結構描述錯誤。
- App 可利用 ResourceGroup 在 manifest 宣告把 multiple backgorund tasks 分組到同一個主機中。但是這與 multi-instancing 會有衝突,因爲每一個被啓動應該分開的 host,因此,app 不能在他們的 manifest 宣告 SupportsMultipleInstances 與 ResourceGroup。
[注意]
- Multi-instancing 雖然支援 JavaScript applications,但不支援 multi-instancing redirection。因此,AppInstance class 無法使用在此類型的 applications。
- 如果想讓 instances 互相溝通的話,也許可以建立 App Service 讓 main instance 來負責接受與處理,可以參考: lprichar/UwpMessageRelay。
- Mutex Class 專門用來同步存取被保護的資源(例如:檔案),因爲不同 instance 存取時會有互相衝突的問題,藉由呼叫 Mutex.WaitOne() 鎖住 threads 的存取,等到呼叫 Mutex.ReleaseMutex() 再開放給其他 threads 使用。
- 目前 Store 不支援 IoT 裝置下載的 app,所以宣告 iot2:SupportsMultipleInstances 只適用與 Appx 獨立安裝。
======
multiple instances 我覺得最困難的問題在於支援可能互相爭奪,例如:SQLite 的使用就需要特別小心,最簡單就是使用 mutexes。
不過如果過去您開發過 Windows Phone 7.1 的音樂播放機制,對於 two process 的處理一定會非常有感覺。
因爲我自己開發的 App 還沒有需要到讓 app 支援 multiple instances,所以這篇寫的有點簡單。希望對大家還是有些幫助。
References
- Universal Windows Platform - Closing UWP-Win32 Gaps
- Create a multi-instance Universal Windows App
- Windows 10 universal Windows platform (UWP) app lifecycle
- Use app services and extensions
- Convert an app service to run in the same process as its host app
- Create and host an app extension
- Multiple instances support for UWP apps
- Multiple instances support for UWP apps (Part 2): Redirection
- Extend your desktop application with Windows 10 features using the new Visual Studio Application Packaging Project
- UWP - 同一個 App 顯示多個視窗