androidkotlinviewmodelandroid-livedatamutablelivedata

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


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)


Solution

  • 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.