Android:在DAO中使用Room数据库和LiveData清理架构

Ale*_*kov 5 android kotlin clean-architecture android-room android-livedata

我正在尝试将干净架构方法应用于我的项目(链接:我当前正在参考的指南)。

我正在使用Room数据库进行本地存储,并且希望它成为应用程序中的单一数据源-这意味着从网络调用中收集的所有数据都首先保存在数据库中,然后再传递给演示者。Room提供了DAO返回的LiveData,这正是我需要的。

但是,我也想使用存储库作为访问数据的单一方法。这是域层中存储库接口的示例(最抽象的一个):

interface Repository<T>{
    fun findByUsername(username: String) : List<T>    
    fun add(entity: T): Long
    fun remove(entity: T)    
    fun update(entity: T) : Int
}
Run Code Online (Sandbox Code Playgroud)

在这里,我遇到了问题-我需要在ViewModel中从Room的DAO获取一个LiveData,我想使用Repository实现来获取它。但是为了实现这一点,我需要:

  1. 更改存储库方法findByUsername以返回LiveData>
  2. 或直接从ViewModel完全跳过存储库实现中调用Room的DAO

这两个选项都具有足够的缺点:

  1. 如果我汇入 android.arch.lifecycle.LiveData我的Repository接口,它将破坏Domain层的抽象,因为它现在取决于android体系结构库。
  2. 如果我直接在ViewModel中调用Room的DAO,val entities: LiveData<List<Entity>> = database.entityDao.findByUsername(username)那么我违反了所有数据访问都必须使用Reposiotry进行访问的规则,并且我将需要创建一些样板代码以与远程存储等进行同步。

如何使用LiveData,Room的DAO和Clean架构模式实现单一数据源方法?

Epi*_*rce 6

从技术上讲,您遇到了麻烦,因为您不希望同步数据获取。

 fun findByUsername(username: String) : List<T>  
Run Code Online (Sandbox Code Playgroud)

您希望订阅List<T>每次发生更改时都会返回一个新订阅。

 fun findByUsernameWithChanges(username: String) : Subscription<List<T>>
Run Code Online (Sandbox Code Playgroud)

所以现在您可能想要做的是制作自己的订阅包装器,可以处理LiveDataFlowable. 当然,LiveData更棘手,因为你还必须给它一个 LifecycleOwner。

public interface Subscription<T> {
    public interface Observer<T> {
        void onChange(T t);
    }

    void observe(Observer<T> observer);

    void clear();
}
Run Code Online (Sandbox Code Playgroud)

然后像

public class LiveDataSubscription<T> implements Subscription<T> {
    private LiveData<T> liveData;
    private LifecycleOwner lifecycleOwner;

    private List<Observer<T>> foreverObservers = new ArrayList<>();

    public LiveDataSubscription(LiveData<T> liveData) {
        this.liveData = liveData;
    }

    @Override
    public void observe(final Observer<T> observer) {
        if(lifecycleOwner != null) {
            liveData.observe(lifecycleOwner, new android.arch.lifecycle.Observer<T>() {
                 @Override
                 public void onChange(@Nullable T t) {
                      observer.onChange(t);
                 }
            });
        } else {
            Observer<T> foreverObserver = new android.arch.lifecycle.Observer<T>() {
                 @Override
                 public void onChange(@Nullable T t) {
                      observer.onChange(t);
                 }
            };
            foreverObservers.add(foreverObserver);
            liveData.observeForever(foreverObserver);
        }
    }

    @Override
    public void clear() {
        if(lifecycleOwner != null) {
            liveData.removeObservers(lifecycleOwner);
        } else {
            for(Observer<T> observer: foreverObservers) {
                liveData.removeObserver(observer);
            }
        }
    }

    public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
        this.lifecycleOwner = lifecycleOwner;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以使用您的存储库

val subscription = repository.findByUsernameWithChanges("blah")
if(subscription is LiveDataSubscription) {
    subscription.lifecycleOwner = this
}
subscription.observe { data ->
    // ...
}
Run Code Online (Sandbox Code Playgroud)


Mam*_*rey 5

当类似的问题被问到关于使用RxJava时,开发人员通常会回答,没关系,RxJava现在是语言的一部分,所以,你可以在领域层使用它。在我看来 - 你可以做任何事情,如果它对你有帮助的话,所以,如果使用 LiveData 不会产生问题 - 使用它,或者你可以使用 RxJava 或 Kotlin 协程代替。