2018. 11. 10 @GDG DevFest Seoul
2018. 11. 24 @GDG DevFest Busan
1. The reasons to use Data Binding
2. How Data Binding works (DirtyFlag)
3. UI Modularization
4. Unit Testing
5. as Android Developer
Activity (Fragment, View)
Drawing UI
Receiving User Interactions Service Broadcast Receiver Content Provider
UI
6. as Android Developer
Activity (Fragment, View)
Drawing UI
Receiving User Interactions Service Broadcast Receiver Content Provider
ViewModel
Repository
Presenter
And more…as API Developer
Use Case
Business
UI
Provides
APIs
7. as Android Developer
Activity (Fragment, View)
Drawing UI
Receiving User Interactions Service Broadcast Receiver Content Provider
ViewModel
Repository
Presenter
And more…as API Developer
Use Case
Business
UI
Provides
APIs
Testable Code
8. as Android Developer
Activity (Fragment, View)
Drawing UI
Receiving User Interactions Service Broadcast Receiver Content Provider
ViewModel
Repository
Presenter
And more…as API Developer
Use Case
Business
UI
Provides
APIs
Testable Code
Presenter
10. The past : Model-View-Presenter
fun setTitleText(title: String) {
titleText.text = title
}
fun setTitleVisible(visible: Boolean) {
titleText.visibility = if (visible)
View.VISIBLE else View.GONE
}
fun setButtonEnabled(enable: Boolean) {
titleText.isEnabled = enable
}
Activity Presenter
fun init() {
if (isFirstLogin) {
view.setTitleText("Welcome To DevFest 2018")
view.setTitleVisible(true)
}
}
fun validateDateOfBirth(number: String) {
view.setButtonEnabled(number.length >= 6)
}
.
.
.
11. The past : Model-View-Presenter
Activity Presenter
fun init() {
if (isFirstLogin) {
view.setTitleText("Welcome To DevFest 2018")
view.setTitleVisible(true)
}
}
fun validateDateOfBirth(number: String) {
view.setButtonEnabled(number.length >= 6)
}
Giant Glue Code
if (isFirstLogin) {
view.setTitleText("Welcome To DevFest 2018")
view.setTitleVisible(true)
}
view.setButtonEnabled(number.length >= 6)
fun setTitleText(title: String) {
titleText.text = title
}
fun setTitleVisible(visible: Boolean) {
titleText.visibility = if (visible)
View.VISIBLE else View.GONE
}
fun setButtonEnabled(enable: Boolean) {
titleText.isEnabled = enable
}
.
.
.
12. So far, It s ok
BusinessUI
Activity
Activity
PresenterActivity
Presenter
Presenter
13. So far, It s ok
one to one
but testable
BusinessUI
Activity
Activity
PresenterActivity
Presenter
Presenter
14. So far, It s ok
one to one
but testable
non-reusable
can be solved by
- use case
- abstraction
BusinessUI
Activity
Activity
PresenterActivity
Presenter
Presenter
15. So far, It s ok
one to one
but testable
non-reusable
can be solved by
- use case
- abstraction
hard to maintain
BusinessUI
Activity
Activity
PresenterActivity
Presenter
Presenter
- abstraction
55. android:text=“@={presenter.authCode)}”
TextViewBindingAdapter.setTextWatcher(this.editText,
null, null, null, editTextandroidTextAttrChanged);
BindingAdapter in ActivityMainBinding.java
TextViewBindingAdapter.setTextWatcher
@BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
"android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
public static void setTextWatcher(TextView view, final BeforeTextChanged before,
final OnTextChanged on, final AfterTextChanged after,
final InverseBindingListener textAttrChanged) {
final TextWatcher newValue;
...
newValue = new TextWatcher() {
...
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
if (textAttrChanged != null) {
textAttrChanged.onChange();
60. class MainPresenter {
var year : String
val authCode = MutableLiveData<String>()
init {
year = "2018"
}
fun login() {
// do something!
}
}
Activity Presenterxml Data Binding
62. class MainViewModel {
var year : String
val authCode = MutableLiveData<String>()
init {
year = "2018"
}
fun login() {
// do something!
}
}
Activity ViewModelxml Data Binding
63. TO-BE : MVVM with Data Binding
Activity
view xml
ViewModel
Data Binding
71. ActivityMainBinding.java Layout Processor
public class ActivityMainTestBinding extends android.databinding.ViewDataBinding implements
android.databinding.generated.callback.OnClickListener.Listener {
...
@NonNull
public final android.widget.Button button;
@NonNull
public final android.widget.EditText editText;
@NonNull
public final android.widget.TextView textView;
@NonNull
private final android.support.constraint.ConstraintLayout mboundView0;
@Nullable
private com.github.kimkevin.devfestseoul18.MainViewModel mVm;
@Nullable
private final android.view.View.OnClickListener mCallback1;
Variable
Listener
View
@NonNull
public final android.widget.Button button;
@NonNull
public final android.widget.EditText editText;
@NonNull
public final android.widget.TextView textView;
@Nullable
private com.github.kimkevin.devfestseoul18.MainViewModel mVm;
@Nullable
private final android.view.View.OnClickListener mCallback1;
ActivityMainTestBinding android.databinding.ViewDataBinding
72. DirtyFlag mapping
Dealing with updates
When data is changed, mDirtyFlags is updated
0 0 1
0 1 0
1 0 0
0x1L
0x2L
0x4L
/* flag mapping
flag 0 : vm.authCode
flag 1 : vm
flag 2 : null
flag mapping end*/
ActivityMainBinding.java
73. DirtyFlag mapping
Dealing with updates
When data is changed, mDirtyFlags is updated
0 0 1
0 1 0
1 0 0
0x1L
0x2L
0x4L
/* flag mapping
flag 0 : vm.authCode
flag 1 : vm
flag 2 : initialized
flag mapping end*/
ActivityMainBinding.java
74. /* flag mapping
vm.authCode : 0x1L
vm : 0x2L
initialized : 0x4L
flag mapping end*/
private boolean onChangeVmAuthCode(MutableLiveData<String> VmAuthCode,
int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
mDirtyFlags |= 0x1L;
}
return true;
}
return false;
ActivityMainBinding.java
mDirtyFlag
vm.authCode : 0x1L
75. /* flag mapping
vm.authCode : 0x1L
vm : 0x2L
initialized : 0x4L
flag mapping end*/
ActivityMainBinding.java
mDirtyFlag
vm : 0x2L
public void setVm(@Nullable com...MainViewModel Vm) {
this.mVm = Vm;
synchronized(this) {
mDirtyFlags |= 0x2L;
}
notifyPropertyChanged(BR.vm);
super.requestRebind();
}
76. /* flag mapping
vm.authCode : 0x1L
vm : 0x2L
initialized : 0x4L
flag mapping end*/
ActivityMainBinding.java
mDirtyFlag
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x4L;
}
requestRebind();
}
initialized : 0x4L
77. ActivityMainBinding.java
flag initialized : 0x4L
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm = MainViewModel()
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
this, R.layout.activity_main)
binding.setLifecycleOwner(this)
binding.setVm(viewModel)
}
}
mDirtyFlags = 0x4L
78. ActivityMainBinding.java
flag vm : 0x2L
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm = MainViewModel()
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
this, R.layout.activity_main)
binding.setLifecycleOwner(this)
binding.setVm(viewModel)
}
}
mDirtyFlags = 0x4L | 0x2L = 0x6L
107. TextView
Welcome message
for Devfest Seoul of the year
Requirements
EditText
6 numbers for authentication Button
Enabled for login
with 6 numbers
108. TextView
Welcome message
for Devfest Seoul of the year
Requirements
EditText
6 numbers for authentication Button
Enabled for login
with 6 numbers
Business
UI