2

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)
                    }
                }
            }
    }
}
Onik
  • 19,396
  • 14
  • 68
  • 91
HighHill
  • 47
  • 1
  • 11
  • Your error message says that you have a null `MutableLiveData` that you attempt to call `postValue` on, but you haven't included that code. Where are you calling `postValue`? – ianhanniballake Jun 15 '21 at 22:29
  • I'm calling `postValue` in my `ViewModel`. Question edited. – HighHill Jun 15 '21 at 22:34

2 Answers2

1

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.

Onik
  • 19,396
  • 14
  • 68
  • 91
  • That answer didn't help me much. Can you write code example?. – HighHill Jun 16 '21 at 16:15
  • Call `getUsers()` from within _Fragment_, not within `SubCategoryViewModel`. The documentation I referred to has code examples and good explanation. – Onik Jun 16 '21 at 17:06
0

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.

defuex
  • 1
  • 2
  • I currently do not have access to the codes for this project. Maybe someone else with the same error can confirm your solution. – HighHill Aug 07 '22 at 00:38