簡體   English   中英

IllegalStateException 在 onSaveInstanceState 顯示 DialogFragment 后無法執行此操作

[英]IllegalStateException Can not perform this action after onSaveInstanceState show DialogFragment

這是我第一次遇到這個問題。

我已經對 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()
        }
    })
}

第二個是當我嘗試在 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()
        }

理解這個問題的任何幫助(對於想法,或對問題的一些解釋,或快速修復......)

保存狀態的目的是讓用戶可以離開應用程序,稍后返回以找到與他離開應用程序時完全相同的狀態的應用程序,因此他可以像什么也沒發生一樣繼續。 在后台,Android 可以終止您的應用程序以釋放資源,但用戶不必知道這一點。

Android 已經為你做了很多狀態保存,比如你添加的片段。 IllegalStateException的原因是因為在Fragment的狀態已經保存之后添加了一個Fragment ,因此它的狀態無法再次完全恢復。 在這兩種情況下,您都啟動了后台任務,當您被“回叫”時,用戶已經離開(或進行了配置更改,例如旋轉設備)。

要處理此類情況,您可以:

  1. 使用commitAllowingStateLoss()提交您的FragmentTransaction允許狀態丟失。 請注意,您可以執行自己的FragmentTransaction而不是在DialogFragment上調用show() ,因為show()正是這樣做的,請參閱源代碼。
  2. 檢查是否已經擁有國家已經做了以前保存FragmentTransaction通過調用isStateSaved()在你的FragmentManager

解決方案 #2(無狀態丟失)比 #1 好,但它確實需要您在配置更改時從 FireStore 獲取數據兩次。 更現代的方法是使用ViewModel來保存您的數據,因此您只需要獲取一次數據(在配置更改時)。 這是因為ViewModel生命周期比Fragment更長(而且非常方便)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM