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?
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 }
}
}
}
}
}
}