Fragment中onCreateView首次与后续调用区分及视图复用标准方案问询
嘿,这个问题确实是Fragment开发里不少人都会卡壳的点,我来给你梳理下官方推荐的内置解决方案,帮你告别那些临时的权宜之计~
先搞懂核心问题的本质
首先得明确两个问题的根源:
为什么Fragment入返回栈时
onSavedInstanceState不调用?
当Fragment被加入返回栈时,系统只是把它从界面上移除(触发onStop、onDestroyView),但Fragment实例本身并没有被销毁,所以不会触发onSavedInstanceState——这个方法只有在系统可能销毁Fragment实例时才会调用(比如内存不足、配置变化)。为什么不能在
onCreate里实例化视图,每次显示都要重建?
Fragment的视图生命周期和实例生命周期是分离的:入返回栈时视图会被销毁(onDestroyView),但实例保留;当从返回栈恢复时,会重新调用onCreateView创建视图。这是官方的设计,目的是保证视图能适配当前的容器上下文和状态。
官方标准解决方案:用ViewModel搞定一切
这是谷歌推荐的最优解,完全不需要自己加布尔值判断或者其他hack手段,完美适配Fragment的生命周期:
ViewModel的生命周期独立于Fragment的视图,不管Fragment入返回栈、经历配置变化,ViewModel都会保留到Fragment实例真正被销毁为止。你可以把需要保存的状态、数据都存在ViewModel里,视图重建后直接从ViewModel取数据填充就行。
举个简单的例子:
class MyFragment : Fragment() { // 通过viewModels()获取与当前Fragment绑定的ViewModel实例 private val myViewModel: MyViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.my_fragment_layout, container, false) // 视图重建后,直接从ViewModel取数据填充 val textView = view.findViewById<TextView>(R.id.content_text) textView.text = myViewModel.savedContent // 设置视图交互,比如按钮点击 view.findViewById<Button>(R.id.save_btn).setOnClickListener { myViewModel.savedContent = textView.text.toString() } return view } } // 自定义ViewModel,专门存需要保留的数据 class MyViewModel : ViewModel() { var savedContent: String = "" }
其他可选的内置处理方式
如果有些场景ViewModel覆盖不到,还有这些官方支持的思路:
- 替代
onSavedInstanceState的状态保存时机:如果是需要持久化的状态(比如用户输入),可以在onStop或onDestroyView里把数据存到ViewModel、SharedPreferences或者Room数据库里,不用依赖onSavedInstanceState。 - 谨慎复用视图(不推荐但可行):如果你确实想避免重建视图,可以自己缓存视图实例,但要注意内存管理(比如移除父容器、避免内存泄漏)。不过官方不推荐这种方式,因为容易导致视图状态和Fragment状态不一致的问题。
总结
不用再找那些临时方案啦,ViewModel就是官方给出的标准解决方案——它既解决了返回栈时的状态保存问题,也能让你在视图重建后快速恢复数据,完全符合Fragment的生命周期设计。
内容的提问来源于stack exchange,提问作者Pier Giorgio Misley

