Search code examples
androidkotlinkotlin-lateinit

lateinit property not initialized after pick file explorer


I have an activity that let user select file from explorer, retrieve the result in onActivityResult() and save the result inside an object called Property

I have a lateinit variable as follow :

lateinit var uploadProperties: Property

And the code to open explorer (permission already granted) :

fun openExplorer(property: Property) {
    uploadProperties = property
    val intent = Intent(Intent.ACTION_GET_CONTENT)
    intent.type = Constants.ALL_FILE
    intent.addCategory(Intent.CATEGORY_OPENABLE)
    startActivityForResult(
        Intent.createChooser(intent, getString(R.string.select_file)),
        REQ_FILE
    )
}

an then onActivityResult(), I convert the data to base64 and assign it to the Property

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == Activity.RESULT_OK) {
        when (requestCode) {
            REQ_FILE -> {
                data?.let {
                    val base64 = data.toBase64()

                    uploadProperties.let {
                        value = base64
                    }
                }
            }
        }
    }
}

The problem is that at some cases, I got these error report on crashlytics :

Caused by kotlin.UninitializedPropertyAccessException
lateinit property uploadProperties has not been initialized

I tried this many times, and I got these error only a few times (doesn't know what trigger this). But some user complaints that the app always crashed after choosing files from explorer. I checked on crashlytics and the message are as mentioned above.

I have tried to debug using breakpoint before startActivityForResult(). The variable uploadProperties is already initialized and the value is correct. But after choosing file from explorer, in some cases, the app still crashed with UninitializedPropertyAccessException.

Any idea what caused this error and how to fix this?


Solution

  • You need to handle the case where your apps process is destroyed to free up memory. In your case storing uploadProperties should be enough.

    In your Activity, first store it when it's being destroyed (I assume your Property class is Parcelable, if not write whatever you need to be able to restore it later):

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // ( ... your save instance states if any)
        // store upload properties if they were set up
        if(::uploadProperties.isInitialized)
            outState.putParcelable("uploadProperties", uploadProperties)
    }
    

    then restore it somewhere in your onCreate:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // (...)
        if(savedInstanceState?.containsKey("uploadProperties") == true){
            uploadProperties = savedInstanceState.getParcelable("uploadProperties")!!
        }
        // (...)
    }
    

    Now alter your result callback so it's delayed until after your properties are restored:

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK) {
            when (requestCode) {
                REQ_FILE -> {
                    data?.let {
                        val base64 = data.toBase64()
                        // delay using androidx.lifecycle
                        lifecycleScope.launchWhenCreated {
                            uploadProperties.let { it.value = base64 }
                        }
                    }
                }
            }
        }
    }