LoginSignup
11
11

More than 5 years have passed since last update.

Android-Binding の Binding の実装を深掘りしてみる

Last updated at Posted at 2013-04-16

完全に自分用メモなので乱文です。

MVVM とは

Model-View-ViewModel なアーキテクチャ。
View(画面)は、ViewModel のプロパティ(Android-binding では IObservable)の変更を検知し、コントロール(Android ではウィジェット)の内容を置き換える。(OneWay の場合)

TwoWay の場合、ユーザーの操作によってコントロールの値が変化した時、ViewModel のプロパティにその変更を適用する。

超断片的なので、詳しくはおググりください。

例:
EditText の Text と、ViewModel の Address プロパティが関連付け(バインド)されていた場合、

  • ViewModel.Address がプログラム処理によって変更された時に、EditText.Text が自動的に変更されるのが OneWay。
  • 上記に加え、EditText にユーザーが文字を入力した時に、ViewModel.Address にその値が適用されるのが、TwoWay。

Android-Binding とは

Android で MVVM できるライブラリ。
* Android-Binding

何が知りたいのか?

TwoWay の時、EditText にユーザーが文字列を入力して、ViewModel.Address に新しい値を設定する。すると、その変更をまた View が検知して、EditText の値を書き換える処理が動いてしまう(OneWay 側の動きをする)のではないか?

通常の文字列などでは、EditText に同じ値が再設定されるだけなので問題なさげだが、それでも意図しないUI更新は気持ち悪い。

調べてみる

Android-Binding では、この辺りを上手く処理しているように見えるので、実装を見てみる。

Android-Binding で、ViewModel 側のプロパティを "Property"、それをバインドする View 側の定義を "Attribute" という。というか勝手にそう呼ぶことにする。(実体はどちらも IObservable の実装だが、この辺を説明しだすと混乱するので割愛)
前述の例だと、「EditText の Text」が "Attribute"、ViewModel.Address が "Property" になる。

  1. Attribute に Property がバインドされると、Attribute#onBind が呼び出され、この中で Attribute#Bridge クラス(これは Observer である)が生成され、Bridge が Property の変更を監視する。と同時に this.subscribe(mBridge) にて TextViewAttribute 自身の変更も監視する。[github]

  2. [TextViewAttribute]ユーザーが EditText の Text を変更すると、TextViewAttribute#onTextChanged が呼び出され、さらに notifyChanged が呼び出される。[github]

  3. [TextViewAttribute]notifyChanged() は Attribute の基底クラスである Observable#notifyChanged() が呼び出され、続いてオーバーロードである notifyChanged(initiators) が呼ばれる。 [github]

  4. [TextViewAttribute]initiators とは、.NET イベントの sender のようなもので、イベント(コールバック)発生元のオブジェクトを示す。ただし、Observable#notifyChanged() の時点では、initiators はまだ空っぽ。

  5. [TextViewAttribute]notifyChanged(Collection<Object> initiators) にて、initiators に this つまり TextViewAttribute 自身を追加して、監視している Observer の onPropertyChanged を呼び出す。[github]

  6. [TextViewAttribute]TextViewAttribute の監視者は Attribute#Bridge クラスであり、Bridge#onPropertyChanged(prop, initiators) が呼び出される。この時点で、prop は TextViewAttribute 自身、initiators にも TextViewAttribute 自身が格納されている。[github]

  7. [TextViewAttribute]Bridge#onPropertyChanged の最初の if文 if (prop==mAttribute){ が true となり、mBindedObservable._setObject(prop.get(), initiators); が呼び出される。mAttribute は TextViewAttribute 自身であり、mBindedObservable はバインドした ViewModel の Property である。[github]

  8. [Property]呼び出されたのは、ViewModel の Property(Observable)、つまり Observable#_setObject(newValue, initiators) である。initiators には依然として TextViewAttbute が1件格納されている。ここから Observable#set(newValue, initiators) が呼び出される。[github]

  9. [Property]Observable#set(newValue, initiators) では、doSetValue で、自身(ViewModel の Property)の値を書き換え、その後、initiators に this、つまり ViewModel の Property を追加して notifyChanged を呼び出す。この時点で initiators は 2件(TextViewAttribute と ViewModelのProperty) である。[github]

  10. [Property]notifyChanged(Collection<Object> initiators) にて、initiators に更に this を追加して(この処理に意味があるのかは謎)、監視者である Observer の onPropertyChanged(this, initiators) を呼び出す。この時点で initiators は 3件(TextViewAttribute と ViewModelのProperty と ViewModelのProperty) [github]

  11. [TextViewAttribute]再び Bridge#onPropertyChanged(prop, initiators) が呼び出される。ここでは prop は ViewModelのProperty である。これにより、最初の if文 は false となる。[github]

  12. [TextViewAttribute]次の if文 else if (prop==mBindedObservable){true となるが、更にその次の if文 if (initiators.contains(Attribute.this)) は、initiators に TextViewAttribute が含まれている為 false となり、処理は終わる。これにより、次の行にある mAttribute._setObject(prop.get(), initiators); は呼び出されない、つまり、EditText の Text に値が再設定されることはない。[github]

まとめ

  • Binder の実装は、Bridge が EditText と ViewModelのProperty 両方の変更を監視し、また initiators という仕組みをうまく使うことで、EdtiText が起こした Property の変更は、Attribute が検知しないように工夫されていた。

Android-Binding の仕組みはなんとかわかったー。.NET の XAML はどうやってんのかな?

11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11