完全に自分用メモなので乱文です。
Model-View-ViewModel なアーキテクチャ。 View(画面)は、ViewModel のプロパティ(Android-binding では IObservable)の変更を検知し、コントロール(Android ではウィジェット)の内容を置き換える。(OneWay の場合)
TowWay の場合、ユーザーの操作によってコントロールの値が変化した時、ViewModel のプロパティにその変更を適用する。
超断片的なので、詳しくはおググりください。
例: EditText の Text と、ViewModel の Address プロパティが関連付け(バインド)されていた場合、
Android で MVVM できるライブラリ。
TwoWay の時、EditText にユーザーが文字列を入力して、ViewModel.Address に新しい値を設定する。すると、その変更をまた View が検知して、EditText の値を書き換える処理が動いてしまう(OneWay 側の動きをする)のではないか?
通常の文字列などでは、EditText に同じ値が再設定されるだけなので問題なさげだが、それでも意図しないUI更新は気持ち悪い。
Android-Binding では、この辺りを上手く処理しているように見えるので、実装を見てみる。
Android-Binding で、ViewModel 側のプロパティを "Property"、それをバインドする View 側の定義を "Attribute" という。というか勝手にそう呼ぶことにする。(実体はどちらも IObservable の実装だが、この辺を説明しだすと混乱するので割愛) 前述の例だと、「EditText の Text」が "Attribute"、ViewModel.Address が "Property" になる。
Attribute に Property がバインドされると、Attribute#onBind
が呼び出され、この中で Attribute#Bridge
クラス(これは Observer である)が生成され、Bridge が Property の変更を監視する。と同時に this.subscribe(mBridge)
にて TextViewAttribute 自身の変更も監視する。[github]
[TextViewAttribute]ユーザーが EditText の Text を変更すると、TextViewAttribute#onTextChanged
が呼び出され、さらに notifyChanged
が呼び出される。[github]
[TextViewAttribute]notifyChanged() は Attribute の基底クラスである Observable#notifyChanged()
が呼び出され、続いてオーバーロードである notifyChanged(initiators)
が呼ばれる。 [github]
[TextViewAttribute]initiators
とは、.NET イベントの sender のようなもので、イベント(コールバック)発生元のオブジェクトを示す。ただし、Observable#notifyChanged()
の時点では、initiators はまだ空っぽ。
[TextViewAttribute]notifyChanged(Collection<Object> initiators)
にて、initiators に this
つまり TextViewAttribute 自身を追加して、監視している Observer の onPropertyChanged
を呼び出す。[github]
[TextViewAttribute]TextViewAttribute の監視者は Attribute#Bridge
クラスであり、Bridge#onPropertyChanged(prop, initiators)
が呼び出される。この時点で、prop
は TextViewAttribute 自身、initiators にも TextViewAttribute 自身が格納されている。[github]
[TextViewAttribute]Bridge#onPropertyChanged
の最初の if文 if (prop==mAttribute){
が true となり、mBindedObservable._setObject(prop.get(), initiators);
が呼び出される。mAttribute
は TextViewAttribute 自身であり、mBindedObservable
はバインドした ViewModel の Property である。[github]
[Property]呼び出されたのは、ViewModel の Property(Observable)、つまり Observable#_setObject(newValue, initiators)
である。initiators には依然として TextViewAttbute が1件格納されている。ここから Observable#set(newValue, initiators)
が呼び出される。[github]
[Property]Observable#set(newValue, initiators)
では、doSetValue
で、自身(ViewModel の Property)の値を書き換え、その後、initiators に this、つまり ViewModel の Property を追加して notifyChanged
を呼び出す。この時点で initiators は 2件(TextViewAttribute と ViewModelのProperty) である。[github]
[Property]notifyChanged(Collection<Object> initiators)
にて、initiators に更に this を追加して(この処理に意味があるのかは謎)、監視者である Observer の onPropertyChanged(this, initiators)
を呼び出す。この時点で initiators は 3件(TextViewAttribute と ViewModelのProperty と ViewModelのProperty) [github]
[TextViewAttribute]再び Bridge#onPropertyChanged(prop, initiators) が呼び出される。ここでは prop は ViewModelのProperty である。これにより、最初の if文 は false
となる。[github]
[TextViewAttribute]次の if文 else if (prop==mBindedObservable){
は true
となるが、更にその次の if文 if (initiators.contains(Attribute.this))
は、initiators に TextViewAttribute が含まれている為 false となり、処理は終わる。これにより、次の行にある mAttribute._setObject(prop.get(), initiators);
は呼び出されない、つまり、EditText の Text に値が再設定されることはない。[github]
Android-Binding の仕組みはなんとかわかったー。.NET の XAML はどうやってんのかな?