代码之家  ›  专栏  ›  技术社区  ›  clzola

如何将两个实时数据一个接一个地组合起来?

  •  7
  • clzola  · 技术社区  · 6 年前

    我有下一个用例:用户来到注册表单,输入姓名、电子邮件和密码,然后单击注册按钮。之后,系统需要检查是否有电子邮件,并在此基础上显示错误消息或创建新用户…

    我正在尝试使用room、viewmodel和livedata来实现这一点。这是一个我试图学习这些组件的项目,我没有远程api,我将把所有东西都存储在本地数据库中

    所以我有这些课程:

    • 注册活动
    • 注册服务模型
    • 用户
    • 用户日期
    • 用户存储
    • 用户注册服务

    所以我的想法是有一个监听器连接到register按钮,它将调用 RegisterViewModel::register() 方法。

    class RegisterViewModel extends ViewModel {
    
        //...
    
        public void register() {
            validationErrorMessage.setValue(null);
            if(!validateInput())
                return;
            registrationService.performRegistration(name.get(), email.get(), password.get());
        }
    
        //...
    
    }
    

    所以这是我的基本想法 performRegistration 返回给我新创建的用户。

    最困扰我的是我不知道如何实现 执行注册 服务中的功能

    class UsersRegistrationService {
        private UsersRepository usersRepo;
    
        //...
    
        public LiveData<RegistrationResponse<Parent>>  performRegistration(String name, String email, String password) {
             // 1. check if email exists using repository
             // 2. if user exists return RegistrationResponse.error("Email is taken") 
             // 3. if user does not exists create new user and return RegistrationResponse(newUser)
        }
    }
    

    据我所知, UsersRepository 应该返回livedata,因为 UsersDAO 正在返回livedata

    @Dao
    abstract class UsersDAO { 
        @Query("SELECT * FROM users WHERE email = :email LIMIT 1")
        abstract LiveData<User> getUserByEmail(String email);
    }
    
    class UsersRepository {
        //...
        public LiveData<User> findUserByEmail(String email) {
            return this.usersDAO.getUserByEmail(email);
        }
    }
    

    所以我的问题是如何实施 performRegistration() 函数以及如何将值传递回视图模型,然后如何将活动从RegisterActivity更改为MainActivity…

    5 回复  |  直到 6 年前
        1
  •  12
  •   Faysal Ahmed    5 年前

    借助于 MediatorLiveData ,可以合并来自多个源的结果。下面是我如何组合两个来源的示例:

    class CombinedLiveData<T, K, S>(source1: LiveData<T>, source2: LiveData<K>, private val combine: (data1: T?, data2: K?) -> S) : MediatorLiveData<S>() {
    
    private var data1: T? = null
    private var data2: K? = null
    
    init {
        super.addSource(source1) {
            data1 = it
            value = combine(data1, data2)
        }
        super.addSource(source2) {
            data2 = it
            value = combine(data1, data2)
        }
    }
    
    override fun <T : Any?> addSource(source: LiveData<T>, onChanged: Observer<T>) {
        throw UnsupportedOperationException()
    }
    
    override fun <T : Any?> removeSource(toRemote: LiveData<T>) {
        throw UnsupportedOperationException()
    }
    }
    

    以下是上述要点,以备将来更新: https://gist.github.com/guness/0a96d80bc1fb969fa70a5448aa34c215

        2
  •  3
  •   Daniel Wilson    5 年前

    何塞·阿尔克·瑞卡可能有 best answer for this :

    fun blogpostBoilerplateExample(newUser: String): LiveData<UserDataResult> {
    
        val liveData1 = userOnlineDataSource.getOnlineTime(newUser)
        val liveData2 = userCheckinsDataSource.getCheckins(newUser)
    
        val result = MediatorLiveData<UserDataResult>()
    
        result.addSource(liveData1) { value ->
            result.value = combineLatestData(liveData1, liveData2)
        }
        result.addSource(liveData2) { value ->
            result.value = combineLatestData(liveData1, liveData2)
        }
        return result
    }
    
        3
  •  2
  •   Marek Kondracki    5 年前

    你可以使用我的助手方法:

    val profile = MutableLiveData<ProfileData>()
    
    val user = MutableLiveData<CurrentUser>()
    
    val title = profile.combineWith(user) { profile, user ->
        "${profile.job} ${user.name}"
    }
    
    fun <T, K, R> LiveData<T>.combineWith(
        liveData: LiveData<K>,
        block: (T?, K?) -> R
    ): LiveData<R> {
        val result = MediatorLiveData<R>()
        result.addSource(this) {
            result.value = block.invoke(this.value, liveData.value)
        }
        result.addSource(liveData) {
            result.value = block.invoke(this.value, liveData.value)
        }
        return result
    }
    
        4
  •  0
  •   EpicPandaForce    5 年前

    您可以定义一个方法,该方法将使用mediatorlivedata组合多个livedata,然后将组合的结果公开为一个元组。

    public class CombinedLiveData2<A, B> extends MediatorLiveData<Pair<A, B>> {
        private A a;
        private B b;
    
        public CombinedLiveData2(LiveData<A> ld1, LiveData<B> ld2) {
            setValue(Pair.create(a, b));
    
            addSource(ld1, (a) -> { 
                 if(a != null) {
                    this.a = a;
                 } 
                 setValue(Pair.create(a, b)); 
            });
    
            addSource(ld2, (b) -> { 
                if(b != null) {
                    this.b = b;
                } 
                setValue(Pair.create(a, b));
            });
        }
    }
    

    如果需要更多值,则可以创建 CombinedLiveData3<A,B,C> 然后暴露 Triple<A,B,C> 而不是一对。

        5
  •  0
  •   d-feverx    5 年前
    LiveData liveData1 = ...;
     LiveData liveData2 = ...;
    
     MediatorLiveData liveDataMerger = new MediatorLiveData<>();
     liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
     liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));