Android 架构组件 ViewModel 可以从多个 LiveData 返回模型组成一个对象吗?

Can an Android Architecture Components ViewModel compose an object from multiple LiveData returning models?

我一直无法创建一个 Android 架构组件 ViewModel,它将多个 LiveData 模型组合成一个 LiveData class 供我的 Fragment 观察。我想从 Fragment 中隐藏模型细节,并通过单个模型响应外部数据变化。

问题是我需要 ViewModel 来观察模型变化,但 ViewModel 不是 LifecycleOwner,所以它无法观察。因为我不想将 LiveData 对象传递给 UI,所以我被卡住了。

这可能吗?我是否需要为我的模型放弃 LiveData 并求助于不同的观察模式/工具?

编辑: 添加伪代码。我实际的 classes 更加复杂和冗长。我希望我的意图是可以理解的。

// OneDataModel.kt
class oneDataModel {
    val oneDataElement = ""
}

// AnotherDataModel.kt
class anotherDataModel {
    val anotherDataElement = 19
}

// OneDataRepository.kt
class OneDataRepository {
    val oneDataSet = MutableLiveData<MutableList<oneDataModel>>()

    private val dataListener = object: ChildEventListener {
        override fun onChildAdded(snapshot: DataSnapshot, p1: String?) {
            val newChild = snapshot.getValue(oneDataModel::class.java)
            if (newChild != null) {
                oneDataSet.value?.add(newChild)
            }
        }
    }

    init {
        oneDataSet.value = mutableListOf<oneDataModel>()
        OneNetworkDataTable.addListener(dataListener)
    }
}

// AnotherDataRepository.kt
class AnotherDataRepository {
    var anotherDataSet = MutableLiveData<MutableList<anotherDataModel>>()

    private val dataListener = object: ChildEventListener {
        override fun onChildAdded(snapshot: DataSnapshot, p1: String?) {
            val newChild = snapshot.getValue(anotherDataModel::class.java)
            if (newChild != null) {
                anotherDataSet.value?.add(newChild)
            }
        }
    }

    init {
        anotherDataSet.value = mutableListOf<anotherDataModel>()
        AnotherNetworkDataTable.addListener(dataListener)
    }
}

// ComposedViewModel.kt
class ComposedViewModel: ViewModel() {
    class ComposedItem {
        var dataName: String = ""   // From OneDataRepository items
        var dataValue: Int = -1     // From AnotherDataRepository items
    }
    var publishedDataSet = MutableLiveData<MutableList<ComposedItem>>()

    //***
    //*** WHAT GOES HERE? HOW DO I LISTEN TO EACH OF THE DATA REPOSITORIES AND BUILD UP COMPOSED
    //*** ITEMS FOR THE UI?
    //***
}

// MyFragment.kt
class MyFragment : Fragment() {
    private val composedViewModel: ComposedViewModel by lazy { ViewModelProviders.of(activity).get(ComposedViewModel::class.java) }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_feed, container, false)

        recyclerView.adapter = UIAdapter

        composedViewModel.publishedDataSet.observe(this, Observer {
            UIAdapter.notifyDataSetChanged()
        })

        return view
    }
}

希望我的作品能解决您的问题。

我之前做了一个示例项目,现在将另一个假的实时数据集成到这个视图模型中。 Difference of the file

使用MediatorLiveData class将多个数据源合并为一个。

FeedEntryListViewModel.java

public class FeedEntryListViewModel extends ViewModel {

  //list all your live data here
  private LiveData<List<FeedEntry>> feedEntries = new MutableLiveData<>();
  private MutableLiveData<String> userId = new MutableLiveData<>();
  //show your composite model here
  private MediatorLiveData<CompositeModel> compositeModelLiveData;

  //list all your repository
  private FeedEntryRepository feedEntryDBRepository;

  /*
  The complete model to show the data
   */
  public static class CompositeModel {

    String userId = "SystemId";
    private List<FeedEntry> feedEntries = new ArrayList<>();


    public String getUserId() {
      return userId;
    }

    public void setUserId(String userId) {
      this.userId = userId;
    }

    public List<FeedEntry> getFeedEntries() {
      return feedEntries;
    }

    public void setFeedEntries(
        List<FeedEntry> feedEntries) {
      this.feedEntries = feedEntries;
    }


  }

  public FeedEntryListViewModel(
      FeedEntryRepository feedEntryDBRepository) {
    this.feedEntryDBRepository = feedEntryDBRepository;
    this.feedEntries = feedEntryDBRepository.getAll();
    this.compositeModelLiveData = new MediatorLiveData<>();
    this.compositeModelLiveData.addSource(feedEntries, data ->
    {
      CompositeModel compositeModel = compositeModelLiveData.getValue();
      compositeModel.setFeedEntries(data);
      compositeModelLiveData.postValue(compositeModel);
    });
    this.compositeModelLiveData.addSource(userId, data -> {
      CompositeModel compositeModel = compositeModelLiveData.getValue();
      compositeModel.setUserId(data);
      compositeModelLiveData.postValue(compositeModel);
    });
    //initialize the composite model to avoid NullPointerException
    this.compositeModelLiveData.postValue(new CompositeModel());
  }

  public void loadUserId(String userId) {
    this.userId.setValue(userId);
  }

  public LiveData<List<FeedEntry>> getFeedEntrys() {
    return feedEntryDBRepository.getAll();
  }

  public LiveData<CompositeModel> getCompositeEntrys() {
    return compositeModelLiveData;
  }

  public LiveData<List<FeedEntry>> insert(FeedEntry... feedEntries) {
    feedEntryDBRepository.insertAll(feedEntries);
    return feedEntryDBRepository.getAll();
  }

  public void delete(FeedEntry feedEntry) {
    feedEntryDBRepository.delete(feedEntry);
  }


  public int update(FeedEntry feedEntry) {
    return feedEntryDBRepository.update(feedEntry);
  }

}

在Activity中,你仍然可以通过语句

得到复合
viewModel.getCompositeEntrys().observe(this, entries -> {...});

View Model Constructor 添加实时数据并绑定到复合实时数据

 public FeedEntryListViewModel(
      FeedEntryRepository feedEntryDBRepository) {
    this.feedEntryDBRepository = feedEntryDBRepository;
    this.feedEntries = feedEntryDBRepository.getAll();
    this.compositeModelLiveData = new MediatorLiveData<>();
    this.compositeModelLiveData.addSource(feedEntries, data ->
    {
      CompositeModel compositeModel = compositeModelLiveData.getValue();
      compositeModel.setFeedEntries(data);
      compositeModelLiveData.postValue(compositeModel);
    });
    this.compositeModelLiveData.addSource(userId, data -> {
      CompositeModel compositeModel = compositeModelLiveData.getValue();
      compositeModel.setUserId(data);
      compositeModelLiveData.postValue(compositeModel);
    });
    //initialize the composite model to avoid NullPointerException
    this.compositeModelLiveData.postValue(new CompositeModel());
  }

我建议采用不同的方法:

  • 使用反应流 (RxJava) 来表示您的模型
  • 使用架构组件中的 lifecycle-reactivestreams 将您的 RxJava Observables 转换为 LiveData。

通过使用 RxJava,您可以轻松地将可观察对象合并在一起,并且仅使用 LiveData 将该组合暴露给视图。我建议仅使用 LiveData 将状态公开给视图(FragmentActivity)。

我写了一个 article 关于如何做到这一点。您可能会发现它很有用。