【问题标题】:Android LiveData - how to reuse the same ViewModel on different activities?Android LiveData - 如何在不同的活动上重用相同的 ViewModel?
【发布时间】:2018-03-19 13:47:08
【问题描述】:

示例视图模型:

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);

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

【问题讨论】:

  • “正确”的答案是“如果你想在它们之间共享数据,它们不应该是不同的活动,而是应该交换片段”。
  • @EpicPandaForce 也许,但这不是 AndroidStudio 主/详细模板的工作方式,也不是 Android 架构蓝图
  • @Mark 那是 Android 架构蓝图和模板的缺陷。

标签: java android android-livedata


【解决方案1】:

当您调用ViewModelProviders.of(this) 时,您实际上创建/保留了一个绑定到thisViewModelStore,因此不同的活动有不同的ViewModelStore,每个ViewModelStore 使用创建ViewModel 的不同实例给定的工厂,因此您不能在不同的 ViewModelStores 中拥有相同的 ViewModel 实例。

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

例如:

public class SingletonNameViewModelFactory extends ViewModelProvider.NewInstanceFactory {


    NameViewModel t;

    public SingletonNameViewModelFactory() {
      //  t = provideNameViewModelSomeHowUsingDependencyInjection
    }

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

所以你需要制作SingletonNameViewModelFactory 单例(例如使用Dagger)并像这样使用它:

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

注意:

在不同范围内保留ViewModels 是一种反模式。强烈建议保留您的数据层对象(例如,使您的 DataSource 或 Repository 单例)并在不同范围(活动)之间保留您的数据。

阅读this文章了解详情。

【讨论】:

  • 如果您已经在单例数据层中缓存数据,那么 ViewModel 的意义何在?
  • @EpicPandaForce,我的意思是无论您的数据层如何运作,ViewModel 必须以某种方式通知您的数据更改,因此缓存您的数据层是在不同范围内维护数据的一种方法。
  • 嗯,是的,我认为你是对的,我不应该尝试那样做。但是,片段呢,让子片段观察相同的视图模型是一种好习惯吗?例如 ViewModelProviders.of(getActivity()).get(NameViewModel.class) 内部片段。
  • @user1209216 是的,详情请看这篇文章developer.android.com/topic/libraries/architecture/…
  • 如果我们使用 DaggerViewModelFactory 怎么实现?
【解决方案2】:

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

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

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
...
}

【讨论】:

  • 这应该是一个公认的答案..不违反 android 架构模式
  • 本官方教程here也推荐这种方式:LiveData in repositories: To avoid leaking ViewModels and callback hell, repositories can be observed
【解决方案3】:

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

你的 ViewModel 工厂是这样的

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 }
            }
    }
}

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

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

创建一个扩展功能以便于访问

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

【讨论】:

  • 我试过这个解决方案,但它不起作用。尝试从第二个活动更新片段中的 TextView。
  • 你能看一下我的提交吗:stackoverflow.com/questions/59887014/…
  • 像魅力一样工作!谢谢
【解决方案4】:

ViewModel 范围/生命周期与 Activity 相关联只是因为您传递给 ViewModelProvider 构造函数的 ViewModelStoreOwner 恰好是 Activity。

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

你可以

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

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

【讨论】:

    猜你喜欢
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多