【问题标题】:Best practice for using MediatorLiveData only by present data仅通过当前数据使用 MediatorLiveData 的最佳实践
【发布时间】:2019-01-19 21:59:18
【问题描述】:

将 MediatorLiveData 与多个源一起使用的最佳做法是什么?

我在 ViewModel 中有一个 MediatorLiveData,可以从视图中访问数据,最终应该会呈现出来。

MediatorLiveData 依赖于多个其他 LiveData。其中一些来自存储库层,其中一些必须在 ViewModel 中处理,然后才能从 MediatorLiveData 访问,还有一些来自 View。

所以我当前的实现看起来像下面的架构:

public MyViewModel extends ViewModel {
   LiveData<Foo> liveData1;
   LiveData<Bar> liveData2;
   LiveData<FooBar> liveData3;
   //Some other LiveDatas

   MediatorLiveData liveDataForView

   public MyViewModel() {
      liveDataForView = new MediatorLiveData();
      //Do some preprocessing with some of the LiveData
      setupForView();
   }

   public MediatorLiveData getLiveDataForView() {
      return liveDataForView;
   }

   private void setupForView() {
      liveDataForView.addSource(liveData1, (foo -> {
         if(liveData1.getValue() != null && liveData2.getValue() != null && liveData3.getValue() != null /*&& some other LiveData-checks*/)
            liveDataForView.setValue(/*Some combinations of the LiveDatas*/);
      }));
      //Add sources to the MediatorLiveData for any other LiveData
   }
}

通过这个实现,我断言,输出 LiveData 的值是在每个 LiveData 存在之后设置的。 在某些情况下,如果我留下一些空检查,我会得到一个 NullPointerException。 但是这个解决方案对我来说似乎有点混乱,因为对于我必须添加到 ViewModel 的每个 LiveData,我都必须将它添加到每个源中。

