Kotlin coroutines: Attempt to invoke virtual method 'void androidx.lifecycle.MutableLiveData.postValue(java.lang.Object)' on a null object reference

Viewed 1078

I have some errors in Retrofit2 and Kotlin Coroutines technologies. I need to dynamically query an info in my service.

For example, the URL is "https://exampleapidomain.com/api/sub_categories/read.php?id=2" I want to change id parameter dynamically.

My service:

interface AltKategoriService {

    @GET("alt_kategoriler/" + Const.READ_URL_PIECE)
    fun getId(@Query("id") id: String?): Call<Resource<AltKategorilerResponse>>

    companion object{
        fun build(): AltKategoriService {
            val interceptor = HttpLoggingInterceptor()
            interceptor.level = HttpLoggingInterceptor.Level.BODY

            val okHttpClient = OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .build()

            val retrofit = Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(Const.BASE_URL)
                .client(okHttpClient)
                .build()

            return retrofit.create(AltKategoriService::class.java)
        }
    }
}

My DataSource file:

class RemoteAltKategorilerDataSource : AltKategorilerDataSource {
    override fun getSubCategories(): Flow<Resource<AltKategorilerResponse>> = flow {
        try {
            emit(Resource.Loading())

            val call = AltKategoriService.build().getId("2").execute()
            if (call.isSuccessful) {

                call.body()?.let {
                    emit(it)
                }
            }

        } catch (ex: Exception) {
            emit(Resource.Error(ex))
            ex.printStackTrace()
        }
    }
}

I get the following error:

Attempt to invoke virtual method 'void androidx.lifecycle.MutableLiveData.postValue(java.lang.Object)' on a null object reference" and then, app crashes.

I'm waiting for your answers and code examples. Thank you!

Edited. My ViewModel:

class SubCategoryViewModel: ViewModel() {
    private val altKategoriRepository = AltKategoriRepository()

    init {
        getUsers()
    }

    var loading: MutableLiveData<Boolean>? = MutableLiveData()
    var altKategoriLiveData = MutableLiveData<AltKategorilerResponse>()
    var error = MutableLiveData<Throwable>()

    fun getUsers() = viewModelScope.launch {
        altKategoriRepository.getSubCategories()
            .asLiveData(viewModelScope.coroutineContext).observeForever {

                when (it.status) {
                    ResourceStatus.LOADING -> {
                        loading?.postValue(true)
                    }

                    ResourceStatus.SUCCESS -> {
                        Log.e("Message", it.data.toString())
                        altKategoriLiveData.postValue(it.data!!)
                        loading?.postValue(false)
                    }

                    ResourceStatus.ERROR -> {
                        error.postValue(it.throwable!!)
                        loading?.postValue(false)
                    }
                }
            }
    }
}
2 Answers

Kotlin class initialisation takes place in the following order:

primary constructor -> init block -> secondary constructor

As no initialisation is done neither for var loading, var altKategoriLiveData nor var error class members of SubCategoryViewModel by the time getUsers() is called in the init { } block, you get the exception resulting in the app crash.

Regarding your implementation of the MVVM pattern, it contradicts to that of the official Android documentation, where a View is supposed to call a corresponding method of ViewModel explicitly or implicitly.

It should also work by just moving the init { } after the variable declarations or if you declare your loading state directly as LOADING. Also i think it's fine to declare it inside the init block, the documentation doesn't refer to that as being at fault if i'm reading correctly. Also it helps doing the call in init to avoid multiple times loading in Activities or Fragments if you only need to do the call once when the viewmodel is created and avoiding multiple calls by e.g: orientation change or other lifecycle dependent things. but please correct me if i'm wrong.

Related