Android Fragments “java.lang.非法状态异常:当显示BottomSheetDialogFragment时,无法在onSaveInstanceState”之后执行此操作

ctehm74n  于 2023-03-30  发布在  Android
关注(0)|答案(2)|浏览(146)

当我尝试在Fragment中打开BottomSheetDialogFragment时,我遇到了问题,使用来自另一个片段的回调结果,该片段嵌套在另一个Activity中。
所有进一步的演示都是对项目中真实的案例的抽象,已经建立了不可更改的应用程序架构。让我来解释一下。
我有一个名为“MainActivity”的主主机Activity,其中包含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使用LoginActivity打开登录屏幕,LoginActivity包含LoginFragment,因为它是授权用户所必需的。

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,其中BaseFragment被称为login,用于授权用户并完成。

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"
    
        }
    }

LoginFragment

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
    }
}

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

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();

有人知道解决办法吗?

vlju58qv

vlju58qv1#

if (!childFragmentManager.isStateSaved) {}

在调用www.example.com()之前添加此检查bottomsheet.show,如果状态未保存,则可以执行事务,否则忽略。
另一种方法是创建自定义的DialogFragment和overide show方法并添加commitallowstateloss实现

vmdwslir

vmdwslir2#

我修复了我的问题,使用:

lifecycleScope.launchWhenResumed { 
    bottomSheetFragment.show(childFragmentManager, MyBottomSheetDialog.TAG)
}

相关问题