【问题讨论】:

    标签: android android-livedata


    【解决方案1】:

    首先你需要一些元组:

    public class Tuple2<S, T> {
        public final S first;
        public final T second;
    
        public Tuple2(S first, T second) {
            this.first = first;
            this.second = second;
        }
    }
    

    public class Tuple3<S, T, U> {
        public final S first;
        public final T second;
        public final U third;
    
        public Tuple3(S first, T second, U third) {
            this.first = first;
            this.second = second;
            this.third = third;
        }
    }
    

    public class Tuple4<S, T, U, V> {
        public final S first;
        public final T second;
        public final U third;
        public final V fourth;
    
        public Tuple4(S first, T second, U third, V fourth) {
            this.first = first;
            this.second = second;
            this.third = third;
            this.fourth = fourth;
        }
    }
    

    一旦你有了你的元组,你就可以编写类似于Transformations.map 的辅助函数,除了现在你可以这样做:

    public class LiveDataTransformations {
        private LiveDataTransformations() {}
    
        public static <S, T> LiveData<Tuple2<S,T>> ifNotNull(LiveData<S> first, LiveData<T> second) {
            MediatorLiveData<Tuple2<S, T>> mediator = new MediatorLiveData<>();
    
            mediator.addSource(first, (_first) -> {
                T _second = second.getValue();
                if(_first != null && _second != null) {
                    mediator.setValue(new Tuple2(_first, _second));
                }
            });
    
            mediator.addSource(second, (_second) -> {
                S _first = first.getValue();
                if(_first != null && _second != null) {
                    mediator.setValue(new Tuple2(_first, _second));
                }
            });
    
            return mediator;
        }
    
        public static <S, T, U> LiveData<Tuple3<S,T,U>> ifNotNull(LiveData<S> first, LiveData<T> second, LiveData<U> third) {
            MediatorLiveData<Tuple3<S, T, U>> mediator = new MediatorLiveData<>();
    
            mediator.addSource(first, (_first) -> {
                T _second = second.getValue();
                U _third = third.getValue();
                if(_first != null && _second != null && _third != null) {
                    mediator.setValue(new Tuple3(_first, _second, _third));
                }
            });
    
            mediator.addSource(second, (_second) -> {
                S _first = first.getValue();
                U _third = third.getValue();
                if(_first != null && _second != null && _third != null) {
                    mediator.setValue(new Tuple3(_first, _second, _third));
                }
            });
    
            mediator.addSource(third, (_third) -> {
                S _first = first.getValue();
                T _second = second.getValue();
                if(_first != null && _second != null && _third != null) {
                    mediator.setValue(new Tuple3(_first, _second, _third));
                }
            });
    
            return mediator;
        }
    
        public static <S, T, U, V> LiveData<Tuple4<S,T,U, V>> ifNotNull(LiveData<S> first, LiveData<T> second, LiveData<U> third, LiveData<V> fourth) {
            MediatorLiveData<Tuple4<S, T, U, V>> mediator = new MediatorLiveData<>();
    
            mediator.addSource(first, (_first) -> {
                T _second = second.getValue();
                U _third = third.getValue();
                V _fourth = fourth.getValue();
                if(_first != null && _second != null && _third != null && _fourth != null) {
                    mediator.setValue(new Tuple4(_first, _second, _third, _fourth));
                }
            });
    
            mediator.addSource(second, (_second) -> {
                S _first = first.getValue();
                U _third = third.getValue();
                V _fourth = fourth.getValue();
                if(_first != null && _second != null && _third != null && _fourth != null) {
                    mediator.setValue(new Tuple4(_first, _second, _third, _fourth));
                }
            });
    
            mediator.addSource(third, (_third) -> {
                S _first = first.getValue();
                T _second = second.getValue();
                V _fourth = fourth.getValue();
                if(_first != null && _second != null && _third != null && _fourth != null) {
                    mediator.setValue(new Tuple4(_first, _second, _third, _fourth));
                }
            });
    
            mediator.addSource(fourth, (_fourth) -> {
                S _first = first.getValue();
                T _second = second.getValue();
                U _third = third.getValue();
                if(_first != null && _second != null && _third != null && _fourth != null) {
                    mediator.setValue(new Tuple4(_first, _second, _third, _fourth));
                }
            });
    
            return mediator;
        }
    }
    

    现在你可以做

    LiveData<???> liveDataForView;
    
    private void setupForView() {
       LiveData<Tuple3<Foo, Bar, FooBar>> intermediate =  LiveDataTransformations.ifNotNull(liveData1, liveData2, liveData3);
       liveDataForView = Transformations.map(intermediate, (tuple) -> {
           Foo foo = tuple.first;
           Bar bar = tuple.second;
           FooBar fooBar = tuple.third;
    
           return /*Some combinations of the LiveDatas*/
       });
    }
    

    编辑:您可以使用库 https://github.com/Zhuinden/livedata-combinetuple-kt 来做同样的事情。

    【讨论】:

    • 感谢您的回答。因此,如果我理解正确:如果我必须在实现时添加一些进一步的 LiveData,我必须添加一个新的元组类,在其中将第 x 个 LiveData 添加到中介,然后调整 setupForView 以处理第 x 个元素?跨度>
    • 是的,但它是same strategy that RxJava uses
    • 只要vararg LiveData 可以映射到相同数量的元组,就可以创建一个更智能的方法来处理任何 N 数组。这样你就不用写那么多了。方法zipArray给了我这个想法。
    【解决方案2】:

    您不必将它添加到每个源的每个表达式中,因为您没有访问 lambda 表达式中的变量 foo。 因此,您可以从所有 lambda 表达式中调用辅助函数(或者您甚至可以为所有源重用相同的 lambda 表达式,无法测试该 atm。),这样您只需要在您的内部定义检查单个辅助函数。

    【讨论】:

      猜你喜欢
      • 2013-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-01
      • 2011-10-12
      • 2010-11-06
      • 2018-04-21
      • 2014-08-01
      相关资源
      最近更新 更多