Android Kotlin中lateinit viewModel未初始化错误修复求助
报错信息
Caused by: android.view.InflateException: Binary XML file line #19 in com.example.newsappkotlin:layout/activity_main: Binary XML file line #19 in com.example.newsappkotlin:layout/activity_main: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #19 in com.example.newsappkotlin:layout/activity_main: Error inflating class fragment
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property viewModel has not been initialized
相关代码
MainActivity代码
class MainActivity : AppCompatActivity() { private lateinit var binding : ActivityMainBinding private lateinit var navController: NavController lateinit var viewModel : NewsViewModel val db : ArticleDatabase? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) val newsRepository = NewsRepository(ArticleDatabase(this)) val viewModelProviderFactory = NewsViewModelProviderFactory(newsRepository) viewModel = ViewModelProvider(this,viewModelProviderFactory)[NewsViewModel::class.java] navController = Navigation.findNavController(this, R.id.newsNavHostFragment) setupWithNavController(binding.bottomNavigationView,navController) } }
Fragment代码
class BrakingNewsFragment : Fragment() { lateinit var viewModel : NewsViewModel lateinit var newsAdapter : NewsAdapter private lateinit var binding : FragmentBrakingNewsBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentBrakingNewsBinding.inflate(inflater,container,false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel = (activity as MainActivity).viewModel setUpRecyclerView() viewModel.breakingNews.observe(viewLifecycleOwner, Observer { response -> when(response){ is Resource.Succes -> { response.data?.let { newsAdapter.differ.submitList(it.articles) } } is Resource.Loading -> { println("abcabc") } } }) } private fun setUpRecyclerView(){ newsAdapter = NewsAdapter() binding.breakingNewsRecyclerView.apply { adapter = newsAdapter layoutManager = LinearLayoutManager(activity) } } }
ViewModelProviderFactory代码
class NewsViewModelProviderFactory(private val newsRepository: NewsRepository) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return NewsViewModel(newsRepository) as T } }
修复方案
核心原因
Activity的onCreate流程中,先调用setContentView加载布局,此时布局内的Fragment会被立即初始化。但你的viewModel是在setContentView之后才完成初始化的,导致Fragment在onViewCreated中访问Activity的viewModel时,该变量还未初始化,最终抛出异常。
具体修复步骤
方案1:调整Activity初始化顺序
将ViewModel的初始化逻辑移到setContentView之前,确保Fragment访问时变量已就绪:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 先初始化ViewModel val newsRepository = NewsRepository(ArticleDatabase(this)) val viewModelProviderFactory = NewsViewModelProviderFactory(newsRepository) viewModel = ViewModelProvider(this,viewModelProviderFactory)[NewsViewModel::class.java] // 再加载布局及后续操作 binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) navController = Navigation.findNavController(this, R.id.newsNavHostFragment) setupWithNavController(binding.bottomNavigationView,navController) }
方案2:Fragment通过ViewModelProvider获取共享ViewModel(推荐)
避免直接访问Activity的成员变量,改用Jetpack规范的方式获取与Activity共享的ViewModel,彻底规避初始化顺序问题:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 替换原有的viewModel赋值逻辑 viewModel = ViewModelProvider(requireActivity())[NewsViewModel::class.java] setUpRecyclerView() // 观察逻辑保持不变 viewModel.breakingNews.observe(viewLifecycleOwner, Observer { response -> when(response){ is Resource.Succes -> { response.data?.let { newsAdapter.differ.submitList(it.articles) } } is Resource.Loading -> { println("abcabc") } } }) }
方案说明
方案2更符合Jetpack架构设计原则,ViewModel的生命周期与Activity绑定,Fragment通过requireActivity()作为ViewModelStoreOwner获取的实例,会和Activity中的ViewModel保持一致,同时避免了直接依赖Activity成员变量带来的耦合与初始化风险。
内容的提问来源于stack exchange,提问作者Raşit Melih Dinçer

