Да, но если вы начинаете свою деятельность с использованием этого контекста, вам нужно установить для флага NEW_TASK значение намерения, и это просто грязно. Контекст приложения не должен использоваться для запуска действий.

но я решил поближе познакомиться с новыми компонентами архитектуры Android, которые выпустила Google, особенно с использованием их класса ViewModel, учитывающего жизненный цикл, для архитектуры MVVM и LiveData.

Пока я имею дело с одним действием или одним фрагментом, все в порядке.

Тем не менее, я не могу найти хорошее решение для переключения активности. Скажем ради краткого примера, что у Действия A есть кнопка для запуска Действия B.

Где будет обрабатываться startActivity ()?

Следуя шаблону MVVM, логика clickListener должна быть в ViewModel. Тем не менее, мы хотим избежать ссылок на активность. Поэтому передача контекста в ViewModel не вариант.

Я сузил пару вариантов, которые кажутся «ОК», но не смог найти правильного ответа «вот как это сделать».

Опция 1 : Иметь перечисление во ViewModel со значениями, отображающими возможную маршрутизацию (ACTIVITY_B, ACTIVITY_C). Соедините это с LiveData. Операция будет наблюдать эти LiveData, и когда ViewModel решит, что ACTIVITY_C должен быть запущен, он просто отправит значение (ACTIVITY_C). Затем активность может вызывать startActivity () в обычном режиме.

Вариант 2 : Обычный шаблон интерфейса. Тот же принцип, что и в варианте 1, но Activity будет реализовывать интерфейс. Я чувствую немного больше связи с этим, хотя.

Вариант 3 : Опция обмена сообщениями, например Otto или аналогичная. ViewModel отправляет широковещательную рассылку, Activity забирает ее и запускает то, что нужно. Единственная проблема с этим решением состоит в том, что по умолчанию вы должны поместить регистр / отмену регистрации этой трансляции в ViewModel. Так что не помогает.

Вариант 4 : Наличие большого класса Routing, где-нибудь, в виде синглтона или аналога, который может быть вызван для отправки соответствующей маршрутизации на любое действие. В конце концов через интерфейс? Таким образом, каждое действие (или BaseActivity) будет реализовывать

IRouting { void requestLaunchActivity(ACTIVITY_B); }

Этот метод меня немного беспокоит, когда в вашем приложении появляется много фрагментов / действий (потому что класс Routing станет огромным)

Ну это все. Это мой вопрос. Как вы, ребята, справляетесь с этим? Вы идете с опцией, о которой я не думал? Какой вариант вы считаете наиболее актуальным и почему? Каков рекомендуемый подход Google?

PS: ссылки, которые меня нигде не получили 1 -Android ViewModel вызов методов деятельности 2 -Как начать деятельность с простого неактивного Java-класса?

Ответы на вопрос(2)

AndroidViewModel, который имеет ссылку на приложение, и начать действие, используя этот контекст.

 Vadim Goryainov18 апр. 2018 г., 12:34
Оба способа хороши, просто добавили еще один вариант.
 4gus71n06 июн. 2018 г., 01:04
Да, но если вы начинаете свою деятельность с использованием этого контекста, вам нужно установить для флага NEW_TASK значение намерения, и это просто грязно. Контекст приложения не должен использоваться для запуска действий.
 NSimon18 апр. 2018 г., 12:01
Правда. Тем не менее, вам нужно смоделировать контекст при выполнении UnitTesting, что добавляет дополнительную (ненужную) работу. Я закончил с вышеупомянутым подходом, который состоит из поля LiveData только для навигации.
Решение Вопроса

Я написалвопрос в aac's-github раньше об этом.

Есть несколько способов сделать это.

Одним из решений будет использование

WeakReference в NavigationController, который содержит контекст действия. Это часто используемый шаблон для обработки связанных с контекстом вещей внутри ViewModel.

Я сильно отказываюсь от этого по нескольким причинам. Во-первых: это обычно означает, что вам нужно сохранить ссылку на ваш NavigationController, который исправляет утечку контекста, но не решает архитектуру вообще.

