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

Android字体大小变更致Activity重建,Fragment上下文及ViewModel异常问题

问题场景

当Activity被重建(比如修改系统字体大小触发重建)并重新初始化ViewPager2时,调用Fragment的函数会出现requireContext()、requireView()为空,且ViewModel未初始化的情况。

相关代码

Activity代码

var pagerAdapter: NewPagerAdapter? = null
fun initView(){
    val vpContent = findViewById<ViewPager2>(R.id.vp_content)
    val fragmentList = ArrayList<ParentFragment>()
    fragmentList.add(FristFragment())
    fragmentList.add(SecondFragment())
    fragmentList.add(ThirdFragment())
    pagerAdapter = NewPagerAdapter(fragmentList, supportFragmentManager, lifecycle)
    vpContent.adapter = pagerAdapter

    val btnRefresh = findViewById<Button>(R.id.btn_scm_refresh)
    btnRefresh.setOnClickListener {
        (pagerAdapter)!!.allRefreshPage()
    }
}

Adapter代码

override fun getItemCount(): Int {
    return fragmentList.size
}

override fun createFragment(position: Int): Fragment {
    return fragmentList[position]
}

fun allRefreshPage(context: Context){
    for(i in 0 until fragmentList.size){
        fragmentList[i].refreshData()
    }
}

ParentFragment代码

abstract class ParentFragment : Fragment() {
    abstract fun refreshData()
}

ChildFragment代码

private lateinit var viewModel: NewViewModel
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    viewModel = ViewModelProvider(this, ViewModelFactory())[NewViewModel::class.java]
}


override fun refreshData() {
    if(context == null || view == null) {
        Log.w(TAG, "refreshData: have not context or view")
        return
    }
    viewModel.initAppList(requireContext(), requireView())
}

问题与解答

1. 该问题产生的原因是什么?

  • Fragment实例管理错误:Activity重建时,你手动创建了新的Fragment实例传入Adapter,但系统已经自动恢复了之前的Fragment实例,此时Adapter中保存的旧实例已与Activity、ViewPager2失去关联,处于detached状态,自然没有可用的Context和View。
  • 生命周期与初始化时机不匹配:旧Fragment实例的onCreate不会在重建时再次触发,导致ViewModel未初始化;即使是新实例,调用refreshData时可能还未执行到onCreateView或onViewCreated,View尚未创建,Context也未完成绑定。
  • ViewPager2的Fragment回收机制:ViewPager2默认会销毁非当前页的Fragment(根据offscreenPageLimit配置),重建后如果Fragment还未被初始化或attach到Activity,调用其方法就会出现空值。

2. 如何确保不会出现上述空值情况?

  • 让系统自动管理Fragment实例:修改Adapter,去掉外部传入的fragmentList,改为在createFragment中动态创建Fragment,系统会自动处理重建时的实例恢复:
    class NewPagerAdapter(fm: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fm, lifecycle) {
        override fun getItemCount(): Int = 3
    
        override fun createFragment(position: Int): Fragment {
            return when(position) {
                0 -> FristFragment()
                1 -> SecondFragment()
                else -> ThirdFragment()
            }
        }
    
        fun allRefreshPage() {
            // 通过FragmentManager获取已attach的有效Fragment实例
            val fragments = fragmentManager.fragments.filterIsInstance<ParentFragment>()
            fragments.forEach { it.refreshData() }
        }
    }
    
  • 增加Fragment状态校验:在Fragment的refreshData方法中,先判断自身是否已attach到Activity、View是否已创建:
    override fun refreshData() {
        if (!isAdded || view == null) {
            Log.w(TAG, "refreshData: fragment not attached or view not created")
            return
        }
        viewModel.initAppList(requireContext(), requireView())
    }
    
  • 调整ViewModel绑定范围(可选):如果需要ViewModel在Activity重建后保留数据,或确保其提前初始化,可以将ViewModel绑定到Activity的ViewModelStore:
    // 在ChildFragment中修改ViewModel初始化代码
    viewModel = ViewModelProvider(requireActivity(), ViewModelFactory())[NewViewModel::class.java]
    
  • 延迟调用时机:在Activity的onResume方法中触发刷新,或通过LiveData通知Fragment主动执行刷新,避免在Activity重建未完成时直接调用Fragment方法。

3. 从Activity调用Fragment的函数还有哪些其他实现方式?

  • LiveData事件通知:在Activity的ViewModel中定义LiveData,Fragment观察该LiveData,Activity通过发送事件触发Fragment执行方法:
    // Activity的ViewModel
    class ActivityViewModel : ViewModel() {
        private val refreshTrigger = MutableLiveData<Event<Unit>>()
        fun triggerRefresh() = refreshTrigger.postValue(Event(Unit))
        fun getRefreshTrigger() = refreshTrigger
    }
    
    // ChildFragment中观察事件
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val activityViewModel = ViewModelProvider(requireActivity())[ActivityViewModel::class.java]
        activityViewModel.getRefreshTrigger().observe(viewLifecycleOwner) { event ->
            event.contentIfNotHandled?.let {
                refreshData()
            }
        }
    }
    
    // Activity中触发刷新
    activityViewModel.triggerRefresh()
    
  • FragmentManager查找实例:直接通过FragmentManager获取已attach的Fragment实例并调用方法:
    // Activity中调用
    val targetFragment = supportFragmentManager.findFragmentByTag("f0") as? FristFragment
    targetFragment?.refreshData()
    
    注:ViewPager2的Fragment标签格式通常为f${position},可通过日志或源码确认具体格式
  • 接口回调(弱引用避免泄漏):定义回调接口,Fragment实现接口,Activity持有接口的弱引用并调用方法:
    // 定义回调接口
    interface RefreshCallback {
        fun onRefresh()
    }
    
    // ChildFragment中绑定回调
    override fun onAttach(context: Context) {
        super.onAttach(context)
        val callback = WeakReference<RefreshCallback>(context as? RefreshCallback)
    }
    
    // Activity实现RefreshCallback接口,调用对应Fragment的回调方法
    

内容的提问来源于stack exchange,提问作者Jun.Grammer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 21:36:01