【问题标题】:"java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState" when show BottomSheetDialogFragment\"java.lang.IllegalStateException: Can't perform this action after onSaveInstanceState\" when show BottomSheetDialogFragment
【发布时间】:2022-02-09 03:10:02
【问题描述】:

当我试图在 Fragment 中打开 BottomSheetDialogFragment 时遇到问题,使用来自另一个片段的回调结果,该片段嵌套在另一个活动中。

所有进一步的演示都是对项目中真实案例的抽象,具有无法更改的已建立的应用程序架构。 让我来解释你。

我有一个名为“MainActivity”的主要主机活动,其中包含 BaseFragment

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)



        binding.flContainer.setOnClickListener { view ->
            Snackbar.make(view, \"Replace with your own action\", Snackbar.LENGTH_LONG)
                .setAction(\"Action\", null).show()
        }
    }

    override fun onStart() {
        super.onStart()

        supportFragmentManager.beginTransaction()
            .add(R.id.flContainer, BaseFragment())
            .addToBackStack(BaseFragment.TAG)
            .commitAllowingStateLoss()
    }

    override fun onResume() {
        super.onResume()

        Log.e(\"VadymTag\", \"MainActivity onResume\")
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)

        Log.e(\"VadymTag\", \"MainActivity onSaveInstanceState\")
    }

    override fun onPause() {
        super.onPause()

        Log.e(\"VadymTag\", \"MainActivity onPause\")

    }
}

这个 BaseFragment 使用包含 LoginFragment 的 LoginActivity 打开登录屏幕,因为它需要授权用户。

class BaseFragment : Fragment() {
    private var _binding: FragmentBaseBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        _binding = FragmentBaseBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        _binding?.bOpenLogin?.setOnClickListener {
            startActivity(Intent(requireContext(), LoginActivity::class.java))
        }

        MainNavigator.openBottomSheet = ::openBottomSheet
    }

    override fun onResume() {
        super.onResume()

        Log.e(\"VadymTag\", \"BaseFragment onResume\")

    }

    fun openBottomSheet() {
        val bottomSheetFragment = MyBottomSheetDialog()

        bottomSheetFragment.show(childFragmentManager, MyBottomSheetDialog.TAG)
    }

    override fun onPause() {
        super.onPause()

        Log.e(\"VadymTag\", \"BaseFragment onPause\")

    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)

        Log.e(\"VadymTag\", \"BaseFragment onSaveInstanceState\")
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    companion object {
        val TAG = BaseFragment::class.java.simpleName
    }
}

LoginActivity 还使用supportFragmentManager.setFragmentResultListener (... 处理登录成功/失败的结果。对于此示例, FragmentResultListener 成功处理任何更改。 LoginActivity 要求 MainNavigator 从称为登录的 BaseFragment 打开 BottomSheetDialogFragment,用于授权用户并完成。

class LoginActivity : AppCompatActivity() {
    
        private lateinit var binding: LoginMainBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            binding = LoginMainBinding.inflate(layoutInflater)
            setContentView(binding.root)
        }
    
        override fun onStart() {
            super.onStart()
    
            supportFragmentManager.beginTransaction()
                .add(R.id.flLoginContainer, LoginFragment())
                .addToBackStack(LoginFragment.TAG)
                .commit()
    
            initLoginListener()
        }
    
    
        fun initLoginListener() {
            supportFragmentManager
                .setFragmentResultListener(LOGIN_KEY, this) { _, bundle ->
                    MainNavigator.openBottomSheet()
                    finish()
                }
        }
    
        companion object {
            const val LOGIN_KEY = \"login_key\"
            const val LOGIN_FIELD = \"login_key\"
    
        }
    }

登录片段

    class LoginFragment : Fragment() {

    private var _binding: LoginFragmentBinding? = null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        _binding = LoginFragmentBinding.inflate(inflater, container, false)
        return _binding?.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        _binding?.bLoginSuccess?.setOnClickListener {
            parentFragmentManager.setFragmentResult(LOGIN_KEY, bundleOf(LOGIN_FIELD to true))

        }
    }

    companion object {
        val TAG = LoginFragment::class.java.simpleName
    }
}

Main Navigator 是用于跨整个应用程序导航的抽象。

    object MainNavigator {

    var openBottomSheet: () -> Unit = {}
} 

MainNavigator 调用 BaseFragment 打开 BottomSheetDialogFragment。

class MyBottomSheetDialog : BottomSheetDialogFragment() {
    
        private var _binding: FragmentMyBottomSheetBinding? = null
    
        private val binding get() = _binding!!
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
    
            _binding = FragmentMyBottomSheetBinding.inflate(inflater, container, false)
            return binding.root
        }
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
    
        }
    
        override fun onDestroyView() {
            super.onDestroyView()
            _binding = null
        }
    
        companion object {
            val TAG = MyBottomSheetDialog::class.java.simpleName
        }
    }

当 LoginActivity 调用 MainNavigator 打开 BottomSheetDialogFragment。 - 发生 2022-02-08 19:49:58.285 20135-20135/com.vadim.stackoverflowquestion E/VadymTag: BaseFragment onPause 2022-02-08 19:49:58.286 20135-20135/com.vadim.stackoverflowquestion E/VadymTag: MainActivity onPause 2022-02-08 19:49:58.790 0-0/? E/init: updatable process \'console\' exited 4 times in 4 minutes 2022-02-08 19:49:59.027 20135-20135/com.vadim.stackoverflowquestion E/VadymTag: BaseFragment onSaveInstanceState 2022-02-08 19:49:59.031 20135-20135/com.vadim.stackoverflowquestion E/VadymTag: MainActivity onSaveInstanceState 2022-02-08 19:49:59.799 20135-20135/com.vadim.stackoverflowquestion E/AndroidRuntime: FATAL EXCEPTION: main Process: com.galazjukvadim.stackoverflowquestion, PID: 20135 java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at androidx.fragment.app.FragmentManager.checkStateLoss(FragmentManager.java:1844) at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1884) at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:329) at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:294) at androidx.fragment.app.DialogFragment.show(DialogFragment.java:260) at com.galazjukvadim.stackoverflowquestion.BaseFragment.openBottomSheet(BaseFragment.kt:52) at com.galazjukvadim.stackoverflowquestion.BaseFragment$onViewCreated$2.invoke(BaseFragment.kt:39) at com.galazjukvadim.stackoverflowquestion.BaseFragment$onViewCreated$2.invoke(BaseFragment.kt:39) at com.galazjukvadim.stackoverflowquestion.LoginActivity.initLoginListener$lambda-0(LoginActivity.kt:33) at com.galazjukvadim.stackoverflowquestion.LoginActivity.$r8$lambda$3dwuINVTP3WL69H0HgmUiCWJ7Dw(Unknown Source:0) at com.galazjukvadim.stackoverflowquestion.LoginActivity$$ExternalSyntheticLambda0.onFragmentResult(Unknown Source:2) at androidx.fragment.app.FragmentManager$LifecycleAwareResultListener.onFragmentResult(FragmentManager.java:256) at androidx.fragment.app.FragmentManager.setFragmentResult(FragmentManager.java:865) at com.galazjukvadim.stackoverflowquestion.LoginFragment.onViewCreated$lambda-0(LoginFragment.kt:30) at com.galazjukvadim.stackoverflowquestion.LoginFragment.$r8$lambda$PNHKtYyi4mi0uK7kLsV6wOurKW4(Unknown Source:0)

它引起了以下称为 Activity state loss 的问题。 阅读以下文章:12 我看到了预期的行为。 我的 BaseFragment 和 MainActivity 调用了 onPause 并在 onSaveInstanceState 之后触发了 throw IllegalStateException: Can not perform this action after onSaveInstanceState

在这种情况下,使用.commitAllowingStateLoss() 显示BottomSheetDialogFragment 是可以负担的。但是在bottomSheetFragment.show(childFragmentManager, MyBottomSheetDialog.TAG) 的引擎盖下通常使用ft.commit();

有人知道解决方案吗?

    标签: android-fragments fragment android-dialogfragment fragmenttransaction activity-state


    【解决方案1】:
            if (!childFragmentManager.isStateSaved) {}
    

    如果未保存状态,则在调用 bottomsheet.show() 之前添加此检查,您可以执行事务或忽略。

    其他方法是创建自定义 DialogFragment 并覆盖 show 方法并添加 commitallowingstateloss 实现

    【讨论】:

    • 你好@Manoj Mohanty 不幸的是使用if (!childFragmentManager.isStateSaved) {} 将修复抛出错误,但打开BottomSheetDialogFragment 的命令将无法修复,因为BaseFragment 将跳过此谢谢您的建议
    猜你喜欢
    • 2012-08-15
    • 2019-05-03
    • 2013-12-19
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    • 1970-01-01
    • 2018-10-21
    • 2020-09-28
    相关资源
    最近更新 更多