John
10/02/2019, 12:44 PMobserveForever
on a LiveData object inside the viewModel where it’s initiated, leak it, or is the subscription ready to be recycled when the viewmodel is?voben
10/02/2019, 2:04 PMremoveObserver
each time an observeForever
call is added https://developer.android.com/reference/android/arch/lifecycle/LiveDataobserveForever
takes an anonymous inner class as a parameter which means its holding a strong reference to the outer class and could potentially cause a memory leakrkeazor
10/03/2019, 2:06 AMwasyl
10/03/2019, 6:17 AMLiveData
updated by the databinding that uses two-way binding (for example `android:text=“@={viewModel.textLiveData}“). ViewModel should react to text changes so it needs to subscribe to itrkeazor
10/03/2019, 11:36 AMwasyl
10/03/2019, 11:42 AMMutableLiveData<String>
to binding and do android:text="@={textLiveData}"
rkeazor
10/03/2019, 11:47 AMwasyl
10/03/2019, 3:59 PMrkeazor
10/04/2019, 12:43 AMwasyl
10/04/2019, 8:19 AMclassField = it
then view model would be garbage collected even earlier, right after there are no reference to it.observeForever
in the view model which owns the live data. Assuming both view model and live data aren’t referenced anymore from the outside, everything is garbage collected nicely.John
10/04/2019, 9:03 AMrkeazor
10/04/2019, 11:45 AMJohn
10/04/2019, 11:46 AMrkeazor
10/04/2019, 11:53 AMwasyl
10/04/2019, 12:41 PMWhy would the viewmodel call observeForever on it’s own livedata object?There are use cases for it and I explained one before. If you use databinding then you expose live data from the view model and want to react to changes to them also in the view model.
He asked does calling observeForever on a livedata object leak it after the viewmodel is destroyedYou do realize you corrected OP about what he asked about? 😄
I’m saying that where leaks usually happens is when the observing object has a different lifecycleI think important thing is that view model typically has the broadest lifecycle you can have. It lives longer than activity, fragment, view, view binding, anything. So I don’t really see how you could have a situation in which something still references the live data while view model should already be garbage collected.
rkeazor
10/04/2019, 1:14 PMwasyl
10/04/2019, 1:38 PMJohn
10/04/2019, 2:19 PMwasyl
10/04/2019, 2:35 PMremoveObserver
. Again, I don’t think it’s necessary as long as you observe live data which has narrower scope/lifecyclerkeazor
10/04/2019, 4:14 PMJohn
10/04/2019, 4:15 PMLogger.logEvent(text)
every time the text input matches a certain condition.rkeazor
10/04/2019, 4:17 PMJohn
10/04/2019, 4:17 PMrkeazor
10/04/2019, 5:12 PMJohn
10/04/2019, 5:13 PMrkeazor
10/04/2019, 5:15 PMJohn
10/04/2019, 5:15 PMrkeazor
10/04/2019, 5:17 PMvoben
10/04/2019, 5:49 PMdataLogger
MediatorLiveData is observing twoWayBinding
livedata. Wouldn't someone also need to observe the twoWayBinding
livedata?rkeazor
10/04/2019, 7:08 PMdewildte
10/04/2019, 7:51 PMIf you use Transformations to react to a two-way binding MutableLiveData, then you need to observe to that Transformations somewhere for it to react to the MutableLiveData, which is why you need to call observeForever in the viewmodelThe two way
MutableLiveData
should be converted into a MediatorLiveData
, observeForever is reserved for unit testing purposes.LiveData
documentation .wasyl
10/04/2019, 10:09 PMobserveForever is reserved for unit testing purposesCan’t find it anywhere in the documentation. There is mention that transformations will usually let you perform things like mapping live data to another live data (e.g. show button only when edit text is not empty). My question is, what if you want to perform some action, in the view model, every time a letter is changed? Say, log it to logcat, so no other live data involved. Would you say “you shouldn’t do that” (even though documentation explicitly says live data should be held in view models, so it looks like a valid use case)? What would be the alternative? I consider view model the place for UI-related logic, so I would want this logic exactly there.
showButton
live data and loginText
live data (bound using two-way binding). Assuming both live datas are handed to the view model by DI, how would you implement the logic of enabling/disabling the button based on loginText
contents?observeForever
on a LiveData object inside the viewModel where it’s initiated, leak it”.observeForever
in certain circumstances, like when ViewModel is observing its own live data)John
10/05/2019, 8:15 AM@ExtendWith(InstantExecutorExtension::class)
class TestTe {
@Test
fun test() {
val vm = Vm()
vm.a.observeForever { }
vm.a.value = "hi"
}
}
class Vm {
val a = MutableLiveData("")
val medi: LiveData<Boolean> = MediatorLiveData<Boolean>().apply {
addSource(a) {
println("a onChange") // never called
this.value = true
}
}
}
dewildte
10/05/2019, 11:46 AM@ExtendWith(InstantExecutorExtension::class)
class TestTe {
@Test
fun test() {
val vm = Vm()
vm.a.observeForever { }
vm.a.value = "hi"
}
}
class Vm {
val a = MediatorLiveData("")
private val backingBoolData = MutableLiveData<Boolean>()
val boolData: LiveData<Boolean> = backingBoolData
init {
a.addSource(backingBoolData) {
println("a onChange") // now this is called
backingBoolData.value = true
}
}
}
John
10/05/2019, 12:31 PMbackingBoolData
is never dispatching any valueprintln
isn’t called there either, I just ran itdewildte
10/05/2019, 12:53 PMclass ExampleLiveDataUnitTest {
@get:Rule
val rule = InstantTaskExecutorRule()
val textData = MediatorLiveData<String>()
val boolData = MutableLiveData<Boolean>()
var actual = false
@Before
fun setUp() {
textData.addSource(textData) {
boolData.value = it == "hi"
}
textData.addSource(boolData) {
actual = it
}
textData.observeForever { }
}
@Test
fun addition_isCorrect() {
val expected = true
textData.value = "hi"
assertEquals(expected, actual)
}
}
Transformations.map(...) { ... }
class ExampleLiveDataUnitTest {
@get:Rule
val rule = InstantTaskExecutorRule()
val textData = MediatorLiveData<String>()
val boolData = Transformations.map(textData) {
it == "hi"
}.apply { textData.addSource(this) { actual = it } }
var actual = false
@Before
fun setUp() {
textData.observeForever { }
}
@Test
fun addition_isCorrect() {
val expected = true
textData.value = "hi"
assertEquals(expected, actual)
}
}
wasyl
10/05/2019, 3:20 PMobserveForever
though. What if you don’t want any observer outside of the view model though? What if you want to react to data in the view model and not pass it anywhere further?rkeazor
10/05/2019, 3:25 PMwasyl
10/05/2019, 3:34 PMobserveForever
if there’s no memory leak and it does what I need? And there’s no way to react to live data without subscribing to itrkeazor
10/05/2019, 3:38 PMJohn
10/06/2019, 7:51 AMtextData.addSource(textData) {
boolData.value = it == "hi"
}
Yes