らこらこブログ

唐揚げとアニメとプログラミングが大好きです

android-bindingでMVVMなAndroidアプリ開発 その2

前回はV→(コマンド)→VM→(変更通知)→Vをやりました。
今回はVMからVを操作するということで、「ボタンを押すとEditTextに入力した文字とToastで表示する」というのをやります。

では前回のBindingSampleプロジェクトに手を加えていきます。…と言いたいところですがまず前提の話をします。

「Toastを発行する」というのはActivity(厳密にはContextですが)のインスタンスが必要です。ViewModelからToastを発行しようとしてまずActivityのインスタンス参照を渡すことを考えますが、これはやってはいけないことです。
インスタンスの寿命は一般的にVM > Vです。なのにVMがVの参照を持っているとVはGCされたくてもされないのでメモリリークの原因になります。
そこで必要になるのがActivityのインスタンスを渡さずにVMからActivityのインスタンスメソッドを呼び出す方法になるのですが、すでにMVVMについて知っている人ならばすぐにMessengerパターンを使えばいいとわかると思います。今回はABに実装してあるEventAggregatorを使ってMessengerパターンでActivityを操作してみましょう。

最初に、MainActivityでEventAggregatorのインスタンスを生成します。

次にMainActivityModelにEventAggregatorのsetterを実装します

MainActivityからViewModelに参照を渡してあげます。これで準備ができました

EventAggregatorのインスタンスの生成にActivityのインスタンスを渡していますが生成時に用いるだけで参照は保持されないのでActivityはEventAggregatorに関係なくGCされることができます。
EventAggregatorにはpublishsubscribeという二つの重要なメソッドがあります。まずはMainActivityのほうでsubscribeします。

subscribeでは、キーとなる文字列と、発行された時の処理を登録します。ここでActivityにしかできない処理を行います。
onEventTriggeredの中身は後回しにして次にMainActivityModelのほうでpublishしますが、その前に今回はEditTextの文字列を取得しなければいけないので、まずはそのバインディングをしましょう。まずはxmlから

不要なTextViewを削除し、ButtonのonClickのバインディングキーをhelloからsubmitに変えています。(ついでにtextも。)そしてEditTextを追加してbinding:textで文字列をバインディングしています。
MainActivityModelの方もバインディングし、submitコマンドでeventAggregatorをpublishします。

publishの引数は1つ目がキーとなる文字列、2つ目が発行者のオブジェクト、3つ目がBundleデータとなっています。今回は発行者であるMainActivityのインスタンスを2つ目に渡していますが、これは本当はどんなオブジェクトでもかまいません。2つ目のオブジェクトと3つ目のBundleオブジェクトを使うのは自分で記述したsubscribeのonEventTriggeredのみなのでどちらもnullでもぶっちゃけかまいません。Viewに渡したいデータを渡しましょう。
それではMainActivityに戻り肝心のonEventTriggeredの中身を書きます。

これで実行してみましょう。EditTextに入力した文字列がButtonを押すとToastに表示されるはずです。
android-bindingのEventAggregatorを使えばこのように簡単にMessengerパターンを実装できます。ViewModelにViewの参照をもたせずに操作することができるのでメモリリーク対策ができます。
今回はやりませんでしたがpublishで渡すオブジェクトはなんでもいいのでRunnableなオブジェクトを送り、View側でrunさせることでコールバックのような処理も可能です。

というわけで今回はここまでです。次回はListViewのバインディングについて解説します。