0

I am working on a very simple sample app that uses ViewModel, coroutines and databinding.

Here the very simple classes used :

class MainFragment : Fragment() {

    companion object {
        fun newInstance() = MainFragment()
    }

    private val viewModel by viewModels<MainViewModel>()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val binding: MainFragmentBinding =
            DataBindingUtil.inflate(inflater, R.layout.main_fragment, container, false)
        binding.lifecycleOwner = viewLifecycleOwner
        binding.model = viewModel
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel.loadData()
    }
}
class MainViewModel : ViewModel() {

    val txt1 = MutableLiveData<String>()

    val txt2 = Transformations.map(txt1)
    {
        if (it == "test") R.string.yes else R.string.no
    }


    fun loadData() {
        viewModelScope.launch(Dispatchers.IO)
        {
            delay(1000)
            txt1.postValue("test")
        }
    }

}
<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="model"
            type="com.example.myapplication.ui.main.MainViewModel" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.main.MainFragment">

        <TextView
            android:id="@+id/txt1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{model.txt1}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

        <TextView
            android:id="@+id/txt2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{model.txt2}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Unfortunately, this very simple app crashs with the following stacktrace :

    java.lang.RuntimeException: Failed to call observer method
        at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:226)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185)
        at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:37)
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361)
        at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:300)
        at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:339)
        at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:145)
        at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:131)
        at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:51)
        at androidx.fragment.app.Fragment.performStart(Fragment.java:2737)
        at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:355)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1192)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1354)
        at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1432)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1495)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2617)
        at androidx.fragment.app.FragmentManager.dispatchStart(FragmentManager.java:2575)
        at androidx.fragment.app.FragmentController.dispatchStart(FragmentController.java:258)
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:550)
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1391)
        at android.app.Activity.performStart(Activity.java:7157)
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:2937)
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: android.content.res.Resources$NotFoundException: String resource ID #0x0
        at android.content.res.Resources.getText(Resources.java:348)
        at android.widget.TextView.setText(TextView.java:5831)
        at com.example.myapplication.databinding.MainFragmentBindingImpl.executeBindings(MainFragmentBindingImpl.java:168)
        at androidx.databinding.ViewDataBinding.executeBindingsInternal(ViewDataBinding.java:472)
        at androidx.databinding.ViewDataBinding.executePendingBindings(ViewDataBinding.java:444)
        at androidx.databinding.ViewDataBinding$OnStartListener.onStart(ViewDataBinding.java:1685)
        at java.lang.reflect.Method.invoke(Native Method)
        at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:216)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194) 
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185) 
        at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:37) 
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361) 
        at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:300) 
        at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:339) 
        at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:145) 
        at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:131) 
        at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:51) 
        at androidx.fragment.app.Fragment.performStart(Fragment.java:2737) 
        at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:355) 
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1192) 
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1354) 
        at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1432) 
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1495) 
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2617) 
        at androidx.fragment.app.FragmentManager.dispatchStart(FragmentManager.java:2575) 
        at androidx.fragment.app.FragmentController.dispatchStart(FragmentController.java:258) 
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:550) 
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:178) 
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1391) 
        at android.app.Activity.performStart(Activity.java:7157) 
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:2937) 
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180) 
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165) 
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6669) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 

The line 168 of the MainFragmentBindingImpl.java class is the following one:

this.txt2.setText(androidxDatabindingViewDataBindingSafeUnboxModelTxt2GetValue);

So, the issue is the Transformations.map(...). If I remove the binding of the txt2 attribute, everything works correctly.

How can I avoid this bug ?

Thank you for your help.

rolandl
  • 1,769
  • 1
  • 25
  • 48

1 Answers1

0

Set a @BindingAdapter for android:text that takes a resource id. I think it attempts to set the integer initial value of 0, hence the ResourceNotFound. Check for this in your adapter. The value of the string resource id immediately changes from 0 to the correct value.

@JvmStatic
@BindingAdapter("android:text")
fun setTextResource(view: TextView, @StringRes resource: Int) {
    Timber.d("resource id: $resource")
    if (resource == 0) {
        view.text = ""
    } else {
        view.setText(resource)
    }
}