#android #kotlin #mvvm #android-livedata
#Android #kotlin #mvvm #android-livedata
Вопрос:
Например, предположим, что у нас есть представление каталога товаров с возможностью добавления товара в корзину. Каждый раз, когда пользователь нажимает кнопку добавить в корзину, вызывается метод ViewModel AddToCart, который может выглядеть следующим образом:
//inside viewModel
fun addToCart(item:Item): LiveData<Result> = liveData {
val result = repository.addToCart(item) // loadUser is a suspend function.
emit(result)
}
//inside view
addButton.onClickListener = {
viewModel.addToCart(selectedItem).observe (viewLifecycleOwner, Observer () {
result -> //show result
}
}
Что произойдет после добавления, например, 5 элементов -> будет ли в памяти 5 объектов livedata, наблюдаемых представлением?
Если да, когда они будут очищены? И если да, следует ли нам избегать livedata builder для одноразовых операций, которые могут быть вызваны несколько раз?
Ответ №1:
Ваша реализация кажется неправильной! Вы постоянно возвращаете новый LiveData
объект для каждого addToCard
вызова функции. Что касается вашего первого вопроса, это Yes
.
Если вы хотите сделать это правильно с помощью LiveData.
// In ViewModel
private val _result = MutableLiveData<Result>()
val result: LiveData<Result>
get() = _resu<
fun addToCart(item: Item) {
viewModelScope.launch {
// Call suspend functions
result.value = ...
}
}
// Activity/Fragment
viewModel.result.observe(lifecycleOwner) { result ->
// Process the result
...
}
viewModel.addToCart(selectedItem)
Все, что вам нужно сделать, это вызвать его из activity и обработать результат. Вы также можете использовать StateFlow
для этой цели. Он также имеет расширение asLiveData
, которое также преобразует Flow -> LiveData.
Комментарии:
1. Ваш ответ указывает на «правильный» путь, но вы не ответили на вопрос, почему это неправильно и что происходит «при постоянном возврате новых LiveData».
2. Согласно коду OP, он будет вызывать наблюдателя только один раз при нажатии кнопки, а дальнейшие последующие вызовы создадут его новый экземпляр и вызовут наблюдателя только один раз. В конце концов, эти предыдущие
LiveData
объекты должны автоматически собираться мусором.3. @kaustubhpatange спасибо, ваш ответ именно таков, как я делаю это в данный момент -> но я задал этот вопрос, потому что я нашел в нескольких местах предположение о том, что MutableLiveData может или даже должен быть заменен новым блоком livedata builder, и это меня беспокоило — похоже, что это небезопасно для повторяемых одноразовых действий.
4. «повторяемый одноразовый выстрел» = парадокс! Если вам нужны одноразовые живые данные, что часто бывает для событий навигации, то создание экземпляра живых данных каждый раз, когда подписчик хочет подписаться, — очень простой способ добиться этого. Очевидно, что это работает только там, где ожидается наличие одного подписчика. В противном случае, то есть повторное использование экземпляра, создает проблему, которую SingleLiveEvent намеревается решить. Зачем создавать дополнительные проблемы?
Ответ №2:
В соответствии с LiveData
реализацией:
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null amp;amp; !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
" with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
}
новый Наблюдатель ( wrapper
) добавляется каждый раз, когда вы просматриваете LiveData. Глядя на это, я был бы осторожен, создавая новых наблюдателей из события просмотра (щелчка). На данный момент я не могу сказать, может ли сборщик мусора освободить эти ресурсы.
Как упоминал @kaustubhpatange, у вас должен быть один LiveData с состоянием / значением, которое может быть изменено ViewModel с каждым новым результатом. Эти живые данные можно наблюдать (один раз) в вашей функции Activity или Fragment onCreate()
:
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.result.observe(lifecycleOwner) { result ->
// handle the result
}
}
Используя MutableLiveData в вашей ViewModel, вы можете в основном создавать LiveData только один раз, а затем заполнять его значениями из событий кликов, ответов и т. Д.
Комментарии:
1. спасибо, именно так я и поступаю в данный момент… Я пытался оценить, следует ли мне переключиться на livedata { } builder, но вы только что подтвердили мои опасения относительно нескольких экземпляров.
Ответ №3:
TL; DR
Если ваша операция одноразовая, используйте Coroutine
и LiveData
.
Если ваша операция работает с потоками, которые вы можете использовать Flow
.
Для одноразовых операций ваш подход в порядке. Я думаю, что в liveData
builder нет утечки памяти. Если вы используете, например, свойство private backing для LiveData
и наблюдаете за общедоступным LiveData
, это может привести к другому поведению, например, получить последнее значение, прежде чем присваивать ему новое значение.