You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行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:

  1. 修改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>
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 16:25:32