Android LiveData - 如何在不同的活动中重用相同的 ViewModel?

Android LiveData - how to reuse the same ViewModel on different activities?

提问人:user1209216 提问时间:3/19/2018 最后编辑:ColdFireuser1209216 更新时间:2/14/2022 访问量:39087

问:

示例 ViewModel:

public class NameViewModel extends ViewModel {
    // Create a LiveData with a String
    private MutableLiveData<String> mCurrentName;

    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData<>();
        }
        return mCurrentName;
    }

}

主要活动:

mModel = ViewModelProviders.of(this).get(NameViewModel.class);

// Create the observer which updates the UI.
final Observer<String> nameObserver = textView::setText;

// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
mModel.getCurrentName().observe(this, nameObserver);

我想调用第二个活动并使 MainActivity 接收更改。这可能吗?mModel.getCurrentName().setValue(anotherName);

java android-livedata

评论

3赞 EpicPandaForce 3/19/2018
“正确”的答案是“如果你想在它们之间共享数据,它们不应该是不同的活动,你应该换掉片段”。
0赞 Mark 7/10/2018
@EpicPandaForce也许,但这不是 AndroidStudio 主/从模板的工作方式,也不是 Android 架构蓝图的工作方式
0赞 EpicPandaForce 1/26/2019
@Mark这是Android架构蓝图和模板的缺陷。
0赞 Levon Petrosyan 6/11/2019
请看这个 stackoverflow.com/questions/56521969/...

答:

57赞 Saeed Masoumi 3/19/2018 #1

当你调用 时,你实际上创建/保留了绑定到 的 ,所以不同的 Activity 有不同的,并且每个 Activity 都使用给定的工厂创建不同的 a 实例,因此你不能在不同的 s 中拥有相同的 a 实例。ViewModelProviders.of(this)ViewModelStorethisViewModelStoreViewModelStoreViewModelViewModelViewModelStore

但是,您可以通过传递自定义 ViewModel 工厂的单个实例来实现此目的,该实例充当单例工厂,因此它将始终在不同的活动之间传递相同的实例。ViewModel

例如:

public class SingletonNameViewModelFactory extends ViewModelProvider.NewInstanceFactory {


    NameViewModel t;

    public SingletonNameViewModelFactory() {
      //  t = provideNameViewModelSomeHowUsingDependencyInjection
    }

    @Override
    public NameViewModel create(Class<NameViewModel> modelClass) {
        return t;
    }
}

所以你需要做的是制作单例(例如使用匕首)并像这样使用它:SingletonNameViewModelFactory

mModel = ViewModelProviders.of(this,myFactory).get(NameViewModel.class);

注意:

在不同作用域之间保留 s 是一种反模式。强烈建议保留数据层对象(例如,将 DataSource 或存储库设置为单一实例),并在不同范围(活动)之间保留数据。ViewModel

有关详细信息,请阅读本文

评论

3赞 EpicPandaForce 3/19/2018
如果您已经在单例数据层中缓存数据,那么 ViewModel 的意义何在?
0赞 Saeed Masoumi 3/19/2018
@EpicPandaForce,我的意思是,无论您的数据层如何操作,都必须以某种方式通知您的数据更改,因此缓存数据层是在不同范围内维护数据的一种方法。ViewModel
0赞 user1209216 3/20/2018
嗯,是的,我认为你是对的,我不应该试图这样做。但是,片段呢,让子片段观察相同的视图模型是好的做法吗?例如,在片段内部。ViewModelProviders.of(getActivity()).get(NameViewModel.class)
1赞 Saeed Masoumi 3/20/2018
@user1209216 是的,为什么不看这篇文章了解详情 developer.android.com/topic/libraries/architecture/......
28赞 TotoliciCristian 3/19/2018 #2

当使用作为生命周期所有者传递的 ViewModelProviders 获取视图模型时,这将为该活动提供视图模型。在第二个活动中,您将获得该 ViewModel 的不同实例,这次是第二个 Activity。第二个模型将具有第二个实时数据。

您可以做的是将数据维护在不同的层中,例如存储库,这可能是一个单一实例,这样您就可以使用相同的视图模型。

enter image description here

public class NameViewModel extends ViewModel {
    // Create a LiveData with a String
    private MutableLiveData<String> mCurrentName;

    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = DataRepository.getInstance().getCurrentName();
        }
        return mCurrentName;
    }
}

//SingleTon
public class DataRepository     

    private MutableLiveData<String> mCurrentName;

    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData<>();
        }
        return mCurrentName;
    }
//Singleton code
...
}

评论

2赞 alierdogan7 6/17/2021
此官方教程中也推荐此方法:LiveData in repositories: To avoid leaking ViewModels and callback hell, repositories can be observed
4赞 Amir Raza 11/26/2019 #3

只需创建 ViewModel 的实例,在本例中为 NameViewModel

您的 ViewModel Factory 就像

class ViewModelFactory : ViewModelProvider.NewInstanceFactory() {

    override fun <T : ViewModel?> create(modelClass: Class<T>) =
        with(modelClass){
            when {
                isAssignableFrom(NameViewModel::class.java) -> NameViewModel.getInstance()
                else -> throw IllegalArgumentException("Unknown viewModel class $modelClass")
            }
        } as T


    companion object {
        private var instance : ViewModelFactory? = null
        fun getInstance() =
            instance ?: synchronized(ViewModelFactory::class.java){
                instance ?: ViewModelFactory().also { instance = it }
            }
    }
}

还有您的 ViewModel

class NameViewModel : ViewModel() {

    //your liveData objects and many more...

    companion object {
        private var instance : NameViewModel? = null
        fun getInstance() =
            instance ?: synchronized(NameViewModel::class.java){
                instance ?: NameViewModel().also { instance = it }
            }
    }
}

现在,您可以使用 ViewModel 的相同实例在任何活动中使用ViewModelProviders

ViewModelProviders.of(this, ViewModelFactory.getInstance()).get(NameViewModel::class.java)

创建扩展功能,方便访问

fun <T : ViewModel> AppCompatActivity.getViewModel(viewModelClass: Class<T>) =
    ViewModelProviders.of(this, ViewModelFactory.getInstance()).get(viewModelClass)

评论

0赞 Samuel 1/24/2020
我尝试了这个解决方案,但它不起作用。尝试从第二个活动更新片段中的 TextView。
1赞 Darrell Burk 2/13/2022 #4

ViewModel 范围/生命周期与活动相关联,仅仅是因为传递给 ViewModelProvider 构造函数的 ViewModelStoreOwner 恰好是该活动。

由于您需要提供 ViewModelStoreOwner,因此您可以轻松地提供具有更长生命周期的 ViewModelStoreOwner,例如应用程序。

您可以

  1. 提供你自己的 Application 子类,并使其实现 ViewModelStoreOwner(并具有 ViewModelStore)
  2. 在 ViewModelProvider 构造函数调用中,传递 activity.application 而不是 activity。

这将导致 ViewModelProvider 与应用程序级 ViewModelStore 交互,从而允许您创建具有应用程序范围的 ViewModel 实例。