在偶合程度低的元件之間通訊
注意
本電子書於 2017 年春季出版,此後尚未更新。 這本書中有很多仍然有價值的,但一些材料已經過時。
發行-訂閱模式是一種訊息模式,發行者可以在不知道任何接收者 (稱為訂閱者) 的情況下傳送訊息。 同樣地,訂閱者可以在不知道任何發行者的情況下接聽特定訊息。
.NET 中的事件會實作發佈-訂閱模式,如果不需要鬆散結合,則元件之間的通訊層是最簡單且直接的方法,例如控件和包含它的頁面。 不過,發行者和訂閱者存留期會藉由物件參考彼此藕合,而訂閱者類型必須具有發行者類型的參考。 這可以建立記憶體管理問題,特別是當有短期物件訂閱靜態或長時間存留物件的事件時。 如果未移除事件處理常式,訂閱者將會由發行者中的參考保持運作,這樣可防止或延遲訂閱者的記憶體回收。
MessagingCenter 簡介
類別 Xamarin.FormsMessagingCenter
會實作發佈-訂閱模式,允許元件之間的訊息式通訊,這些元件無法依物件和型別參考連結。 此機制可讓發行者和訂閱者在彼此沒有參考的情況下進行通訊,有助於減少元件之間的相依性,同時還能讓元件獨立開發和測試。
MessagingCenter
類別提供多點傳送的發行-訂閱功能。 這表示可以有多個發行單一訊息的發行者,而且可以有多個訂閱者接聽相同的訊息。 圖 4-1 說明此關聯性:
圖 4-1: 多播發佈-訂閱功能
發行者會使用 MessagingCenter.Send
方法來傳送訊息,而訂閱者會使用 MessagingCenter.Subscribe
方法來接聽訊息。 此外,訂閱者也可以在必要時,使用 MessagingCenter.Unsubscribe
方法,取消訂閱訊息訂閱。
在內部,MessagingCenter
類別會使用弱式參考。 這表示它將不會讓物件持續運作,並將允許它們進行記憶體回收。 因此,應該只有當類別不再希望收到訊息時,才需要取消訂閱訊息。
eShopOnContainers 行動應用程式會 MessagingCenter
使用 類別在鬆散結合的元件之間進行通訊。 應用程式會定義三則訊息:
- 當專案新增至購物籃時,
CatalogViewModel
類別AddProduct
會發佈訊息。 在傳回中,類別BasketViewModel
會訂閱訊息,並在響應中遞增購物籃中的項目數。 此外,類別BasketViewModel
也會取消訂閱此訊息。 - 當使用者將品牌或類型篩選套用至從目錄顯示的專案時,類別
Filter
就會發佈CatalogViewModel
訊息。 在傳回中,類別CatalogView
會訂閱訊息並更新UI,以便只顯示符合篩選準則的專案。 - 當巡覽至
MainViewModel
下列成功建立並提交新訂單時CheckoutViewModel
,類別ChangeTab
就會發佈MainViewModel
訊息。 在傳回中,類別MainView
會訂閱訊息並更新UI,讓 [我的配置檔 ] 索引卷標處於作用中狀態,以顯示使用者的訂單。
注意
雖然 類別 MessagingCenter
允許鬆散結合類別之間的通訊,但它不提供此問題的唯一架構解決方案。 例如,檢視模型與檢視之間的通訊也可以藉由繫結引擎和透過屬性變更通知來實現。 此外,也可以在導覽期間傳遞資料,以實現兩個檢視模型之間的通訊。
在 eShopOnContainers 行動裝置應用程式中, MessagingCenter
用來更新 UI 中的動作,以回應另一個類別中發生的動作。 因此,訊息會在UI線程上發佈,訂閱者會在相同的線程上接收訊息。
提示
執行UI更新時封送處理至UI線程。 如果需要從背景執行緒傳送的訊息來更新 UI,請叫用 Device.BeginInvokeOnMainThread
方法,在訂閱者中的 UI 執行緒上處理訊息。
如需 的詳細資訊 MessagingCenter
,請參閱 MessagingCenter。
定義訊息
MessagingCenter
訊息是用來識別訊息的字串。 下列程式代碼範例顯示 eShopOnContainers 行動應用程式內定義的訊息:
public class MessageKeys
{
// Add product to basket
public const string AddProduct = "AddProduct";
// Filter
public const string Filter = "Filter";
// Change selected Tab programmatically
public const string ChangeTab = "ChangeTab";
}
在此範例中,訊息是使用常數來定義。 這種方法的優點是其提供編譯時間類型安全性和重構支援。
發佈訊息
發行者會使用其中一個 MessagingCenter.Send
多載來通知訊息的訂閱者。 下列程式碼範例示範如何發佈 AddProduct
訊息:
MessagingCenter.Send(this, MessageKeys.AddProduct, catalogItem);
在此範例中 Send
,方法會指定三個自變數:
- 第一個自變數會指定傳送者類別。 寄件人類別必須由任何想要接收訊息的訂閱者指定。
- 第二個引數會指定訊息。
- 第三個自變數會指定要傳送至訂閱者的承載數據。 在此情況下,承載數據是
CatalogItem
實例。
Send
方法將使用射後不理方法來發佈訊息和任何承載資料。 因此,即使沒有任何訂閱者已註冊要接收訊息,也會傳送訊息。 在此情況下,會忽略已傳送的訊息。
注意
方法 MessagingCenter.Send
可以使用泛型參數來控制傳遞訊息的方式。 因此,多個共用訊息身分識別但傳送不同承載數據類型的訊息,可由不同的訂閱者接收。
訂閱訊息
訂閱者可以註冊,以使用其中一個 MessagingCenter.Subscribe
多載來接收訊息。 下列程式代碼範例示範 eShopOnContainers 行動應用程式如何訂閱和處理 AddProduct
訊息:
MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(
this, MessageKeys.AddProduct, async (sender, arg) =>
{
BadgeCount++;
await AddCatalogItemAsync(arg);
});
在此範例中 Subscribe
,方法會 AddProduct
訂閱訊息,並執行回呼委派以回應接收訊息。 這個回撥委派是以 Lambda 運算式指定,可執行更新 UI 的程式碼。
提示
請考慮使用不可變的承載數據。 請勿嘗試從回呼委派內修改承載數據,因為數個線程可以同時存取接收的數據。 在此案例中,承載資料應該不可變,以避免發生並行錯誤。
訂閱者可能不需要處理已發行訊息的每個執行個體,而且這可由 Subscribe
方法中指定的泛型類型引數來控制。 在此範例中,訂閱者只會接收 AddProduct
從 CatalogViewModel
類別傳送的訊息,其承載數據是 CatalogItem
實例。
取消訂閱訊息
若訂閱者不想再接收訊息,則應從訊息中取消訂閱。 這可透過其中一個 MessagingCenter.Unsubscribe
多載來達成,如下列程式碼範例所示:
MessagingCenter.Unsubscribe<CatalogViewModel, CatalogItem>(this, MessageKeys.AddProduct);
在此範例中 Unsubscribe
,方法語法會反映訂閱以接收 AddProduct
訊息時所指定的類型自變數。
摘要
類別 Xamarin.FormsMessagingCenter
會實作發佈-訂閱模式,允許元件之間的訊息式通訊,這些元件無法依物件和型別參考連結。 此機制可讓發行者和訂閱者在彼此沒有參考的情況下進行通訊,有助於減少元件之間的相依性,同時還能讓元件獨立開發和測試。