疎結合コンポーネント間の通信

Note

この電子ブックは 2017 年の春に発行され、それ以来更新されていません。 貴重なままの本に多くがありますが、資料の一部は時代遅れです。

発行/サブスクライブ パターンは、パブリッシャーがサブスクライバーと呼ばれる受信者を知らずに、メッセージを送信するメッセージング パターンです。 同様に、サブスクライバーは、パブリッシャーを知らずに特定のメッセージをリッスンします。

.NET のイベントでは、発行/サブスクライブ パターンが実装されます。こうしたイベントは、コントロールやそれを含むページなど、疎結合が不要な場合に、コンポーネント間の通信レイヤーに対して最もシンプルで簡単な方法です。 ただし、パブリッシャーとサブスクライバーの有効期間はオブジェクト参照によって相互に結合され、サブスクライバーの種類にはパブリッシャーの種類への参照が含まれている必要があります。 これにより、特に、静的なオブジェクトまたは有効期間の長いオブジェクトのイベントをサブスクライブする、有効期間が短いオブジェクトがある場合に、メモリ管理の問題が発生する可能性があります。 イベント ハンドラーが削除されていない場合、サブスクライバーは、パブリッシャー内のサブスクライバーへの参照によって保持されます。これにより、サブスクライバーのガベージ コレクションが妨げられるか、または遅延します。

MessagingCenter の概要

クラスは Xamarin.FormsMessagingCenter パブリッシュ/サブスクライブ パターンを実装し、オブジェクトと型の参照によるリンクに不便なコンポーネント間のメッセージ ベースの通信を可能にします。 このメカニズムにより、パブリッシャーとサブスクライバーは相互に参照することなく通信できるため、コンポーネント間の依存関係を減らすことができます。また、コンポーネントを個別に開発およびテストすることもできます。

MessagingCenter クラスでは、マルチキャストの発行/サブスクライブ機能を提供します。 つまり、1 つのメッセージをパブリッシュする複数のパブリッシャーが存在し、同じメッセージをリッスンしている複数のサブスクライバーが存在する可能性があるということです。 図 4-1 は、次の関係を示しています。

マルチキャスト発行/サブスクライブ機能

図 4-1: マルチキャスト発行/サブスクライブ機能

パブリッシャーは MessagingCenter.Send メソッドを使用してメッセージを送信しますが、サブスクライバーは MessagingCenter.Subscribe メソッドを使用してメッセージをリッスンします。 さらに、サブスクライバーは、必要に応じて MessagingCenter.Unsubscribe メソッドを使用して、メッセージ サブスクリプションを解除することもできます。

内部的には、MessagingCenter クラスでは弱参照が使用されます。 つまり、オブジェクトはアクティブに保持されず、ガベージ コレクションの実行が可能になります。 そのため、クラスでメッセージを受信する必要がなくなった場合にのみ、メッセージのサブスクリプションを解除する必要があります。

eShopOnContainers モバイル アプリでは、 クラスを MessagingCenter 使用して疎結合コンポーネント間で通信します。 アプリは、次の 3 つのメッセージを定義します。

  • アイテムが買い物かごに追加されると、CatalogViewModel クラスによって AddProduct メッセージが発行されます。 その代わりに、クラスは BasketViewModel メッセージをサブスクライブし、応答として買い物かご内の項目の数をインクリメントします。 さらに、 BasketViewModel クラスもこのメッセージのサブスクライブを解除します。
  • Filterユーザーがカタログから表示されるアイテムにCatalogViewModelブランドまたは種類のフィルターを適用すると、クラスによってメッセージが発行されます。 その代わりに、クラスは CatalogView メッセージをサブスクライブし、フィルター条件に一致する項目のみが表示されるように UI を更新します。
  • メッセージはChangeTab、 が新しい注文のMainViewModel作成と送信に成功した次の MainViewModel に移動したときにCheckoutViewModel、 クラスによって発行されます。 その代わりに、クラスは MainView メッセージをサブスクライブし、[ マイ プロファイル ] タブがアクティブになるように UI を更新して、ユーザーの注文を表示します。

