Android开发:Activity无法观察ViewModel内LiveData的状态变更
问题现象
- 作为Android开发新手,在宿主Activity中观察关联ViewModel内的Boolean类型LiveData状态时,应用启动阶段能正常收到数据的初始值,后续所有状态变更(比如切换Fragment时改动的数值)都无法触发已设置的观察者。
- 预期目标:通过
MyMusicViewOn这个MutableLiveData的状态变化,对应监听ViewModel初始化、ViewModel清理、界面切换的时机。
现有实现代码
自定义ViewModel
class MyMusicViewModel : ViewModel() { private var _MyMusicViewOn = MutableLiveData<Boolean>() val MyMusicViewOn: LiveData<Boolean> get() = _MyMusicViewOn init { Timber.i("MyMusicViewModel Init Called!") setMyMusicView(true) } override fun onCleared() { super.onCleared() Timber.i("MyMusicViewModel Cleared!") setMyMusicView(false) } fun setMyMusicView(setter: Boolean) { Timber.i("MyMusicViewModel setter called! %s", setter) _MyMusicViewOn.value = setter } }
宿主Activity代码
class FullscreenActivity : AppCompatActivity() { private val viewModel: MyMusicViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.MyMusicViewOn.observe(this, Observer { MyMusicViewOn -> Timber.i("Observer called for MyMusicViewOn %s", MyMusicViewOn) }) } }
关联Fragment代码
class MyMusicFragment : Fragment() { private lateinit var viewModel: MyMusicViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val binding = DataBindingUtil.inflate<FragmentMyMusicBinding>( inflater, R.layout.fragment_my_music, container, false ) // 错误的ViewModel获取逻辑 viewModel = ViewModelProvider(this).get(MyMusicViewModel::class.java) return binding.root } override fun onResume() { super.onResume() Timber.i("MyMusicViewFragment resumed!") viewModel.setMyMusicView(true) } }
问题根因
- 问题出在Fragment获取ViewModel的传参上:代码里写的
ViewModelProvider(this)传入的this是Fragment自身,这会让系统从Fragment独有的ViewModelStore存储空间里获取(或新建)一个MyMusicViewModel实例,和宿主Activity里通过by viewModels()从Activity的ViewModelStore里拿到的ViewModel根本不是同一个对象。 - 两个独立的ViewModel实例各持有独立的
_MyMusicViewOnLiveData对象,Fragment修改自己持有实例的LiveData值时,Activity观察的是自己手里那个ViewModel的LiveData,两边数据完全不互通,自然无法触发Activity里设置的观察者回调。
修复方案
如果要实现Activity和Fragment共享同一个ViewModel实例,Fragment获取ViewModel时需要把绑定源指定为宿主Activity,两种常用写法:
- 使用
activityViewModels属性代理,直接绑定和Activity相同的ViewModel实例:private val viewModel: MyMusicViewModel by activityViewModels() - 手动创建ViewModelProvider时传入宿主Activity作为绑定源,注意不要传Fragment自己:
// 传入requireActivity()而非Fragment本身的this viewModel = ViewModelProvider(requireActivity()).get(MyMusicViewModel::class.java)
内容的提问来源于stack exchange,提问作者Fabrizio Ferrari
相关产品推荐
相关产品推荐

