Детализация ViewModel с действиями и фрагментами

Этот вопрос связан с архитектурой приложения для Android. При использовании ViewModel компонента LifeCycle лучше всего иметь одну ViewModel для каждого фрагмента или одну ViewModel для родительского действия, на которое подписаны фрагменты?

Мне кажется непонятным, как сориентировать что-то вроде отношения Мастер-Деталь-фрагмент-активность без какой-либо связи. Например, если у каждого фрагмента была своя собственная ViewModel, неясно, как Activity должна знать, как реагировать без привязки (интерфейс, прямые вызовы функций).


person Josh Ribeiro    schedule 13.06.2018    source источник
comment
Если вы правильно используете MVVM, наличие модели просмотра как для фрагмента, так и для активности было бы неверным. В идеале он нужен только в одном месте.   -  person Chisko    schedule 13.06.2018
comment
Это важная информация. Однако до сих пор неясно, как должны ли ViewModels содержаться в Activity или Fragments, а также как они должны взаимодействовать.   -  person Josh Ribeiro    schedule 13.06.2018
comment
они действуют как интерфейс между вашими данными (моделью) и представлением, передавая изменения от первого ко второму. Однако нет простого или единственного способа сделать это. Это действительно зависит от вашей архитектуры. В моем текущем проекте мы используем MVP, но уровень M включает ViewModels, которые предполагается использовать с MVVM, но архитекторам удалось реализовать это наложение слоев очень хорошо, и каждый класс имеет свои собственные роли и сильно развязан. Опять же, это зависит от ваших потребностей.   -  person Chisko    schedule 13.06.2018
comment
Я могу поработать над коротким ответом, если вы будете немного терпеливы. Сегодня у нас замораживание кода. После этого я создам небольшой пример, если меня первым не опередит никто.   -  person Chisko    schedule 13.06.2018
comment
Пожалуйста, если вы не против. Ваше понимание уже оценено. Я понимаю, как вещи разделены, но кажется, что связь фрагмент-активность добавляет ненужный слой путаницы. Например, зная, когда закрыть подробный фрагмент или передать Reclerview фрагмента ViewModel, ему необходимо загрузить список.   -  person Josh Ribeiro    schedule 13.06.2018


Ответы (1)


Как я уже упоминал в комментариях, уникального способа добиться этого не существует, но в идеале и очень конкретно для вашей проблемы с потоком Master / Detail, давайте проанализируем предоставленный по умолчанию пример:

ItemDetialActivity обрабатывает создание и отображение фрагментов, FAB и действия меню. Обратите внимание, что с пользовательскими данными ничего не связано, только "системные" дескрипторы. Я, например, пытаюсь ограничить ответственность действий навигацией и вещами, которых действительно нельзя избежать, например, обработкой кнопок меню. Теперь ItemListActivity, похоже, нарушает этот принцип, потому что заботится об отображении списка (примеры Google только создают путаницу -IMHO- между этим разделением задач), я бы создал отдельный фрагмент, содержащий RecyclerView и его адаптер.

Теперь перейдем к мелочам. Вот скелет очень высокого уровня, я надеюсь, вы сможете его использовать. Проверьте это, внедрите и вернитесь, если возникнут вопросы:

public interface BaseView {
     LifecycleOwner lifecycleOwner();

     /* perform actions that affect a basic screen status, like hide/show progress bars and errors,  
        animate views, etc. */
}

public class BaseRepo {
    // will contain LiveData instances which will postValues()
}

public class FooRepo extends BaseRepo {
    /* will contain access to database and networking functions, either by creating instance methods 
       or enforcing with an interface, it's up to you.  */
}

public class BaseModel<P extends BasePresenter> extends ViewModel {
    protected final FooRepo fooRepo; // optional, can be on concretes

    <T> void subscribe(LiveData<T> liveData, Observer<T> observer) {
        liveData.observe(view.lifecycleOwner(), observer);
    }

    <T> void unsubscribe(LiveData<T> liveData, Observer<T> observer) {
        if (liveData != null) {
            liveData.removeObserver(observer);
        }
    }
    ...
} 

public abstract class BasePresenter<M extends BaseModel, V extends BaseView> implements LifecycleObserver {
    protected V view;
    protected M model;

    public void setModel(M model) {
        this.model = model;
    }

    public final void attachView(V view, Lifecycle lifecycle) {
        this.view = view;
        lifecycle.addObserver(this);
    }

    public void setPresenter(P presenter) {
        this.presenter = presenter;
        this.presenter.setModel(this);
    }
    ...
}

public abstract class BaseFragment implements BaseView {
    /* generics is highly encouraged here, I've seen examples of both BasePresenter<P> 
       and BaseView<P> */
    protected P presenter; 

    /* You should bind layers here, or in the concrete class, 
       either with Dagger, reflection, or some other way */ 

    @Override
    public LifecycleOwner lifecycleOwner() {
        return this;
    }
    ...
}

Теперь для каждого конкретного экрана вы должны создать презентатора, модель и фрагмент, производные от баз, и выполнить на них специфические особенности. Я надеюсь, что это помогает.

person Chisko    schedule 18.06.2018