注意

MessagingCenter クラスでは疎結合クラス間の通信が許可されますが、この問題に対する唯一のアーキテクチャ ソリューションは提供されません。 たとえば、ビュー モデルとビューの間の通信は、バインド エンジンとプロパティ変更通知を介して行うこともできます。 さらに、移動中にデータを渡すことで、2 つのビュー モデル間の通信を実現することもできます。

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 オーバーロードの 1 つを使用してメッセージをサブスクライバーに通知します。 次のコード例は、AddProduct メッセージの発行を示します。

MessagingCenter.Send(this, MessageKeys.AddProduct, catalogItem);

この例では、 メソッドは Send 次の 3 つの引数を指定します。

  • 最初の引数では、送信者クラスを指定します。 送信者クラスは、メッセージを受信するサブスクライバーによって指定される必要があります。
  • 2 番目の引数では、メッセージを指定します。
  • 3 番目の引数は、サブスクライバーに送信されるペイロード データを指定します。 この場合、ペイロード データは インスタンスです CatalogItem

Send メソッドでは、ファイア アンド フォーゲット アプローチを使用して、メッセージとそのペイロード データが発行されます。 そのため、メッセージを受信するために登録されたサブスクライバーがない場合でも、メッセージが送信されます。 この状態では、送信されたメッセージは無視されます。

注意

MessagingCenter.Send メソッドでは、汎用パラメーターを使用して、メッセージの配信方法を制御できます。 これにより、メッセージ ID を共有するものの、異なるペイロード データ型を送信する複数のメッセージを、異なるサブスクライバーが受信することができます。

メッセージのサブスクライブ

サブスクライバーは、MessagingCenter.Subscribe オーバーロードのいずれかを使用して、メッセージを受信するように登録できます。 次のコード例は、eShopOnContainers モバイル アプリがメッセージをサブスクライブして処理する方法を AddProduct 示しています。

MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(  
    this, MessageKeys.AddProduct, async (sender, arg) =>  
{  
    BadgeCount++;  

    await AddCatalogItemAsync(arg);  
});

この例では、 メソッドは Subscribe メッセージをサブスクライブし、メッセージの AddProduct 受信に応答してコールバック デリゲートを実行します。 このコールバック デリゲートは、ラムダ式として指定され、UI を更新するコードを実行します。

ヒント

不変ペイロード データの使用を検討してください。 複数のスレッドが同時に受信データにアクセスする可能性があるため、コールバック デリゲート内からペイロード データを変更しないでください。 このシナリオでは、コンカレンシー エラーを回避するためにペイロード データを変更不可にする必要があります。

サブスクライバーは、パブリッシュされたメッセージのすべてのインスタンスを処理する必要はなく、これは Subscribe メソッドで指定されたジェネリック型引数によって制御できます。 この例では、サブスクライバーは、ペイロード データCatalogItemがインスタンスである クラスからCatalogViewModel送信されたメッセージのみを受信AddProductします。

メッセージからのサブスクライブ解除

サブスクライバーは、受信する必要がなくなったメッセージのサブスクライブを解除することができます。 これは、次のコード例に示すように、MessagingCenter.Unsubscribe オーバーロードのいずれかで実現されます。

MessagingCenter.Unsubscribe<CatalogViewModel, CatalogItem>(this, MessageKeys.AddProduct);

この例では、メソッド構文は Unsubscribe 、メッセージの受信をサブスクライブするときに指定された型引数を AddProduct 反映しています。

まとめ

クラスは Xamarin.FormsMessagingCenter パブリッシュ/サブスクライブ パターンを実装し、オブジェクトと型の参照によるリンクに不便なコンポーネント間のメッセージ ベースの通信を可能にします。 このメカニズムにより、パブリッシャーとサブスクライバーは相互に参照することなく通信できるため、コンポーネント間の依存関係を減らすことができます。また、コンポーネントを個別に開発およびテストすることもできます。