Difference of Setvalue() & Postvalue() in Mutablelivedata

Difference of setValue() & postValue() in MutableLiveData

Based on the documentation:

setValue():

Sets the value. If there are active observers, the value will be
dispatched to them. This method must be called from the main thread.

postValue():

Posts a task to a main thread to set the given value. If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.

To summarize, the key difference would be:

setValue() method must be called from the main thread. But if you need set a value from a background thread, postValue() should be used.

When to use postvalue

According to the documentation:

Based on the documentation:

setValue():

Sets the value. If there are active observers, the value will be
dispatched to them. This method must be called from the main thread.

postValue():

Posts a task to a main thread to set the given value. If you called
this method multiple times before a main thread executed a posted
task, only the last value would be dispatched.

So I would always use setValue when possible, in your case as you have noticed setValue is better because you can be sure it will always be on the Main Thread.

However postValue can also be called from the Main Thread, but will perform a tiny bit worse.

Why LiveData setValue or PostValue triggers onChange just once in the view?

The Activity shouldn't have any MutablieLiveData member variables, that should be inside the ViewModel.

The reason for why it only works the first time, is because the first time you observe something it notifes as changed, however because your arrangement is incorrect it never updates again. That is, because ArticleRepository is recreated again inside your ViewModel with a new set of MutableLiveData, the previous one you subscribed to is no longer relevant - and you only subscribe once onCreate().

You should separate bind from async-tasks such as loadData() they are not the same thing. Binding is what you do at the beginning to gather the MutableLiveData (what you are doing in loadData), but after you've done that once you shouldn't do it again.

I also noted that you are actually having LiveData inside the model, it's not recommended to do it this way as it breaks the pattern and can bring other issues. It's the ViewModel that is supposed to prepare the presentation, not the Repository. As you currently have configured things your repository might as well be called the ViewModel. Instead what you should do is use observables to notify the ViewModel of a new batch to post or handle possible errors that occurred.

Study this example: https://developer.android.com/topic/libraries/architecture/viewmodel

Note that loadUsers() is done once when getUsers() is called. This is what binds the Activity to the ViewModel. But loadUsers() can be done again later and should post the changes to the LiveData inside the ViewModel.

Difference between 'setValue' and 'value' in Kotlin 4.1 MutableLiveData?

Thanks to this link - kotlinlang.org/docs/reference/java-interop.html#getters-and-setters - provided by @IR42 and other information by other contributors whos comments were unfortunately deleted I found my answer:

MutableLiveData is a Java class and Kotlin will infer a property when the Java class has methods that follow the Java conventions for getters and setters (no-argument methods with names starting with get and single-argument methods with names starting with set)

Code completion will not suggest the Java getter methods (i.e. getValue and setValue) but it will suggest the Kotlin inferred property (i.e. value)

You can still use the Java getter/setter methods but this is discouraged.

Using setValue instead of PostValue for LiveData

I just upgraded my project using a single Activity and multiple fragments using Navigation component, and the problem that I explained in the question resolved.

As I mentioned I was using multiple Activities with multiple fragments, as a result I reported that in Google issue tracker

setValue and postValue on MutableLiveData in UnitTest

First thing is that you need to work with the InstantTaskExecutorRule, that means:

@Rule public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule();

This will allow to work with MutableLiveData instantly. Remember to include in your app's build.gradle the import:

testCompile "android.arch.core:core-testing:$rootProject.ext.arch_version"

Then, you need to either define a JUnit Rule with a rule overriding RxSchedulers or within the @Before override the Schedulers as well using RxAndroidPlugins (if you are using RxJava)

RxSchedulersOverrideRule:

public class RxSchedulersOverrideRule implements TestRule {
@Override public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override public void evaluate() throws Throwable {
RxAndroidPlugins.reset();
RxAndroidPlugins.setMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());

RxJavaPlugins.reset();
RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());

base.evaluate();

RxAndroidPlugins.reset();
RxJavaPlugins.reset();
}
};
}
}

And calling it in the test class:

@Rule public RxSchedulersOverrideRule rxSchedulersOverrideRule = new RxSchedulersOverrideRule();

The other option is calling within setup() in @Before:

RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline());

After using this you need to reset in @After's tearDownClass():

RxAndroidPlugins.reset(); 

MutableLiveData setValue: Unexpected behavior in Activity's onCreate

Your problem is that livedata observer is not active. because of this when you do liveData.value = 11, this value doesn't get posted to the observer.
and subsequently when you do liveData.value = 13 it overrides the value 11.

To check if your live data has any active observers, you can do liveData.hasActiveObservers()

Docs clearly state that setValue only dispatches if there are any active observers

void setValue (T value)

Sets the value. If there are active observers, the value will be
dispatched to them.

But why your observer is not active?

When you set a value, LiveData internally uses ObserverWrapper's shouldBeActive method to check if a specific observer is active .

and when you use observe method to register your observer, the observer is wrapped in a LifecycleBoundObserver (subclass of ObserverWrapper) which defines its shouldBeActive as follows.

@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

The part that matters is .isAtLeast(STARTED), here STARTED is of type Lifecycle.State, and its documentation has following to say about it.

Started state for a LifecycleOwner. For an Activity, this state is
reached in two cases:

after onStart call;

right before onPause call.

and because you register observer in onCreate, it doesn't become active right away and hence the problem.

For verification you can also use the observeForever (please read its documentation, it differs greatly from observe) method to register your observer. because this method makes the observer active instantly, you will see the output that you expect.

liveData.observeForever {
Log.v("ActivityA", "liveData = $it")
}


Related Topics



Leave a reply



Submit