Search code examples
androidkotlinandroid-databinding

Data binding: how to solve 'Could not find accessor' issue


I am using data binding library for binding data in recyclerview but I keep getting the below error, I don't understand where is the mistake, I have checked my code several times and also I have tried all the possible solutions from the internet but nothing is working. Kindly help me to resolve this issue. Thank you in advance.

Error

Could not find accessor com.apis.models.MoviesPageModel.movies
at android.databinding.tool.util.L.printMessage(L.java:134)
at android.databinding.tool.util.L.e(L.java:107)

viewmodel

class MoviesPageModel(private val api: ServiceApi): BaseViewModel()  {

private val _moviesList: MutableLiveData<List<Section>> = NotNullMutableLiveData(arrayListOf())
val movies: MutableLiveData<List<Section>> get() = _moviesList

fun getMovies() {
    addToDisposable(api.getMoviesRE().with()
        .doOnSubscribe {}
        .doOnSuccess {}
        .doOnError {}.subscribe({

            if(it != null){
                _moviesList.value = it.Sections
             }
        }, {
        })
    )
   }
 }

xml

<layout 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">

<data>
    <variable
        name="vm" type="com.models.MoviesPageModel" />
</data>

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui_fragments.Movies">

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:moviesAdapter="@{vm.movies}"
        app:viewModel="@{vm}" />

</RelativeLayout>

Solution

  • How to fix the issue?

    Rename either movies variable or getMovies method to something else. I'd rename the method to requestMovies.

    Explanation

    In your case, the issue is the naming of class members of the MoviesPageModel class. Remember that Kotlin produces Java-compatible bytecode, it could be simply understood as - "Kotlin is compiled to Java", sort of.

    When you create in Kotlin a class-level variable named movies with a getter it is compiled to a Java-like method.

    Here is a strip down version of your class. Only declarations are left so we can focus on the problem:

    class MoviesPageModel(): ViewModel()  {
    
        private val _moviesList: MutableLiveData<List<Object>> = MutableLiveData(arrayListOf())
        val movies: MutableLiveData<List<Object>> get() = _moviesList
    
        fun getMovies() {
        }
    }
    

    Kotlin understands movies as a variable and it doesn't give any hints to the outer world that movies is actually just a getter for _moviesList.

    Now, let's look at how compiled to bytecode and decompiled back to Java this code will look like. This is what ends up in your program and runs on a selected machine:

    public final class MoviesPageModel extends ViewModel {
       private final MutableLiveData _moviesList;
    
       @NotNull
       public final MutableLiveData getMovies() {
          return this._moviesList;
       }
    
       public final void getMovies() {
       }
    
       public MoviesPageModel() {
          boolean var1 = false;
          this._moviesList = new MutableLiveData(new ArrayList());
       }
    }
    

    Notice something? There is no movies variable! It was replaced with:

    public final MutableLiveData getMovies()
    

    But here is the problem! You already had getMovies method. This code in Java produces a compile-time error (while Kotlin analogue exists without a warning):

    Two getMovies() methods in a class! Not allowed!

    That is the reason for this error:

    Could not find accessor com.apis.models.MoviesPageModel.movies