databindingソースコード分析一
22712 ワード
前言
Databindingはgoogleが2015年に発表したライブラリで、レイアウトファイルとmodeデータのバインドをサポートしています.最新バージョンでは、双方向バインドがサポートされており、データの更新により、レイアウトに対応するuiインタフェースへの同期がトリガーされ、レイアウトファイルのデータ更新がmodeデータに伝達されます.
1.データ更新はui更新をどのようにトリガーしますか?
2.ui操作はどのようにデータに関連付けられていますか?
3.カスタムviewデータバインドをどのように関連付けますか?
以上の3つの問題を持ってソースコードを分析し、本編ではまず問題1を分析する.
ソースコード
Databindingソースコードは、Android/sdk/extras/android/m 2 repository/com/android/databindingの2つのコンポーネントに分かれています.
1. library
2. adapters
libraryはdatabindingコアのためにコードを構築し、BaseObservable、DataBinderMapper、ViewDataBindingなどを含む.
adaptersは、modeデータにバインドする際によく使用されるuiコントロールのアダプタを定義し、カスタムコントロールであれば参照して記述できます.
mvvmアーキテクチャの気象工学を例に、cloneコードをローカルに、mvvm-databinding分のコードに切り替えます.
このエンジニアリングコードはmvvmフレームワークのアーキテクチャを用い,viewとmodeの関連はdatabindingフレームワークによって実現される.天気テキスト情報を表す文字列mWeatherinfoがWeatherViewModeクラスで定義されています.
この文字はObservableFieldで包装されています.対応するレイアウトファイル
レイアウトファイルで関連付けられたviewとview modeが宣言され、viewに対応するクラスはWeatherFragment
現在のviewとviewmodeは、WeatherFragmentでFragmentWeatherBindingのクラスを介して関連付けられていることがわかります.FragmentWeatherBindingというクラスはコンパイル中に自動的に生成され、ViewDataBindingから継承されます.FragmentWeatherBindingに入り、特定のバインド操作の流れを見てみましょう.
FragmentWeatherBindingコンストラクション関数ではinvalidateAllを呼び出してインタフェースのリフレッシュを行い、requestRebindによってviewの登録、viewとviewmodeの間のバインドが実現されていることがわかりました.
requestRebindでは最終的にexecuteBindingsが呼び出され、executeBindingsでは天気情報viewmodelMWeatherinfoが登録され、対応するidは1です.viewとview modeのidは、後続の操作フローで引き続き使用されます.
setViewmodelメソッドにはviewmodeが登録されており、idは0に対応しています.updateRegistration(1,viewmodelMWeatherinfo)親のViewDataBindingの登録方法を呼び出しました
CREATE_PROPERTY_LISTENERは通常のオブジェクト属性を表すリスナーであり、listとmapに対応するリスナーも対応している.この例ではStringが1つしか定義されていないため、一致するのはCREATE_PROPERTY_LISTENER.
addListenerはListenerを追加するために使用されます
onPropertyChanged Listerコールバック,handleFieldChangeはオブジェクト属性データの変化後のui更新を処理するために使用され,後でトリガフローを詳細に見る.引き続き、リスナーの登録プロセスを分析します.
これでlistner登録が完了します.
後でonPropertyChangedコールバックはuiの更新をどのようにトリガーしますか?ObservableFieldを例にとると、
ObservableFieldのデータ内容を変更するとnotifyChangeがトリガーされ、親BaseObservableの実装が直接呼び出されます
この中のcallbackは何ですか?
callbackはOnPropertyChangedCallbackで、WeakPropertyListenerの中のリスナーです.onPropertyChangedリスナーコールバック、handleFieldChangeはui更新を処理するために使用されます.
まずonFieldChange(mLocalFieldId,object,fieldId)を呼び出し、この例では天気情報が変化するとmLocalFieldIdはmWeatherinfoに対応するid、objectはmWeatherinfo、fieldIdはここでは0となる.
onFieldChange処理では、まずlocalFieldIdの値をチェックし、ここではmWeatherinfoのidが1に対応し、onChangeViewmodelMWeatherinfoの呼び出しを継続する.
天気情報データの変化を検出し、mDirtyFlags|=0 x 2 Lを設定してマークします.
handleFieldChangeメソッドでは、onChangeViewmodelMWeatherinfoがtrueを返すとデータ更新があると判断し、requestRebind()の呼び出しを開始する
最終的にUIスレッドmUIthreadHandlerによって対応するviewにデータをリフレッシュする.FragmentWeatherBinding実装メソッドexecuteBindingsに最終的に呼び出され、UIの再描画が完了します.
まとめ
以上の解析から、databindingが管理view、viewmodeをどのように登録し、viewmodeデータの更新がリスナーコールバックによってUIインタフェースの更新をトリガするかが概略的に分かる.
Databindingはgoogleが2015年に発表したライブラリで、レイアウトファイルとmodeデータのバインドをサポートしています.最新バージョンでは、双方向バインドがサポートされており、データの更新により、レイアウトに対応するuiインタフェースへの同期がトリガーされ、レイアウトファイルのデータ更新がmodeデータに伝達されます.
1.データ更新はui更新をどのようにトリガーしますか?
2.ui操作はどのようにデータに関連付けられていますか?
3.カスタムviewデータバインドをどのように関連付けますか?
以上の3つの問題を持ってソースコードを分析し、本編ではまず問題1を分析する.
ソースコード
Databindingソースコードは、Android/sdk/extras/android/m 2 repository/com/android/databindingの2つのコンポーネントに分かれています.
1. library
2. adapters
libraryはdatabindingコアのためにコードを構築し、BaseObservable、DataBinderMapper、ViewDataBindingなどを含む.
adaptersは、modeデータにバインドする際によく使用されるuiコントロールのアダプタを定義し、カスタムコントロールであれば参照して記述できます.
mvvmアーキテクチャの気象工学を例に、cloneコードをローカルに、mvvm-databinding分のコードに切り替えます.
このエンジニアリングコードはmvvmフレームワークのアーキテクチャを用い,viewとmodeの関連はdatabindingフレームワークによって実現される.天気テキスト情報を表す文字列mWeatherinfoがWeatherViewModeクラスで定義されています.
public class WeatherViewMode extends BaseObservable {
public final ObservableField mWeatherinfo = new ObservableField<>();
この文字はObservableFieldで包装されています.対応するレイアウトファイル
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<variable
name="view"
type="com.android_app_architecture_demo.weather.WeatherFragment" />
<variable
name="viewmodel"
type="com.android_app_architecture_demo.weather.WeatherViewMode" />
data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/weather_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewmodel.mWeatherinfo}" />
FrameLayout>
layout>
レイアウトファイルで関連付けられたviewとview modeが宣言され、viewに対応するクラスはWeatherFragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
mFragmentWeatherBinding = FragmentWeatherBinding.inflate(inflater,container,false);
mFragmentWeatherBinding.setView(this);
mFragmentWeatherBinding.setViewmodel(mViewModel);
View root = mFragmentWeatherBinding.getRoot();
return root;
}
現在のviewとviewmodeは、WeatherFragmentでFragmentWeatherBindingのクラスを介して関連付けられていることがわかります.FragmentWeatherBindingというクラスはコンパイル中に自動的に生成され、ViewDataBindingから継承されます.FragmentWeatherBindingに入り、特定のバインド操作の流れを見てみましょう.
public FragmentWeatherBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
...
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x8L;
}
requestRebind();
}
FragmentWeatherBindingコンストラクション関数ではinvalidateAllを呼び出してインタフェースのリフレッシュを行い、requestRebindによってviewの登録、viewとviewmodeの間のバインドが実現されていることがわかりました.
protected void executeBindings() {
...
updateRegistration(1, viewmodelMWeatherinfo);
...
}
requestRebindでは最終的にexecuteBindingsが呼び出され、executeBindingsでは天気情報viewmodelMWeatherinfoが登録され、対応するidは1です.viewとview modeのidは、後続の操作フローで引き続き使用されます.
public void setViewmodel(com.android_app_architecture_demo.weather.WeatherViewMode Viewmodel) {
// viewmode, id 0
updateRegistration(0, Viewmodel);
this.mViewmodel = Viewmodel;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.viewmodel);
super.requestRebind();
}
setViewmodelメソッドにはviewmodeが登録されており、idは0に対応しています.updateRegistration(1,viewmodelMWeatherinfo)親のViewDataBindingの登録方法を呼び出しました
/**
* @hide
*/
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
CREATE_PROPERTY_LISTENERは通常のオブジェクト属性を表すリスナーであり、listとmapに対応するリスナーも対応している.この例ではStringが1つしか定義されていないため、一致するのはCREATE_PROPERTY_LISTENER.
/**
* Method object extracted out to attach a listener to a bound Observable object.
*/
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener mListener;
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
mListener = new WeakListener(binder, localFieldId, this);
}
@Override
public WeakListener getListener() {
return mListener;
}
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
@Override
public void removeListener(Observable target) {
target.removeOnPropertyChangedCallback(this);
}
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
ViewDataBinding binder = mListener.getBinder();
if (binder == null) {
return;
}
Observable obj = mListener.getTarget();
if (obj != sender) {
return; // notification from the wrong object?
}
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
}
addListenerはListenerを追加するために使用されます
onPropertyChanged Listerコールバック,handleFieldChangeはオブジェクト属性データの変化後のui更新を処理するために使用され,後でトリガフローを詳細に見る.引き続き、リスナーの登録プロセスを分析します.
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
registerTo(localFieldId, observable, listenerCreator);
return true;
}
if (listener.getTarget() == observable) {
return false;//nothing to do, same object
}
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
/**
* @hide
*/
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
}
listener.setTarget(observable);
}
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
mObservable.addListener(mTarget);
}
}
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
これでlistner登録が完了します.
後でonPropertyChangedコールバックはuiの更新をどのようにトリガーしますか?ObservableFieldを例にとると、
public class ObservableField<T> extends BaseObservable implements Serializable {
static final long serialVersionUID = 1L;
private T mValue;
/**
* Set the stored value.
*/
public void set(T value) {
if (value != mValue) {
mValue = value;
notifyChange();
}
}
ObservableFieldのデータ内容を変更するとnotifyChangeがトリガーされ、親BaseObservableの実装が直接呼び出されます
/**
* Notifies listeners that all properties of this instance have changed.
*/
public void notifyChange() {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.notifyCallbacks(this, 0, null);
}
この中のcallbackは何ですか?
@Override
public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
synchronized (this) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
}
mCallbacks.add(callback);
}
callbackはOnPropertyChangedCallbackで、WeakPropertyListenerの中のリスナーです.onPropertyChangedリスナーコールバック、handleFieldChangeはui更新を処理するために使用されます.
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
requestRebind();
}
}
まずonFieldChange(mLocalFieldId,object,fieldId)を呼び出し、この例では天気情報が変化するとmLocalFieldIdはmWeatherinfoに対応するid、objectはmWeatherinfo、fieldIdはここでは0となる.
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeViewmodel((com.android_app_architecture_demo.weather.WeatherViewMode) object, fieldId);
case 1 :
return onChangeViewmodelMWeatherinfo((android.databinding.ObservableField) object, fieldId);
}
return false;
}
onFieldChange処理では、まずlocalFieldIdの値をチェックし、ここではmWeatherinfoのidが1に対応し、onChangeViewmodelMWeatherinfoの呼び出しを継続する.
private boolean onChangeViewmodelMWeatherinfo(android.databinding.ObservableField ViewmodelMWeatherinfo, int fieldId) {
switch (fieldId) {
case BR._all: {
synchronized(this) {
mDirtyFlags |= 0x2L;
}
return true;
}
}
return false;
}
天気情報データの変化を検出し、mDirtyFlags|=0 x 2 Lを設定してマークします.
handleFieldChangeメソッドでは、onChangeViewmodelMWeatherinfoがtrueを返すとデータ更新があると判断し、requestRebind()の呼び出しを開始する
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
最終的にUIスレッドmUIthreadHandlerによって対応するviewにデータをリフレッシュする.FragmentWeatherBinding実装メソッドexecuteBindingsに最終的に呼び出され、UIの再描画が完了します.
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String viewmodelMWeatherinfoGet = null;
com.android_app_architecture_demo.weather.WeatherViewMode viewmodel = mViewmodel;
android.databinding.ObservableField viewmodelMWeatherinfo = null;
if ((dirtyFlags & 0xbL) != 0) {
if (viewmodel != null) {
// read viewmodel.mWeatherinfo
viewmodelMWeatherinfo = viewmodel.mWeatherinfo;
}
updateRegistration(1, viewmodelMWeatherinfo);
if (viewmodelMWeatherinfo != null) {
// read viewmodel.mWeatherinfo.get()
viewmodelMWeatherinfoGet = viewmodelMWeatherinfo.get();
}
}
// batch finished
if ((dirtyFlags & 0xbL) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.weatherInfo, viewmodelMWeatherinfoGet);
}
}
まとめ
以上の解析から、databindingが管理view、viewmodeをどのように登録し、viewmodeデータの更新がリスナーコールバックによってUIインタフェースの更新をトリガするかが概略的に分かる.