[SOLVED] Kotlin failed to start emulator – ViewModel issue


This Question and Answer are collected from stackoverflow and tested by JTuto community, is licensed under
CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

Issue

Hello I am trying to run a BMI calculator with a ViewModel, yet I haven’t had a successful run. I am new to coding in kotlin and android studio. Any help would be much appreciated. I am hoping it is just a small bug. I think the issue lies in the ViewModel code yet, I am un sure. As in my logcat is Caused by: kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized

ViewModel Code:


import android.util.Log
import androidx.lifecycle.ViewModel

lateinit var binding: ActivityMainBinding
private const val TAG = "inc, wt, ft"

class bmiViewModel() : ViewModel() {
    val inc =(binding.inch.text.toString()).toInt()
    val wt =(binding.lbs.text.toString()).toInt()
    val ft =(binding.feet.text.toString()).toInt()
    val total = formula(inc,ft,wt)

    private fun formula(inc: Int, ft: Int, wt: Int): Float {
        val htTotal = (ft.toFloat() / 12) + inc.toFloat()
        val BMI  = (wt.toFloat()*703) / (htTotal * htTotal)
         Log.d(TAG,"BMI", Exception())
        return BMI
    }
     fun updatebmi(): Unit{

        if (total < 18.5) {
            binding.status.text = "Under Weight"
        } else if (total >= 18.5 && total < 24.9) {
            binding.status.text = "Healthy"
        } else if (total >= 24.9 && total < 30) {
            binding.status.text = "Overweight"
        } else if (total >= 30){
            binding.status.text = "Obese"
        }
    }

}

MainActivity:

private const val TAG = "MainActivity"

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private val bmiViewModel: bmiViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG, "OnCreate(Bundle?) called")
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        Log.d(TAG, "Got a BMIViewModel: $bmiViewModel")

        binding.calc.setOnClickListener {
            if(binding.height.text.isNotEmpty() && binding.weight.text.isNotEmpty()){
                bmiViewModel.updatebmi()
                binding.bmiTV.visibility = View.VISIBLE
                binding.status.visibility = View.VISIBLE
            }
            else {
                Toast.makeText(this,
                    "Please add a Height & Weight", Toast.LENGTH_SHORT).show()
            }
        }
        binding.clear.setOnClickListener {
            reset()
        }
    }

    override fun onStart() {
        super.onStart()
        Log.d(TAG, "onStart() called")
    }

    override fun onResume() {
        super.onResume()
        Log.d(TAG, "onResume() called")
    }

    override fun onPause() {
        super.onPause()
        Log.d(TAG, "onPause() called")
    }

    override fun onStop() {
        super.onStop()
        Log.d(TAG, "onStop() called")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy() called")
    }

    private fun reset() {
        binding.inch.text.clear()
        binding.feet.text.clear()
        binding.lbs.text.clear()
        binding.status.text = " "
        binding.bmiTV.visibility = View.GONE
    }
}

Solution

In your bmiViewModel, you have:

lateinit var binding: ActivityMainBinding

That will not work, for two reasons:

  1. You are not assigning a value to it before trying to use it
  2. You can’t assign a value to it, as view binding is a UI thing for activities and fragments, not viewmodels

Rewrite it as:

import android.util.Log
import androidx.lifecycle.ViewModel

private const val TAG = "inc, wt, ft"

class bmiViewModel() : ViewModel() {

    private fun formula(inc: Int, ft: Int, wt: Int): Float {
        val htTotal = (ft.toFloat() / 12) + inc.toFloat()
        val BMI  = (wt.toFloat()*703) / (htTotal * htTotal)
         Log.d(TAG,"BMI", Exception())
        return BMI
    }
}

Move the updatebmi() code into your activity, as it is updating widgets.

Answered By – CommonsWare

people found this article helpful. What about you?