次の方法で共有


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

Note

この電子ブックは 2017 年春に発行されたもので、その後は改訂されていません。 このブックには今なお価値のある内容が多く含まれていますが、一部の記載内容は古くなっています。

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

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

MessagingCenter の概要

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

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

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

Figure 4-1: マルチキャストのパブリッシュ/サブスクライブ機能

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

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

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

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

Note

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

eShopOnContainers モバイル アプリでは、別のクラスで発生するアクションに応答して UI を更新するために MessagingCenter が使われます。 そのため、メッセージは 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 メソッドでは、ファイア アンド フォーゲット アプローチを使用して、メッセージとそのペイロード データが発行されます。 そのため、メッセージを受信するために登録されたサブスクライバーがない場合でも、メッセージが送信されます。 この状態では、送信されたメッセージは無視されます。

Note

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 メソッドで指定されたジェネリック型引数によって制御できます。 この例では、サブスクライバーは CatalogViewModel クラスから送信された AddProduct メッセージのみを受信し、そのペイロード データは CatalogItem のインスタンスです。

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

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

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

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

まとめ

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