5

I'm learning Data Binding by reading up on the official docs. Everything makes sense expect the possible infinite loops in the two-way binding. As per the official docs on two-way binding:

Be careful not to introduce infinite loops when using two-way data binding. When the user changes an attribute, the method annotated using @InverseBindingAdapter is called, and the value is assigned to the backing property. This, in turn, would call the method annotated using @BindingAdapter, which would trigger another call to the method annotated using @InverseBindingAdapter, and so on.

I understand first part of the statement that the method annotate with @InverseBindingAdapter will be called if the attribute is changed and new value is assigned to the backing property.

But what I don't understand is why @InverseBindingAdapter method is called again when @BindingAdapter method is called in this process and how it leads to infinite loops?

Harry
  • 1,151
  • 11
  • 27

2 Answers2

2

Better late than never I guess :) The reason why an infinite loop can happen is InverseBindingAdapter is a basically an observer for changes. So when a user changed something the onChanged observer in InverseBindingAdapter is triggered and executes some logic. So then BindingAdapter also reacts to the change in the field and updates value again so the change listener in InverseBindingAdapter is triggered again and not we are in a loop.

So here is some visual for that

  1. User -> Types in their name "Joe"
  2. InverseBindingAdapter -> triggered by the update
  3. ObservableField/LiveData -> also updated with 2 way binding and now contains value "Joe"
  4. As ObservableField/LiveData was updated BindingAdapter is triggered to set the new value into the filed.
  5. InverseBindingAdapter -> detected another change in the field and got triggered.
  6. step 3, 4, 5 on repeat....

Check my article on Medium on advanced DataBinding it actually describes this case with the ViewPager and 2 way binding example. (Yes, shameless self-plug disclaimer)

Rainmaker
  • 10,294
  • 9
  • 54
  • 89
2

This issue can be resolved by checking the old and new values before setting the new value to the target view.

Example:

@BindingAdapter("android:text")
fun setText(editText: EditText, value: Int) {
    val newVal = if (value == 0) "" else value.toString()
    val oldVal = editText.text.toString()
    if (oldVal == newVal) {
        return
    }
    editText.setText(newVal)
    if (newVal.isNotEmpty()) {
        editText.setSelection(newVal.length)
    }
}
gopalanrc
  • 254
  • 3
  • 11