My ViewModel class
@HiltViewModel
class RandomRecipeViewModel @Inject constructor(
val repo: RecipesRepo,
val app: MyApplication
) : AndroidViewModel(app) {
init {
getRandomRecipes()
}
val randomRecipe = MutableLiveData<Resource<RandomRecipe>>()
var randomRecipeList: RandomRecipe? = null
fun getRandomRecipes() {
viewModelScope.launch {
try {
**//Exception in the below line**
randomRecipe.postValue(Resource.Loading())
val response = repo.getRandomRecipes()
randomRecipe.postValue(handleRandomRecipeResponse(response))
} catch (e: Throwable) {
if (e is IOException) {
randomRecipe.postValue(Resource.Error("Turn on Internet"))
} else {
randomRecipe.postValue(Resource.Error("Conversion Problem"))
}
}
}
}
private fun handleRandomRecipeResponse(response: Response<RandomRecipe>): Resource<RandomRecipe> {
if(response.isSuccessful) {
response.body()?.let { newResponse->
if(randomRecipeList == null) {
randomRecipeList = newResponse
}else{
val oldRecipeList = randomRecipeList?.recipes
val newRecipeList = newResponse.recipes
oldRecipeList?.addAll(newRecipeList)
}
return Resource.Success(randomRecipeList ?: newResponse)
}
}
return Resource.Error(msg = response.message())
}
}
RandomRecipe(MainActivity)
@AndroidEntryPoint
class RandomRecipes : AppCompatActivity() {
private lateinit var binding: RecipesRandomBinding
lateinit var randomRecipeViewModel: RandomRecipeViewModel
lateinit var randomRecipeAdapter: RandomRecipeAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = RecipesRandomBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.hide()
randomRecipeAdapter = RandomRecipeAdapter()
randomRecipeViewModel = ViewModelProvider(this)[RandomRecipeViewModel::class.java]
binding.apply {
randomRecyclerView.adapter = randomRecipeAdapter
randomRecyclerView.layoutManager = LinearLayoutManager(applicationContext)
}
randomRecipeViewModel.randomRecipe.observe(this, Observer { resource->
when(resource) {
is Resource.Loading -> showProgressBar()
is Resource.Error -> {
hideProgressBar()
resource.msg?.let {
Snackbar.make(binding.randomRecipeActivity, it, Snackbar.LENGTH_INDEFINITE)
.setAction("Refresh"){
randomRecipeViewModel.getRandomRecipes()
}.show()
}
}
is Resource.Success -> {
hideProgressBar()
resource.data?.let { recipe ->
randomRecipeAdapter.differ.submitList(recipe.recipes.toList())
}
}
}
})
}
private fun showProgressBar() {
binding.progressbar.visibility = View.VISIBLE
}
private fun hideProgressBar() {
binding.progressbar.visibility = View.INVISIBLE
}
}
Resource class
sealed class Resource<T>(
val data: T? = null,
val msg: String? = null
) {
class Loading<T>(): Resource<T>()
class Success<T>(data: T): Resource<T>(data)
class Error<T>(msg: String, data: T? = null): Resource<T>(data, msg)
}
Error java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.lifecycle.MutableLiveData.postValue(java.lang.Object)' on a null object reference at com.example.recipe.viewmodels.RandomRecipeViewModel$getRandomRecipes$1.invokeSuspend(RandomRecipeViewModel.kt:45) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:367) at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30) at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25) at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110) at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56) at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47) at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1) at com.example.recipe.viewmodels.RandomRecipeViewModel.getRandomRecipes(RandomRecipeViewModel.kt:33) at com.example.recipe.viewmodels.RandomRecipeViewModel.(RandomRecipeViewModel.kt:26) at com.example.recipe.util.DaggerMyApplication_HiltComponents_SingletonC$ViewModelCImpl.randomRecipeViewModel(DaggerMyApplication_HiltComponents_SingletonC.java:442) at com.example.recipe.util.DaggerMyApplication_HiltComponents_SingletonC$ViewModelCImpl.access$1800(DaggerMyApplication_HiltComponents_SingletonC.java:419) at com.example.recipe.util.DaggerMyApplication_HiltComponents_SingletonC$ViewModelCImpl$SwitchingProvider.get(DaggerMyApplication_HiltComponents_SingletonC.java:477) at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory$1.create(HiltViewModelFactory.java:100) at androidx.lifecycle.AbstractSavedStateViewModelFactory.create(AbstractSavedStateViewModelFactory.java:97) at androidx.lifecycle.AbstractSavedStateViewModelFactory.create(AbstractSavedStateViewModelFactory.java:119) at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:109) at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.kt:83) at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187) at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153) at com.example.recipe.RandomRecipes.onCreate(RandomRecipes.kt:31) at android.app.Activity.performCreate(Activity.java:7088) at android.app.Activity.performCreate(Activity.java:7079) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2895) at android.app.ActivityThread.-wrap11(Unknown Source:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1616) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:176) at android.app.ActivityThread.main(ActivityThread.java:6651) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:824)
Switch the order of the init
block and property initializers:
val randomRecipe = MutableLiveData<Resource<RandomRecipe>>()
var randomRecipeList: RandomRecipe? = null
init {
getRandomRecipes()
}
Kotlin documentation is pretty clear on that topic:
During the initialization of an instance, the initializer blocks are executed in the same order as they appear in the class body, interleaved with the property initializers.
The randomRecipe
was simply not initialized at the time when the getRandomRecipes
method was executed.