Лучший способ (по моему мнению) - использовать LiveData, который учитывает жизненный цикл и может делать все, что нужно.

Пример:

class YourVm : ViewModel() { 

    val uiEventLiveData = SingleLiveData<Pair<YourModel, Int>>()
    fun onClick(item: YourModel) {
        uiEventLiveData.value = item to 3 // can be predefined values
    }
}

После этого вы можете слушать внутри вашего взгляда на изменения.

class YourFragmentOrActivity { 
     //assign your vm whatever
     override fun onActivityCreated(savedInstanceState: Bundle?) { 
        var context = this
        yourVm.uiEventLiveData.observe(this, Observer {
            when (it?.second) {
                1 -> { context.startActivity( ... ) }
                2 -> { .. } 
            }

        })
    }
}

Позаботьтесь о том, чтобы я использовал измененный MutableLiveData, потому что иначе он всегда будет выдавать последний результат для новых наблюдателей, что приводит к плохому поведению. Например, если вы измените активность и вернетесь назад, она закончится циклом.

class SingleLiveData<T> : MutableLiveData<T>() {

    private val mPending = AtomicBoolean(false)

    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<T>) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        }

        // Observe the internal MutableLiveData
        super.observe(owner, Observer { t ->
            if (mPending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

    @MainThread
    override fun setValue(t: T?) {
        mPending.set(true)
        super.setValue(t)
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        value = null
    }

    companion object {
        private val TAG = "SingleLiveData"
    }
}

Почему эта попытка лучше, чем использование WeakReferences, Interfaces или любого другого решения?

Потому что это событие разделило логику пользовательского интерфейса с бизнес-логикой. Также возможно иметь несколько наблюдателей. Это заботится о жизненном цикле. Ничего не течет.

Вы также можете решить эту проблему, используя RxJava вместо LiveData, используя PublishSubject. (addTo требуетRxKotlin)

Позаботьтесь о том, чтобы не пропустить подписку, выпуская ее в onStop ().

class YourVm : ViewModel() { 
   var subject : PublishSubject<YourItem>  = PublishSubject.create();
}

class YourFragmentOrActivityOrWhatever {
    var composite = CompositeDisposable() 
    onStart() { 
         YourVm.subject 
             .subscribe( { Log.d("...", "Event emitted $it") }, { error("Error occured $it") }) 
               .addTo(compositeDisposable)         
       }   
       onStop() {
         compositeDisposable.clear()
       }
    }

Также позаботьтесь о том, чтобы ViewModel был привязан к Activity или фрагменту. Вы не можете совместно использовать ViewModel между несколькими действиями, так как это нарушит «осведомленность о жизненном цикле».

Если вам нужно сохранить ваши данные, используя базу данных, каккомната или поделиться данными с помощью посылок.

 Tejas26 дек. 2018 г., 12:08
@Suman, проект архитектуры Android описывает это очень подробно. Я думаю, что вы сможете найти класс SingleLiveData, реализованный здесь в ссылке.github.com/googlesamples/android-architecture/blob/...
 NSimon19 окт. 2017 г., 15:32
Спасибо за очень подробный ответ. Я также склонялся к подходу LiveData, однако не думал о ваших настройках LiveData. В целом, все это кажется очень "хакерским", и почти так плохо это делать. В любом случае, спасибо ! (Изменить: можно подтвердить только за 20 часов)
 Emanuel S19 окт. 2017 г., 15:33
Нет, это не так. Вот как работает шаблон наблюдателя. Вы получаете щелчок, толкаете его в свою ViewModel и, если есть View (это гарантировано), то он обрабатывает данные. Это даже не патч :) Вот как это работает. Клик - это просто пример, это может быть любой «ввод данных».
 Suman25 сент. 2018 г., 13:26
@ EmanuelS, какой-нибудь пример для Java?
 Derek K23 янв. 2019 г., 15:32
Это именно то решение, которое я использовал с тех пор, как были представлены компоненты архитектуры Android :)

Ваш ответ на вопрос