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

Fragment无法观测ViewModel中LiveData变化的问题及解法验证

问题与解决方案确认

问题背景

我在NetworkDisadvantageVm中定义了MutableLiveData<Boolean>(isEquipmentTabEnabled),在NetWorkDisadvantageDetailFragment(Fragment1)中调用updateIsEquipmentTab方法更新该LiveData,在NetworkDisadvantageFragment(Fragment2)中对其进行观测。但Fragment1调用更新方法后,Fragment2无法观测到变化,调试发现LiveData的观察者列表为空,且Fragment1处于不可见状态(因ViewPager切换导致)。

我将两个Fragment中获取ViewModel的代码从:

mViewModel = ViewModelProviders.of(this, mViewModelFactory)
            .get(NetworkDisadvantageVm::class.java)

修改为:

mViewModel = ViewModelProviders.of(requireActivity(), mViewModelFactory)
            .get(NetworkDisadvantageVm::class.java)

请问这是否是正确的解决方式?


解决方案确认

这个修改是完全正确的,核心原因如下:

原问题本质:ViewModel作用域不匹配

当你使用ViewModelProviders.of(this)时,this指向当前Fragment实例,这意味着每个Fragment都会创建并持有独立的ViewModel实例。Fragment1更新的是自己ViewModel里的isEquipmentTabEnabled,而Fragment2观测的是另一个完全独立的ViewModel实例中的LiveData,自然无法收到更新通知——这也是你看到观察者列表为空的原因:Fragment2的ViewModel里的LiveData根本没有被Fragment1关联的观察者订阅。

改为requireActivity()后,ViewModel的作用域变为Activity级别,两个Fragment会共享同一个NetworkDisadvantageVm实例。此时Fragment1更新的LiveData和Fragment2观测的是同一个对象,LiveData的变化就能正确传递到Fragment2的观察者中。


额外优化建议

  1. 优化LiveData的暴露方式
    为了保证数据的单向流动,避免外部直接修改LiveData,建议在ViewModel中将MutableLiveData私有化,对外暴露不可变的LiveData:

    private val _isEquipmentTabEnabled = MutableLiveData<Boolean>(false)
    val isEquipmentTabEnabled: LiveData<Boolean>
        get() = _isEquipmentTabEnabled
    
    fun updateIsEquipmentTab(condition: Boolean) {
        _isEquipmentTabEnabled.value = condition
    }
    
  2. 更精准的ViewModel作用域(可选)
    如果两个Fragment的生命周期和Activity不完全绑定(比如Fragment2是DialogFragment,关闭后不需要保留ViewModel数据),可以将ViewModel的作用域绑定到Fragment2(父Fragment),Fragment1通过requireParentFragment()获取ViewModel:

    • Fragment2中获取ViewModel:
      mViewModel = ViewModelProviders.of(this, mViewModelFactory)
                  .get(NetworkDisadvantageVm::class.java)
      
    • Fragment1中获取ViewModel:
      mNetWorkDisadvantageViewModel = ViewModelProviders.of(requireParentFragment(), mViewModelFactory)
                  .get(NetworkDisadvantageVm::class.java)
      

    这样ViewModel的生命周期会和Fragment2绑定,比Activity级别更精准。

  3. 修复ViewPager索引越界问题
    在Fragment2的onCreateView中,你设置了binding.viewPager.currentItem = 2,但你的Fragment列表frgs只有2个元素(索引0和1),这会导致索引越界异常,建议修改为currentItem = 0或currentItem = 1。


相关代码(修正格式后)

ViewModel代码

class NetworkDisadvantageVm @Inject constructor(
    var applicationn: Application,
    var networkDisadvantageManager: NetworkDisadvantageManager,
    var goodsItemManager: GoodsItemManager,
    var gson: Gson
) : BaseVm(applicationn) {
    private var isEquipmentTabEnabled = MutableLiveData<Boolean>(false)

    val getIsEquipmentTabEnabled: MutableLiveData<Boolean>
        get() = isEquipmentTabEnabled

    fun updateIsEquipmentTab(condition: Boolean) {
        isEquipmentTabEnabled.value = condition
    }

    override fun handleUserAction(action: String, data: Any) {
        when (action) {
            
        }
    }

    companion object {
        const val ACTION_UPDATE_NETWORK_DISADVANAGE = "ACTION_UPDATE_NETWORK_DISADVANAGE"
        const val ACTION_ERROR_UPDATE_NETWORK_DISADVANAGE = "ACTION_ERROR_UPDATE_NETWORK_DISADVANAGE"
    }
}

Fragment1(NetWorkDisadvantageDetailFragment)代码

class NetWorkDisadvantageDetailFragment : BaseFragment() {
    private var _binding: NabNetworkDisadvantageFragmentBinding? = null
    private lateinit var mNetWorkDisadvantageViewModel: NetworkDisadvantageVm
    private lateinit var mission: Mission

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mNetWorkDisadvantageViewModel = ViewModelProviders.of(this, mViewModelFactory)
            .get(NetworkDisadvantageVm::class.java)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = NabNetworkDisadvantageFragmentBinding.inflate(inflater, container, false)
        val view = binding.root

