本サイトからスピンオフした姉妹サイトです。iOS2015年9月16日MvvmCrossでのiOS/Androidアプリ開発において、ViewModelからViewにイベントを通知するMessengerパターンの実装方法を紹介する。→MVVMを使った開発では、ViewModelからViewに対する値の反映は主にバインディングを使用する。メッセージダイアログ(アラート)*1の表示や、プラットフォーム固有機能の呼び出しの際は、ViewModelからViewに対してイベントを通知したい場合がある。このようなイベントの通知には、Messengerという機構を使用できる。MvvmCrossでは、Messengerはプラグインとして提供されている。用語として、iOSでは「アラート」が、Androidでは「ダイアログ」が用いられる。今回は以後、ダイアログで共通化する。Messengerの仕組みMessengerは、送信側となるPublisherから、受信側となるSubscriberにメッセージを送る、イベント通知機構のひとつで、Subscriberがメッセージの種類に対して受信先を登録しておくと(図1の1)、Publisherによりメッセージが送られたタイミングで(2)、そのメッセージを登録されている全てのSubscriberに送信する(3)。Messengerの動作まずSubscribeでメッセージを受信するメソッド/デリゲートを登録(1)。その後、ViewModelやServiceからMessageをPublishすると(2)、Subscribeで登録したメソッドが実行される(3)。同じ種類のメッセージに対して複数のメソッドを受信登録でき、複数の受信登録がある種類のメッセージをPublishすると全ての登録先にメッセージが配信されるMvvmCrossのMessengerプラグインでは、PublisherやSubscriberはIoCコンテナーから取得したIMvxMessengerインターフェースのオブジェクトを通じてデリゲートを登録し、メッセージを送る。送るメッセージはクラスで表し、その中に値を持たせることもできる。このようなイベント通知機構自体は各プラットフォーム固有でも存在しており、iOSの場合はNSNotificationCenter、Androidの場合はLocalBroadcastManagerが使用できる。NSNotificationCenterは、アプリの状態変更(起動・停止)や画面回転など、フレームワークやシステムからの情報の送信にも用いられている。Messengerプラグインを使用したメッセージダイアログの表示ここでは、Messengerプラグインを使ってメッセージダイアログを表示する方法を解説する。「Tips:MvvmCrossのプロジェクトをセットアップするには?」の手順に従い、MvvmCrossプロジェクトを作成する。(MvvmCross.HotTuna.Plugin.Messenger)」を各プロジェクトに追加する。次に、PublisherとSubscriber間でやりとりするメッセージを表すクラスを定義する。MessengerSample.Coreプロジェクトを右クリックし、(それにより表示されるコンテキストメニューの)[追加]の中から[新しいフォルダ]を選択してフォルダーを作成する。そのフォルダーにはMessagesと名前を指定する。次にMessagesフォルダーを右クリックして[追加]の中から[新しいファイル]を選択する。開いた[新しいファイル]ダイアログで[空のクラス]を選択し、ShowAlertMessageと名前を付け、ファイルを作成する。そのShowAlertMessage.csファイルの内容を以下のように編集する。メッセージの定義(ShowAlertMessage.cs)メッセージはMvxMessageクラスから派生する必要がある(1)。メッセージクラスには、このメッセージで渡したい情報を、プロパティとして持たせておくこともできる。今回は表示するテキストをAlertMessageプロパティとして持ち(2)、コンストラクターでこの値をセットするようにした(3)。base(sender)という部分を見ると分かるように、MvxMessageクラス自体がコンストラクターにsenderというobject型の引数を持っている。これには送信元となるオブジェクトを指定すればよい。メッセージの送信メッセージの送信側となる部分の実装だが、まずIMvxMessengerオブジェクトをIoCコンテナーから取得し、これを使ってPublishメソッドでメッセージを送信する。FirstViewModel.csファイルを開き、以下のように編集する。メッセージの送信(FirstViewModel)コンストラクターにIMvxMessenger型の引数を持つことでIoCコンテナーからIMvxMessengerオブジェクトを取得し、フィールドに保存しておく(1)。実際にメッセージを送る処理は、コマンドの中に記述した。Task.Delayメソッドを使って5秒処理を待った上でメッセージを送信している(2)。メッセージの送信はIMvxMessenger.Publish<TMessage>メソッドにMvxMessageを渡すことで行える。先ほど作ったShowAlertMessageを渡している。iOSでのメッセージの受信とダイアログ表示iOSでメッセージを受信してダイアログを表示する部分を実装する。MessengerSample.TouchプロジェクトのFirstView.csファイルを開き、以下のように編集する。iOSでのメッセージの受信とダイアログ表示(FirstView.cs)まず、処理を発生させるボタンを用意し(1)、これにコマンドをバインディングする(2)。次に、画面の準備が完了したら呼び出されるViewDidAppearメソッドのタイミングでSubscriberを登録する(3)。Mvx.Resolve<T>メソッドは、実行したその場でIoCコンテナーからオブジェクトを取得できる。取得したIMvxMessengerオブジェクトのSubscribeOnMainThread<T>メソッドで、ダイアログ表示の処理を記述する。Subscribe<T>メソッドでも受信は可能だが、このメソッドで登録した場合はPublisherと同じスレッドで実行されてしまう。UIの操作をメインスレッド以外で行うとクラッシュの要因となるため、受信した箇所でUIを操作するものは、メインスレッドでの実行が保証されるSubscribeOnMainThread<T>メソッドを使用する。どちらのSubscribe系メソッドも、戻り値としてMvxSubscriptionTokenを返す。このサブスクリプショントークンは、Subscriberの登録解除の際に必要となるのでフィールド変数に保持しておく。画面を抜ける前に実行されるViewWillDisappearメソッドで、先ほどのトークンを引数として渡してUnsubscribe<T>メソッドを呼び出すことにより登録を解除する(4)。実際の表示にはiOS標準のUIAlertControllerを使用している。UIAlertController.Createメソッドでメッセージのタイトル、メッセージ内容、表示スタイルを指定してインスタンスを作り、[OK]ボタンをAddActionメソッドで追加する。UIAlertContrllerはその名の通りUIViewControllerの派生クラスなので、PresentViewControllerメソッドで表示する(5)。7まではUIAlertViewを使用していた。8で新規追加され、UIAlertViewは非推奨となった。8未満はUIAlertViewを使用する必要がある。OSのバージョン判定はUIDeviceクラスのCheckSystemVersionメソッドを使うと簡単にできる。7に対応する場合は、以下のようにするとよい。7以前に対応する場合のコード例Androidでのメッセージの受信とダイアログ表示Androidでも同様にダイアログを表示する実装をする。MessengerSample.TouchプロジェクトのFirstView.axmlファイルを開き、以下のように編集する。ダイアログを表示するためのボタンの定義(FirstView.axml)ここでは、ShowAlertCommandを実行するボタンを作成した(1)。次に、FirstView.csファイルを開き、以下のように編集する。Androidでのメッセージの受信とダイアログ表示(FirstView.cs)iOSのときと同様、画面表示時に実行されるOnResumeメソッドでSubscribeを登録し(1)、画面から抜ける際に実行されるOnPauseメソッドでその登録を解除する(2)。実際のダイアログの表示は、DialogFragmentクラスを派生させたクラスを実装する必要がある。Fragmentの外から値を引き渡すときは、基底クラスにあるArgumentsプロパティにBundleをセットするようにするが、この処理をCreateメソッドにまとめておくと、その後で引数が増えたときに、そのメソッド内を修正するのみで済むため、都合がよい。今回はDialogFragmentの派生クラスMessageDialogFragmentに静的なCreateメソッドを定義して実現した(3)。ダイアログの作成はOnCreateDialogメソッドからDialogオブジェクトを返す(4)。実行結果の確認このアプリを実行すると、画面上にボタンが表示され、ボタンをタップして5秒後にメッセージダイアログが表示される。ダイアログの表示ボタンをタップして5秒後にダイアログが表示される。メッセージの内容はViewModel内で指定したものになっていることが分かる。乱用に注意するMessengerなどのイベント通知機構は便利な半面、送ったメッセージをどこで使用しているかを追いづらくなる弊害がある。同じメッセージに対して違う場所から別々の処理が登録されていて、その処理が相互に作用を起こす場合だ。この場合、片方にデバッガーでブレークポイントを指定して動作を見ていても、もう片方にはブレークがかからず、問題の解決に手間を要することになる。Messengerを使う前にプラグインやPCL(ポータブルクラスライブラリ)が用意されているライブラリで代用できないか検討し、使用する際はSubscriberを登録する場所と解除する場所をよく検討した上で利用したい。※以下では、本稿の前後を合わせて5回分(第57回~第61回)のみ表示しています。INDEX]を参照してください。MvvmCrossで画像をバインディングするには?MvvmCrossでのiOS/Androidアプリ開発において、画像のURLをViewへバインディングできるMvxImageViewの使い方を説明する。MvvmCrossで文字列をローカライズ(多言語化)するには?MvvmCrossでのiOS/Androidアプリ開発において、ViewModelの文字列リソースを多言語化してローカライズする方法を解説する。MvvmCrossでViewModelからViewにイベントを通知するには?(Messengerパターン)MvvmCrossでのiOS/Androidアプリ開発において、ViewModelからViewにイベントを通知するMessengerパターンの実装方法を紹介する。XamarinのUIやコードの実行結果を簡単に確認できる「Sketches」を使うには?Xamarin.Formsのレイアウトなどを、ビルドすることなくREPL環境で手軽に確認できるSketchesの使い方を紹介する。Playerの使い方を紹介する。機械学習エンジニアを目指す人~初中級者のための技術情報サイトです。「機械学習&AI」「クラウド&ビッグデータ」「IoTデバイス」のネット記事情報を共有中。開発者のためのSlackチーム、作りました:「機械学習&AI」「クラウド&ビッグデータ」「IoTデバイス」のネット記事情報を共有中。2020最適なビューへの切り替えを推奨します:スマートフォン用表示に。