简体   繁体   English

使用saveInstanceState保存和还原Kotlin lambda

[英]Saving and restoring Kotlin lambda with savedInstanceState

How to save Kotlin lambda in fragment before the screen rotation? 如何在屏幕旋转之前以片段形式保存Kotlin lambda? 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? 如果执行lambda表达式包含对PermissionsFragment的子类的方法的调用,则会发生异常,为什么?

Class which stores lambda in itself: 本身存储lambda的类:

class ActionKeeper(var action: ((isGranted: Boolean) -> Unit)? = null) : Serializable

Fragment which saves lambda to ActionKeeper on screen rotation: 在屏幕旋转时将lambda保存到ActionKeeper的片段:

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: 从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: Logcat灭绝:

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 您可能对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: ActionKeeper对象另存为:

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. 您的代码中的问题在于,您的lambda内部有一些引用,这些引用将来会被释放(因此不可访问)。 When saving the state, Android will save the pointers (references) inside that lambda, so it can't point towards invalid memory. 保存状态时,Android会将指针(引用)保存在该lambda中,因此它不能指向无效的内存。

This is better explained here: How to save and restore lambdas in Android? 此处对此有更好的解释: 如何在Android中保存和还原Lambda?

To fix it, in your case, you could have a reference of the callee in one of the lambda parameters, ie 要解决此问题,在您的情况下,您可以在lambda参数之一中引用被调用者,即

private var action: ((Fragment, Boolean) -> Unit)? = null

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用savedInstanceState在DialogFragment中保存和恢复EditText光标位置 - Saving and restoring EditText cursor position in a DialogFragment using savedInstanceState savedInstanceState无法正确还原Fragment - savedInstanceState not restoring Fragment properly 使用saveInstanceState保存数据 - Saving data using savedInstanceState 用savedInstanceState保存fragmentstate - Saving fragmentstate with savedInstanceState 在bundle savedInstanceState中保存ArrayList - saving ArrayList in the bundle savedInstanceState 从后台堆栈恢复片段时的savedInstanceState - savedInstanceState when restoring fragment from back stack 如何知道系统是否正在从ViewModel中的saveInstanceState恢复 - How to know if system is restoring from savedInstanceState in viewModel 返回活动时恢复状态不起作用,savedInstanceState为null - Restoring state not working when returning to activity, savedInstanceState is null 在不使用savedInstanceState的情况下保存动态添加的LinearLayouts? - Saving dynamically added LinearLayouts without using savedInstanceState? Android kotlin onCreate(savedInstanceState: Bundle?) 导致 IllegalArgumentException - Android kotlin onCreate(savedInstanceState: Bundle?) cause IllegalArgumentException
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM