IllegalStateException onSaveInstanceState 显示 DialogFragment 后无法执行此操作

Ito*_*oun 3 android android-lifecycle onsaveinstancestate

这是我第一次遇到这个问题。

我已经仔细研究了 SO 上的几个答案,尤其是这个这个,但它没有解决我的问题,并且大多数答案不能用作解决我的案例的安全可靠的方法。

我已经尝试过:

  • 覆盖onSaveInstanceState并且不调用 super

但 commitAllowingStateLoss 不能用于第一种情况。

我正在寻找有关如何避免引发此异常以及如何实现引发异常的操作的解释(在第一种情况下,显示dialogFragment)。我已经知道如何引发此异常,但是,我不知道在我的情况下引发了什么。它在我的应用程序中出现两次:

第一个发生在一个非常简单的活动中,我有一个简单的动画,在该动画的末尾我显示了一个 DialogFragment (SplashActivity):

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val potentialLanguage = storage.getString(Constants.LANGUAGE)
    val lang = if (potentialLanguage.isNotEmpty()) {
        potentialLanguage
    } else {
        Locale.getDefault().language
    }
    val language = Language.getFromName(lang)!!
    val dm = res.displayMetrics
    val conf = res.configuration
    conf.setLocale(Locale(language))
    saveLanguage(context, lang)
    // Use conf.locale = new Locale(...) if targeting lower versions
    res.updateConfiguration(conf, dm)
    initWarningDialog()
    RevelyGradient
        .radial()
        .colors(
            intArrayOf(
                getColor(R.color.backgroundOnBoardingStart),
                getColor(R.color.backgroundOnBoardingEnd)
            )
        )
        .onBackgroundOf(root)
    ivCap.animate()
        .alpha(1f)
        .setListener(object : Animator.AnimatorListener{
            override fun onAnimationEnd(p0: Animator?) {
                try {
                    commonDialog.show(supportFragmentManager, "CommonDialogSplash") //crash here commonDialog is a DialogFragment
                }
                catch (e: IllegalStateException){
                    try {
                        startActivity(Intent(this@SplashActivity, MainActivity::class.java))
                        finish()
                    }
                    catch (e: IllegalStateException){

                    }
                }
            }

            override fun onAnimationCancel(p0: Animator?) {

            }

            override fun onAnimationRepeat(p0: Animator?) {

            }

            override fun onAnimationStart(p0: Animator?) {

            }
        }).duration = 1000
}

private fun initWarningDialog(){
    commonDialog.isCancelable = false
    commonDialog.setTitle(getString(R.string.warning))
    commonDialog.setFirstTextButton(getString(R.string.ok))
    commonDialog.setDescription(getString(R.string.warning_message))
    commonDialog.setFirstButtonListener(object : CommonDialog.CommonDialogClickListener {
        override fun onClick() {
            commonDialog.dismiss()
            startActivity(Intent(this@SplashActivity, MainActivity::class.java))
            finish()
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

第二个是当我尝试在 firebase firestore 请求(TotoFragment)之后添加片段时:

fun pullChallenges(){
        val db = Firebase.firestore
        val docRef = db.collection("challenges").document(language.name.toLowerCase(Locale.ROOT))
        docRef
            .get()
            .addOnSuccessListener { result ->
                result.data?.let {data ->
                    data.values.map {values->
                        val alOfHm = values as ArrayList<HashMap<String, String>>
                        for (item in alOfHm){
                            val challenge = Challenge()
                            Challenge.ChallengeCategory.getValueOf(item["category"]!!)?.let {
                                challenge.challengeCategory = it
                            }
                            Game.GameMode.getValueOf(item["mode"]!!)?.let {
                                challenge.mode = it
                            }
                            challenge.challenge = item["content"]!!
                            challenges.add(challenge)
                        }
                    }
                }
                ChallengesManager.challenges = challenges
                listener.onChallengesReady(true)
            }
            .addOnFailureListener { exception ->
                listener.onChallengesReady(false)
                Timber.e("Error getting challenges $exception")
            }
    }

 override fun onChallengesReady(success: Boolean) {
        renderLoading()
        if (success) {
            try {
                goToChooseMode()
            }
            catch (e: IllegalStateException){

            }
        }
        else {
            Toast.makeText(requireContext(), getString(R.string.error_get_caps), Toast.LENGTH_SHORT).show()
        }
    }

    private fun goToChooseMode(){
            val bundle = Bundle()
            bundle.putStringArrayList(Constants.PLAYERS, ArrayList(viewModel.players))
            activity.supportFragmentManager
                .beginTransaction()
                .addToBackStack(ChooseModeFragment::class.java.name)
                .setReorderingAllowed(true)
                .add(R.id.fragmentContainer, ChooseModeFragment::class.java, bundle, ChooseModeFragment::class.java.name)
                .commit()
        }
Run Code Online (Sandbox Code Playgroud)

理解这个问题的任何帮助(对于问题的想法,或对问题的一些解释,或快速解决......)

小智 5

保存状态的目的是,用户可以离开某个应用程序,稍后再回来时会发现该应用程序处于与他离开时完全相同的状态,因此他可以像什么都没发生一样继续操作。在后台,Android 可以终止您的应用程序以释放资源,但用户不必知道这一点。

Android 已经为你做了很多状态保存,比如你添加的片段。IllegalStateException抛出该异常的原因是,您Fragment在保存了 a 的状态后添加了 a,因此它的状态无法再次完全恢复。在这两种情况下,您都会启动后台任务,并且当您“回电”时,用户已经导航离开(或进行了配置更改,例如旋转设备)。

要处理此类情况,您可以:

  1. 提交您的FragmentTransaction允许状态损失commitAllowingStateLoss()。请注意,您可以自己做,而不是调用show()您的,因为正是这样做,请参阅源代码。DialogFragmentFragmentTransactionshow()
  2. 在执行 a 之前检查状态是否已保存,FragmentTransaction方法是调用isStateSaved()您的FragmentManager.

解决方案 #2(无状态丢失)比 #1 更好,但它确实要求您在配置更改时从 FireStore 获取数据两次。更现代的方法将使用ViewModel来保存数据,因此您只需要获取一次数据(在配置更改时)。这是因为 aViewModel的生命周期比Fragment.