        binding.datetext.text = GeneralUtil.convertPersianDate(GeneralUtil.Now())
        binding.datetext.tag = GeneralUtil.getDateFormat().format(GeneralUtil.Now())
        binding.takenAction.setOnClickListener { onRadioButtonClicked(it) }
        binding.inProgressReferTo.setOnClickListener { onRadioButtonClicked(it) }
        binding.tempActionReferTo.setOnClickListener { onRadioButtonClicked(it) }
        binding.CustomerAbsence.setOnClickListener { onRadioButtonClicked(it) }
        binding.notSeen.setOnClickListener { onRadioButtonClicked(it) }
        binding.wrongAddressNoActionTaken.setOnClickListener { onRadioButtonClicked(it) }
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        mNetWorkDisadvantageViewModel.getIsEquipmentTabEnabled.observe(viewLifecycleOwner) { isEnabled ->
            Log.e("test","tet")
        }
    }

    private val binding get() = _binding!!

    private fun onRadioButtonClicked(view: View) {
        val checked = (view as RadioButton).isChecked

        when (view.id) {
            binding.takenAction.id -> {
                if (checked) {
                    mNetWorkDisadvantageViewModel.updateIsEquipmentTab(true)
                    binding.inProgressReferTo.isChecked = false
                }
            }

            binding.inProgressReferTo.id -> {
                if (checked) {
                    mNetWorkDisadvantageViewModel.updateIsEquipmentTab(false)
                    binding.notSeen.isChecked = false
                }
            }

            binding.tempActionReferTo.id -> {
                if (checked) {
                    mNetWorkDisadvantageViewModel.updateIsEquipmentTab(false)
                    binding.inProgressReferTo.isChecked = false
                }
            }

            binding.CustomerAbsence.id -> {
                if (checked) {
                    mNetWorkDisadvantageViewModel.updateIsEquipmentTab(false)
                    binding.takenAction.isChecked = false
                }
            }

            binding.notSeen.id -> {
                if (checked) {
                    mNetWorkDisadvantageViewModel.updateIsEquipmentTab(false)
                    binding.inProgressReferTo.isChecked = false
                }
            }

            binding.wrongAddressNoActionTaken.id -> {
                if (checked) {
                    mNetWorkDisadvantageViewModel.updateIsEquipmentTab(false)
                    binding.takenAction.isChecked = false
                    binding.tempActionReferTo.isChecked = false
                    binding.inProgressReferTo.isChecked = false
                    binding.CustomerAbsence.isChecked = false
                    binding.notSeen.isChecked = false
                }
            }
        }
    }

    companion object {
        fun newInstance(mission: Mission) = NetWorkDisadvantageDetailFragment().apply {
            this.mission = mission
        }
    }
}

Fragment2(NetworkDisadvantageFragment)代码

class NetworkDisadvantageFragment: BaseFullScreenDialogFragment() {
    private lateinit var mViewModel: NetworkDisadvantageVm
    private var _binding: NabTabLayoutBinding? = null
    private lateinit var mission: Mission

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mViewModel = ViewModelProviders.of(this, mViewModelFactory)
            .get(NetworkDisadvantageVm::class.java)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = NabTabLayoutBinding.inflate(inflater, container, false)
        val view = binding.root
        val networkDisadvantage= NetWorkDisadvantageDetailFragment.newInstance(mission)
        val goodsItem= GoodsItemFragment.newInstance(mission)
        val title = listOf("اجناس", "فرم اقدام")
        val frgs = listOf(goodsItem, networkDisadvantage)
        val adaptor= FragmentAdaptor(requireActivity(), frgs as List<Fragment>, title)
        binding.viewPager.adapter = adaptor
        // 修复索引越界问题,改为0或1
        binding.viewPager.currentItem = 1
        TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
            tab.text = title[position]
        }.attach()

        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        mViewModel.getIsEquipmentTabEnabled.observe(viewLifecycleOwner) { isEnabled ->
            val tabStrip = binding.tabLayout.getChildAt(0) as LinearLayout
            val tabView = tabStrip.getChildAt(0)
            tabView.isEnabled = isEnabled
            tabView.isClickable = isEnabled
        }
    }

    private val binding get() = _binding!!
    companion object{
        fun newInstance(mission: Mission) = NetworkDisadvantageFragment().apply {
            this.mission = mission
        }
    }
}

ViewModel提供者代码

package com.edsab.takrim.injection.component

@Subcomponent
interface ViewModelSubComponent {
    @Subcomponent.Builder
    interface Builder {
        fun build(): ViewModelSubComponent
    }
    
    fun NetworkDisadvantageVm(): NetworkDisadvantageVm
    fun NetWorkDisadvantageDetailVm(): NetWorkDisadvantageDetailVm
}

@Singleton
class ViewModelFactory @Inject constructor(viewModelSubComponent: ViewModelSubComponent) : ViewModelProvider.Factory {
    private val creators: ArrayMap<Class<*>, Callable<out ViewModel>> = ArrayMap()

    init {
        creators[NetworkDisadvantageVm::class.java] = Callable { viewModelSubComponent.NetworkDisadvantageVm() }
        creators[NetWorkDisadvantageDetailVm::class.java] = Callable { viewModelSubComponent.NetWorkDisadvantageDetailVm() }
    }

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        var creator = creators.get(modelClass)
        if (creator == null) {
            creators.forEach { entry ->
                if (modelClass.isAssignableFrom(entry.key)) {
                    creator = entry.value
                }
            }
        }
        if (creator == null) {
            throw IllegalArgumentException("Unknown model class $modelClass")
        }
        try {
            return creator?.call() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

内容的提问来源于stack exchange,提问作者MojtabaValizadeh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 09:48:11