带有导航图事务的 Fragment InstantiationException
FragmentInstantiationException with Navigation Graph transaction
我在尝试通过 Android 导航组件(导航图)执行事务时遇到异常。但是它在没有导航图的情况下使用旧的 fragent 事务方式工作正常!
java.lang.RuntimeException: Unable to start activity ComponentInfo{XYZ/XYZ.framework.presentation.MainActivity}: android.view.InflateException: Binary XML file line #18 in XYZ:layout/activity_main: Binary XML file line #18 in XYZ:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
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:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: android.view.InflateException: Binary XML file line #18 in XYZ:layout/activity_main: Binary XML file line #18 in com.xyz.diary:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: android.view.InflateException: Binary XML file line #18 XYZ:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment XYZ.recipeList.RecipeListFragment: could not find Fragment constructor
at androidx.fragment.app.Fragment.instantiate(Fragment.java:628)
at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
at androidx.fragment.app.FragmentManager.instantiate(FragmentManager.java:483)
at androidx.navigation.fragment.FragmentNavigator.instantiateFragment(FragmentNavigator.java:132)
at androidx.navigation.fragment.FragmentNavigator.navigate(FragmentNavigator.java:162)
at androidx.navigation.fragment.FragmentNavigator.navigate(FragmentNavigator.java:58)
at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.java:71)
at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.java:28)
at androidx.navigation.NavController.navigate(NavController.java:1066)
at androidx.navigation.NavController.onGraphCreated(NavController.java:639)
at androidx.navigation.NavController.setGraph(NavController.java:592)
at androidx.navigation.NavController.setGraph(NavController.java:557)
at androidx.navigation.NavController.setGraph(NavController.java:539)
at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.java:248)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2949)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:475)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:278)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2106)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1971)
at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:311)
at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:180)
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:52)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/recipeListFragment">
<fragment
android:id="@+id/recipeListFragment"
android:name="com.xyz.diary.framework.presentation.recipeList.RecipeListFragment"
android:label="RecipeListFragment" >
<action
android:id="@+id/action_recipeListFragment_to_recipeInsertNewFragment"
app:destination="@id/recipeInsertNewFragment" />
<action
android:id="@+id/action_recipeListFragment_to_recipeDetailFragment"
app:destination="@id/recipeDetailFragment" />
</fragment>
</navigation>
activty_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".framework.presentation.MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
RecipeListFragment
@FlowPreview
@ExperimentalCoroutinesApi
class RecipeListFragment
constructor(
private val viewModelFactory: ViewModelProvider.Factory,
private val dateUtil: DateUtil
) : Fragment() {
val viewModel: RecipeListViewModel by viewModels {
viewModelFactory
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.setupChannel()
}
@ExperimentalComposeUiApi
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return ComposeView(requireContext()).apply {
setContent { BoxLayout() }
}
}
...
}
HILT DI
@ExperimentalCoroutinesApi
@FlowPreview
@Module
@InstallIn(SingletonComponent::class)
object RecipeViewModelModule {
@Singleton
@JvmStatic
@Provides
fun provideRecipeViewModelFactory(
recipeListInteractors: RecipeListInteractors,
recipeInsertNewInteractors: RecipeInsertNewInteractors,
recipeDetailInteractors: RecipeDetailInteractors,
recipeFactory: RecipeFactory,
editor: SharedPreferences.Editor,
sharedPreferences: SharedPreferences
): ViewModelProvider.Factory {
return RecipeViewModelFactory(
recipeListInteractors = recipeListInteractors,
recipeInsertNewInteractors=recipeInsertNewInteractors,
recipeDetailInteractors = recipeDetailInteractors,
recipeFactory = recipeFactory,
editor = editor,
sharedPreferences = sharedPreferences
)
}
}
@ExperimentalCoroutinesApi
@FlowPreview
@InstallIn(SingletonComponent::class)
@Module
object AppModule {
// https://developer.android.com/reference/java/text/SimpleDateFormat.html?hl=pt-br
@JvmStatic
@Singleton
@Provides
fun provideDateFormat(): SimpleDateFormat {
val sdf = SimpleDateFormat("yyyy-MM-dd hh:mm:ss a", Locale.ENGLISH)
sdf.timeZone = TimeZone.getTimeZone("UTC-7") // match firestore
return sdf
}
@JvmStatic
@Singleton
@Provides
fun provideDateUtil(dateFormat: SimpleDateFormat): DateUtil {
return DateUtil(
dateFormat
)
}
如docs所述:
All subclasses of Fragment must include a public no-argument constructor. The framework will often re-instantiate a fragment class when needed, in particular during state restore, and needs to be able to find this constructor to instantiate it. If the no-argument constructor is not available, a runtime exception will occur in some cases during state restore.
您的片段 class 应该有一个 public 无参数构造函数,以便由 Android 实例化(当进程被系统终止时)或用于导航组件等
尝试将您的 XML
从 FragmentContainerView
更改为 fragment
,如下所示:
@AndroidEntryPoint
class MainNavHostFragment : NavHostFragment() {
@Inject
lateinit var mainFragmentFactory: RecipeFragmentFactory
override fun onAttach(context: Context) {
super.onAttach(context)
childFragmentManager.fragmentFactory = mainFragmentFactory
}
}
xml
<fragment
android:id="@+id/container"
android:name="<path>.MainNavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
我在尝试通过 Android 导航组件(导航图)执行事务时遇到异常。但是它在没有导航图的情况下使用旧的 fragent 事务方式工作正常!
java.lang.RuntimeException: Unable to start activity ComponentInfo{XYZ/XYZ.framework.presentation.MainActivity}: android.view.InflateException: Binary XML file line #18 in XYZ:layout/activity_main: Binary XML file line #18 in XYZ:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
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:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: android.view.InflateException: Binary XML file line #18 in XYZ:layout/activity_main: Binary XML file line #18 in com.xyz.diary:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: android.view.InflateException: Binary XML file line #18 XYZ:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment XYZ.recipeList.RecipeListFragment: could not find Fragment constructor
at androidx.fragment.app.Fragment.instantiate(Fragment.java:628)
at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
at androidx.fragment.app.FragmentManager.instantiate(FragmentManager.java:483)
at androidx.navigation.fragment.FragmentNavigator.instantiateFragment(FragmentNavigator.java:132)
at androidx.navigation.fragment.FragmentNavigator.navigate(FragmentNavigator.java:162)
at androidx.navigation.fragment.FragmentNavigator.navigate(FragmentNavigator.java:58)
at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.java:71)
at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.java:28)
at androidx.navigation.NavController.navigate(NavController.java:1066)
at androidx.navigation.NavController.onGraphCreated(NavController.java:639)
at androidx.navigation.NavController.setGraph(NavController.java:592)
at androidx.navigation.NavController.setGraph(NavController.java:557)
at androidx.navigation.NavController.setGraph(NavController.java:539)
at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.java:248)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2949)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:475)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:278)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2106)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1971)
at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:311)
at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:180)
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:52)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/recipeListFragment">
<fragment
android:id="@+id/recipeListFragment"
android:name="com.xyz.diary.framework.presentation.recipeList.RecipeListFragment"
android:label="RecipeListFragment" >
<action
android:id="@+id/action_recipeListFragment_to_recipeInsertNewFragment"
app:destination="@id/recipeInsertNewFragment" />
<action
android:id="@+id/action_recipeListFragment_to_recipeDetailFragment"
app:destination="@id/recipeDetailFragment" />
</fragment>
</navigation>
activty_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".framework.presentation.MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
RecipeListFragment
@FlowPreview
@ExperimentalCoroutinesApi
class RecipeListFragment
constructor(
private val viewModelFactory: ViewModelProvider.Factory,
private val dateUtil: DateUtil
) : Fragment() {
val viewModel: RecipeListViewModel by viewModels {
viewModelFactory
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.setupChannel()
}
@ExperimentalComposeUiApi
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return ComposeView(requireContext()).apply {
setContent { BoxLayout() }
}
}
...
}
HILT DI
@ExperimentalCoroutinesApi
@FlowPreview
@Module
@InstallIn(SingletonComponent::class)
object RecipeViewModelModule {
@Singleton
@JvmStatic
@Provides
fun provideRecipeViewModelFactory(
recipeListInteractors: RecipeListInteractors,
recipeInsertNewInteractors: RecipeInsertNewInteractors,
recipeDetailInteractors: RecipeDetailInteractors,
recipeFactory: RecipeFactory,
editor: SharedPreferences.Editor,
sharedPreferences: SharedPreferences
): ViewModelProvider.Factory {
return RecipeViewModelFactory(
recipeListInteractors = recipeListInteractors,
recipeInsertNewInteractors=recipeInsertNewInteractors,
recipeDetailInteractors = recipeDetailInteractors,
recipeFactory = recipeFactory,
editor = editor,
sharedPreferences = sharedPreferences
)
}
}
@ExperimentalCoroutinesApi
@FlowPreview
@InstallIn(SingletonComponent::class)
@Module
object AppModule {
// https://developer.android.com/reference/java/text/SimpleDateFormat.html?hl=pt-br
@JvmStatic
@Singleton
@Provides
fun provideDateFormat(): SimpleDateFormat {
val sdf = SimpleDateFormat("yyyy-MM-dd hh:mm:ss a", Locale.ENGLISH)
sdf.timeZone = TimeZone.getTimeZone("UTC-7") // match firestore
return sdf
}
@JvmStatic
@Singleton
@Provides
fun provideDateUtil(dateFormat: SimpleDateFormat): DateUtil {
return DateUtil(
dateFormat
)
}
如docs所述:
All subclasses of Fragment must include a public no-argument constructor. The framework will often re-instantiate a fragment class when needed, in particular during state restore, and needs to be able to find this constructor to instantiate it. If the no-argument constructor is not available, a runtime exception will occur in some cases during state restore.
您的片段 class 应该有一个 public 无参数构造函数,以便由 Android 实例化(当进程被系统终止时)或用于导航组件等
尝试将您的 XML
从 FragmentContainerView
更改为 fragment
,如下所示:
@AndroidEntryPoint
class MainNavHostFragment : NavHostFragment() {
@Inject
lateinit var mainFragmentFactory: RecipeFragmentFactory
override fun onAttach(context: Context) {
super.onAttach(context)
childFragmentManager.fragmentFactory = mainFragmentFactory
}
}
xml
<fragment
android:id="@+id/container"
android:name="<path>.MainNavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />