Ответ 1
NSimon, это замечательно, что вы начинаете использовать AAC.
Я написал issue в aac's-github до этого.
Есть несколько способов сделать это.
В одном решении будет использоваться
WeakReference в NavigationController, который содержит контекст Activity. Это распространенный шаблон, используемый для обработки содержимого, связанного с контекстом, внутри ViewModel.
Я сильно отклоняю это по нескольким причинам. Во-первых: это обычно означает, что вы должны хранить ссылку на свой навигационный контроллер, который исправляет утечку контекста, но не решает архитектуру вообще.
Лучший способ (в моем oppinion) использовать 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 -> { .. }
}
})
}
}
Позаботьтесь о том, чтобы ive использовала модифицированную 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, интерфейсов или любого другого решения?
Поскольку это событие разделяет логику пользовательского интерфейса с бизнес-логикой. Также возможно иметь несколько наблюдателей. Он заботится о жизненном цикле. Он ничего не течет.
Вы также можете решить эту проблему, используя 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 OR or Fragment. Вы не можете поделиться ViewModel между несколькими действиями, поскольку это сломает "Livecycle-Awareness".
Если вам нужно, что ваши данные сохраняются с помощью базы данных, например room или обмениваться данными с помощью посылок.