androidkotlinforceclose

Why do I get an UninitializedPropertyAccessException? Android Kotlin


So I'm trying to send a text data from text entered in edit text through another activity with intent. I'm trying to send the text data from HomeActivity to SearchResultActivity. However, when I clicked the button that calls the startActivity(intent), my application forced closed and I get this exception:

2021-04-27 02:46:30.622 13861-13861/com.dicoding.movieapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.dicoding.movieapp, PID: 13861
kotlin.UninitializedPropertyAccessException: lateinit property activityHomeBinding has not been initialized
    at com.dicoding.movieapp.home.HomeActivity.onClick(HomeActivity.kt:39)
    at android.view.View.performClick(View.java:7500)
    at android.view.View.performClickInternal(View.java:7472)
    at android.view.View.access$3600(View.java:824)
    at android.view.View$PerformClick.run(View.java:28657)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:239)
    at android.app.ActivityThread.main(ActivityThread.java:8107)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:626)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1015)

I'm quite confused what I did wrong here. This is my code for the home activity. I have already initialized the binding in the onCreate function but why is it still saying that its uninitialized?

 override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val activityHomeBinding = ActivityHomeBinding.inflate(layoutInflater)
    setContentView(activityHomeBinding.root)
    activityHomeBinding.btnSearch.setOnClickListener(this)
}

    override fun onClick(v: View?) {
    when (v?.id){
        R.id.btn_search -> {
            val searchText = activityHomeBinding.editTextSearch.text.toString().trim()
            if (searchText == null){
                activityHomeBinding.editTextSearch.error = "Field cannot be empty"
            } else {
                val intent = Intent(this@HomeActivity, SearchResultActivity::class.java)
                intent.putExtra(SearchResultActivity.EXTRA_SEARCH,searchText)
                startActivity(intent)
            }
        }
    }

And this is the code inside my SearchResultActivity:

private lateinit var searchResultActivity: ActivitySearchResultBinding

companion object {
    const val EXTRA_SEARCH = "extra_search"
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    searchResultActivity = ActivitySearchResultBinding.inflate(layoutInflater)
    setContentView(searchResultActivity.root)

    searchResultActivity.testIntent.text = intent.getStringExtra(EXTRA_SEARCH)
}

Solution

  • The error message points that on line 39 of your HomeActivity class, you're pointing at an uninitialized property. This is because you have defined 2 activityHomeBindings with different scopes

    class HomeActivity : AppCompatActivity(), View.OnClickListener {
        
        ...
    
        lateinit var activityHomeBinding: ActivityHomeBinding <-- 1st one, globally scoped
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val activityHomeBinding = ActivityHomeBinding.inflate(layoutInflater) <-- 2nd one, function-scoped (to onCreate)
            setContentView(activityHomeBinding.root)
            activityHomeBinding.btnSearch.setOnClickListener(this)
        }
    
        override fun onClick(v: View?) {
            when (v?.id){
                R.id.btn_search -> {
                    val searchText = activityHomeBinding.editTextSearch.text.toString().trim() <-- this points at 1st one, (i.e. globally scoped)
                    if (searchText == null){
                        activityHomeBinding.editTextSearch.error = "Field cannot be empty"
                    } 
    
                    ...
                }
            }
        }
    }
    

    The solution is to simply remove val from the line where you're inflating the binding. This way, instead of creating a new variable, you're assigning the inflated binding to globally scoped lateinit var activityHomeBinding which then can be used by your onClick callback.

    So make the change from

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        val activityHomeBinding = ActivityHomeBinding.inflate(layoutInflater)
        ...
    }
    

    to

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        activityHomeBinding = ActivityHomeBinding.inflate(layoutInflater)
        ...
    }