运行MVVM新闻App时遇Error inflating class Fragment错误求助
问题:Error inflating class Fragment 引发lateinit属性未初始化异常
运行自研MVVM架构新闻App时触发Error inflating class Fragment错误,报错根源为kotlin.UninitializedPropertyAccessException:NewsActivity中的lateinit属性viewModel未初始化,该异常在BreakingNewsFragment的onCreateView方法调用NewsActivity的viewModel属性时抛出。
报错堆栈
Caused by: android.view.InflateException: Binary XML file line #25 in com.androiddevs.mvvmnewsapp:layout/activity_news: Binary XML file line #25 in com.androiddevs.mvvmnewsapp:layout/activity_news: Error inflating class fragment Caused by: android.view.InflateException: Binary XML file line #25 in com.androiddevs.mvvmnewsapp:layout/activity_news: Error inflating class fragment Caused by: kotlin.UninitializedPropertyAccessException: lateinit property viewModel has not been initialized at com.androiddevs.mvvmnewsapp.ui.NewsActivity.getViewModel(NewsActivity.kt:15) at com.androiddevs.mvvmnewsapp.ui.fragments.BreakingNewsFragment.onCreateView(BreakingNewsFragment.kt:29)
问题原因
- 布局中通过
<fragment>标签声明NavHostFragment时,系统会在Activity执行setContentView()的阶段就实例化该Fragment,并调用其生命周期方法(如onCreateView)。 - 原NewsActivity中
viewModel的初始化代码写在setContentView()之后,导致Fragment尝试获取Activity的viewModel时,该属性还未完成初始化,从而抛出未初始化异常。
解决方案
方案1:调整ViewModel初始化顺序
将Activity中viewModel的初始化逻辑移到setContentView()之前,确保Fragment实例化时viewModel已经准备完毕:
class NewsActivity : AppCompatActivity() { lateinit var viewModel: NewsViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 优先初始化ViewModel val newsRepository = NewsRepository(ArticleDatabase(this)) val viewModelProviderFactory = NewsViewModelProviderFactory(newsRepository) viewModel = ViewModelProvider(this, viewModelProviderFactory).get(NewsViewModel::class.java) // 再加载布局 setContentView(R.layout.activity_news) val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView) val newsNavHostFragment = findViewById<View>(R.id.newsNavHostFragment) bottomNavigationView.setupWithNavController(newsNavHostFragment.findNavController()) } }
方案2:Fragment直接获取Activity级ViewModel(推荐)
无需强转Activity获取viewModel,通过ViewModelProvider指定Activity作为ViewModelStoreOwner直接获取,既避免强依赖,也符合MVVM实践:
class BreakingNewsFragment:Fragment() { lateinit var binding: FragmentBreakingNewsBinding lateinit var viewModel: NewsViewModel lateinit var newsAdapter:NewsAdapter val TAG = "BreakingNewsFragment" override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentBreakingNewsBinding.inflate(layoutInflater,container,false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupRecyclerView() // 直接从Activity的ViewModelStore获取实例 val factory = NewsViewModelProviderFactory(NewsRepository(ArticleDatabase(requireContext()))) viewModel = ViewModelProvider(requireActivity(), factory)[NewsViewModel::class.java] viewModel.breakingNews.observe(viewLifecycleOwner, Observer { response -> when(response){ is Resource.Success -> { hideProgressBar() response.data?.let { newsResponse -> newsAdapter.differ.submitList(newsResponse.articles) } } is Resource.Error -> { hideProgressBar() response.message?.let { message -> Log.e(TAG, "An error occured: $message") } } is Resource.Loading -> { showProgressBar() } } }) } private fun hideProgressBar(){ binding.paginationProgressBar.visibility = View.INVISIBLE } private fun showProgressBar(){ binding.paginationProgressBar.visibility = View.VISIBLE } private fun setupRecyclerView(){ newsAdapter = NewsAdapter() binding.rvBreakingNews.apply { adapter = newsAdapter layoutManager = LinearLayoutManager(activity) } } }
方案3:动态添加NavHostFragment(不推荐)
将布局中的<fragment>标签替换为FrameLayout,在Activity初始化完成后通过代码动态添加NavHostFragment:
- 修改Activity布局:
<FrameLayout android:id="@+id/flFragment" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottomNavigationView" android:layout_width="match_parent" android:layout_height="56dp" app:menu="@menu/bottom_navigation_menu" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
- 修改NewsActivity代码:
class NewsActivity : AppCompatActivity() { lateinit var viewModel: NewsViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_news) // 初始化ViewModel val newsRepository = NewsRepository(ArticleDatabase(this)) val viewModelProviderFactory = NewsViewModelProviderFactory(newsRepository) viewModel = ViewModelProvider(this, viewModelProviderFactory).get(NewsViewModel::class.java) // 动态添加NavHostFragment if (savedInstanceState == null) { val navHostFragment = NavHostFragment.create(R.navigation.news_nav_graph) supportFragmentManager.beginTransaction() .replace(R.id.flFragment, navHostFragment) .setPrimaryNavigationFragment(navHostFragment) .commit() } val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView) val navController = supportFragmentManager.findFragmentById(R.id.flFragment)!!.findNavController() bottomNavigationView.setupWithNavController(navController) } }
内容的提问来源于stack exchange,提问作者robson idongesit samuel
相关产品推荐
相关产品推荐

