Android: чистая архитектура с базой данных Room и LiveData в DAO - PullRequest
0 голосов
/ 08 июня 2018

Я пытаюсь применить подход чистой архитектуры к своему проекту ( Ссылка: руководство, на которое я сейчас ссылаюсь ).

Я использую базу данных Room для локального хранения, и яхотите, чтобы это был единственный источник данных в приложении - это означает, что все данные, собранные в результате сетевых вызовов, сначала сохраняются в базе данных и только после того, как они передаются докладчику.Room обеспечивает возврат LiveData из своих DAO, и это именно то, что соответствует моим потребностям.

Однако я также хочу использовать репозитории в качестве единственного способа доступа к данным.Вот пример интерфейса репозитория в доменном слое (самый абстрактный):

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

И здесь я сталкиваюсь с проблемой - мне нужно получить LiveData из DAO Room в моей ViewModel, и я 'Я хотел бы получить его, используя реализацию репозитория.Но для этого мне нужно:

  1. Изменить метод репозитория findByUsername для возврата LiveData>
  2. Или вызвать DAO Room напрямую из ViewModel, пропустив реализацию репозитория полностью

Обе эти опции имеют достаточные недостатки:

  1. Если я импортирую android.arch.lifecycle.LiveData в свой интерфейс репозитория, это нарушит абстракцию на уровне домена, как это теперь зависит от библиотек архитектуры Android..
  2. Если я вызываю DAO комнаты непосредственно в ViewModel как val entities: LiveData<List<Entity>> = database.entityDao.findByUsername(username), тогда я нарушаю правило, что весь доступ к данным должен быть сделан с помощью Reposiotry , и мне нужно будет создать некоторый шаблонкод для синхронизации с удаленным хранилищем и т. д.

Как можно достичь подхода с использованием единого источника данных с использованием шаблонов LiveData, Room DAO и Clean architecure?

Ответы [ 2 ]

0 голосов
/ 10 июня 2018

Технически у вас возникают проблемы, потому что вы не хотите синхронной выборки данных.

 fun findByUsername(username: String) : List<T>  

Вам нужна подписка, которая возвращает вам новую List<T> каждый раз, когда происходит изменение.

 fun findByUsernameWithChanges(username: String) : Subscription<List<T>>

Итак, теперь вы можете создать свою собственную оболочку подписки, которая может обрабатывать LiveData или Flowable.Конечно, LiveData сложнее, потому что вы также должны дать ему LifecycleOwner.

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

    void observe(Observer<T> observer);

    void clear();
}

А потом что-то вроде

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

И теперь вы можете использовать свой репозиторий

val subscription = repository.findByUsernameWithChanges("blah")
if(subscription is LiveDataSubscription) {
    subscription.lifecycleOwner = this
}
subscription.observe { data ->
    // ...
}
0 голосов
/ 08 июня 2018

Когда задают аналогичный вопрос об использовании RxJava, разработчики обычно отвечают, это нормально, и RxJava теперь является языковой частью, так что вы можете использовать его в доменном слое.По моему мнению, вы можете сделать что-нибудь, если это вам поможет, поэтому, если использование LiveData не создает проблем - используйте это, или вы можете использовать вместо него RxJava или сопрограммы Kotlin.

...