How to save Kotlin lambda in fragment before the screen rotation? It works in Activity but doesn't correct work in the fragment. An exception occurs when executing a lambda expression if it contains calls to methods of the child class of PermissionsFragment, Why?
Class which stores lambda in itself:
class ActionKeeper(var action: ((isGranted: Boolean) -> Unit)? = null) : Serializable
Fragment which saves lambda to ActionKeeper on screen rotation:
abstract class PermissionsFragment : Fragment() {
private var action: ((isGranted: Boolean) -> Unit)? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState != null) {
restoreState(savedInstanceState)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putSerializable("actionKeeper", ActionKeeper(action))
}
private fun restoreState(state: Bundle) {
val keeper = state.getSerializable("actionKeeper") as ActionKeeper
action = keeper.action
}
fun usePermission(permission: String, action: (isGranted: Boolean) -> Unit) {
if (!isPermissionGranted(permission)) {
this.action = action
requestPermissions(arrayOf(permission), 1)
} else {
action(true)
}
}
/* ........ */
}
Class which extends from PermissionsFragment:
class SamplePermissionsFragment : PermissionsFragment() {
private var toast: Toast? = null
private fun doWithPermission() {
usePermission(Manifest.permission.SEND_SMS) { isGranted ->
if (isGranted) {
showToast("Fragment permission granted")
} else {
showToast("Fragment permission refused")
}
}
}
private fun showToast(text: String) {
toast?.cancel()
toast = Toast.makeText(context!!, text, Toast.LENGTH_SHORT).apply { show() }
}
}
Logcat exteption:
2018-09-27 15:57:17.068 5569-5569/com.alexchurkin.permissionsample E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.alexchurkin.permissionsample, PID: 5569
java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=65537, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {com.alexchurkin.permissionsample/com.alexchurkin.permissionsample.fragment.FragmentHostActivity}: kotlin.KotlinNullPointerException
at android.app.ActivityThread.deliverResults(ActivityThread.java:4196)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4239)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1599)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:165)
at android.app.ActivityThread.main(ActivityThread.java:6365)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:883)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
Caused by: kotlin.KotlinNullPointerException
at com.alexchurkin.permissionsample.fragment.SamplePermissionsFragment.showToast(SamplePermissionsFragment.kt:56)
at com.alexchurkin.permissionsample.fragment.SamplePermissionsFragment.access$showToast(SamplePermissionsFragment.kt:13)
at com.alexchurkin.permissionsample.fragment.SamplePermissionsFragment$doWithPermission$1.invoke(SamplePermissionsFragment.kt:35)
at com.alexchurkin.permissionsample.fragment.SamplePermissionsFragment$doWithPermission$1.invoke(SamplePermissionsFragment.kt:13)
at com.alexchurkin.fastpermissions.fragments.PermissionsFragment.onRequestPermissionsResult(PermissionsFragment.kt:38)
at android.support.v4.app.FragmentActivity.onRequestPermissionsResult(FragmentActivity.java:860)
at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:7268)
at android.app.Activity.dispatchActivityResult(Activity.java:7120)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4192)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4239)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1599)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:165)
at android.app.ActivityThread.main(ActivityThread.java:6365)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:883)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
You might have issue with context
Try below code and let me know :
private fun showToast(text: String) {
toast?.cancel()
activity?.let {
toast = Toast.makeText(it, text, Toast.LENGTH_SHORT).apply { show() }
}
}
Edit:
Save object of your ActionKeeper
as:
outState.putSerializable("actionKeeper", object: ActionKeeper(action))
The problem in your code is that there are references inside your lambda that will be deallocated (thus, inaccessible) in the future. When saving the state, Android will save the pointers (references) inside that lambda, so it can't point towards invalid memory.
This is better explained here: How to save and restore lambdas in Android?
To fix it, in your case, you could have a reference of the callee in one of the lambda parameters, ie
private var action: ((Fragment, Boolean) -> Unit)? = null
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.