打开BottomSheet切换主题时Android应用崩溃问题求助
问题与解决方案
问题场景
我实现了一个用于展示餐厅产品详情的BottomSheetDialogFragment,代码如下:
class ProductItemInfo(private val product: Product) : BottomSheetDialogFragment() { private var _binding: DialogProductInfoBinding? = null private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = DialogProductInfoBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.apply { productName.text = product.name productPrice.text = String.format("Rs %s", product.totalPrice) productCategory.text=product.category productDescription.text=product.category productImage.load(product.image) } } override fun getTheme(): Int { return R.style.CustomBottomSheetDialogTheme } override fun onDestroyView() { super.onDestroyView() _binding = null } }
我从另一个Fragment中启动该底部弹窗:
val dialogProductInfoBinding = ProductItemInfo(it) dialogProductInfoBinding.show(childFragmentManager, null)
打开弹窗后,下拉通知栏切换深色主题时应用崩溃,错误日志如下:
E/AndroidRuntime: FATAL EXCEPTION: main Process: ass.cafeburp.dine, PID: 18522 java.lang.RuntimeException: Unable to start activity ComponentInfo{ass.cafeburp.dine/ass.cafeburp.dine.presentation.MainActivity}: android.view.InflateException: Binary XML file line #22 in ass.cafeburp.dine:layout/activity_main: Binary XML file line #9 in ass.cafeburp.dine:layout/content_main: Error inflating class fragment at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4035) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4201) at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:6347) at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:6212) at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:71) at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2438) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8663) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135) Caused by: android.view.InflateException: Binary XML file line #22 in ass.cafeburp.dine:layout/activity_main: Binary XML file line #9 in ass.cafeburp.dine:layout/content_main: Error inflating class fragment Caused by: android.view.InflateException: Binary XML file line #9 in ass.cafeburp.dine:layout/content_main: Error inflating class fragment Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment ass.cafeburp.dine.presentation.dialogs.ProductItemInfo: could not find Fragment constructor at androidx.fragment.app.Fragment.instantiate(Fragment.java:678) at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57) at androidx.fragment.app.FragmentManager$3.instantiate(FragmentManager.java:498) at androidx.fragment.app.FragmentState.instantiate(FragmentState.java:81) at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:85) at androidx.fragment.app.FragmentManager.restoreSaveStateInternal(FragmentManager.java:2496) at androidx.fragment.app.Fragment.restoreChildFragmentState(Fragment.java:1989) at androidx.fragment.app.Fragment.onCreate(Fragment.java:1965) at androidx.fragment.app.Fragment.performCreate(Fragment.java:3090) at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:475) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:257) at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968) at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:2875) at androidx.fragment.app.Fragment.restoreChildFragmentState(Fragment.java:1990) at androidx.fragment.app.Fragment.onCreate(Fragment.java:1965) at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.kt:169) at androidx.fragment.app.Fragment.performCreate(Fragment.java:3090) at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:475) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:257) at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:142) at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:136) E/AndroidRuntime: at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:248) at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:227) at android.view.LayoutInflater.tryCreateView(LayoutInflater.java:1073) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1001) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965) at android.view.LayoutInflater.rInflate(LayoutInflater.java:1127) at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088) at android.view.LayoutInflater.parseInclude(LayoutInflater.java:1267) at android.view.LayoutInflater.rInflate(LayoutInflater.java:1123) at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088) at android.view.LayoutInflater.inflate(LayoutInflater.java:686) at android.view.LayoutInflater.inflate(LayoutInflater.java:538) at ass.cafeburp.dine.databinding.ActivityMainBinding.inflate(ActivityMainBinding.java:49) at ass.cafeburp.dine.databinding.ActivityMainBinding.inflate(ActivityMainBinding.java:43) at ass.cafeburp.dine.presentation.MainActivity.onCreate(MainActivity.kt:28) at android.app.Activity.performCreate(Activity.java:8290) at android.app.Activity.performCreate(Activity.java:8270) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4009) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4201) at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:6347) at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:6212) at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:71) at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2438) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8663) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135) Caused by: java.lang.NoSuchMethodException: ass.cafeburp.dine.presentation.dialogs.ProductItemInfo.<init> [] at java.lang.Class.getConstructor0(Class.java:2363) at java.lang.Class.getConstructor(Class.java:1759) at androidx.fragment.app.Fragment.instantiate(Fragment.java:663) ... 56 more
原因分析
崩溃核心原因是Fragment必须提供无参构造函数。切换深色主题时,系统会重建Activity和Fragment,此时系统会通过反射调用Fragment的无参构造来实例化对象,但你的ProductItemInfo只有带参数的构造,导致反射失败抛出NoSuchMethodException。
解决方案
修改ProductItemInfo,添加无参构造,并通过Bundle传递Product参数(注意Product必须实现Parcelable接口):
1. 让Product实现Parcelable
@Parcelize data class Product( val name: String, val totalPrice: String, val category: String, val image: String ) : Parcelable
2. 修改ProductItemInfo代码
class ProductItemInfo : BottomSheetDialogFragment() { private var _binding: DialogProductInfoBinding? = null private val binding get() = _binding!! private lateinit var product: Product // 提供newInstance方法创建实例并传递参数 companion object { private const val ARG_PRODUCT = "arg_product" fun newInstance(product: Product): ProductItemInfo { val fragment = ProductItemInfo() val args = Bundle() args.putParcelable(ARG_PRODUCT, product) fragment.arguments = args return fragment } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 从Bundle中获取参数 arguments?.let { product = it.getParcelable(ARG_PRODUCT)!! } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = DialogProductInfoBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.apply { productName.text = product.name productPrice.text = String.format("Rs %s", product.totalPrice) productCategory.text = product.category productDescription.text = product.category // 注意:原代码此处赋值为category,建议检查是否应为description字段 productImage.load(product.image) } } override fun getTheme(): Int { return R.style.CustomBottomSheetDialogTheme } override fun onDestroyView() { super.onDestroyView() _binding = null } }
3. 修改启动弹窗的代码
val dialog = ProductItemInfo.newInstance(it) dialog.show(childFragmentManager, null)
这样修改后,系统重建Fragment时会调用无参构造,然后通过arguments恢复参数,就不会出现崩溃问题了。
内容的提问来源于stack exchange,提问作者Ankit Verma
相关产品推荐
相关产品推荐

