192

There are two ways that make change value of MutableLiveData. But what is difference between setValue() & postValue() in MutableLiveData.

I could not find documentation for same.

Here is class MutableLiveData of Android.

package android.arch.lifecycle;

/**
 * {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method.
 *
 * @param <T> The type of data hold by this instance
 */
@SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}
Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212

10 Answers10

310

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.

multidynamic
  • 804
  • 8
  • 8
Sagar
  • 23,903
  • 4
  • 62
  • 62
  • "only the last value would be dispatched". I can't be sure about it by reading the code. So it seems like once the first thread about to hit the inner synchronised block inside postValue(), the next CPU window can be potentially given to the thread 2 which is posting another value. Thread 2 can then complete the synchronised block and the scheduler gives the first thread a window to run itself. Now, it overrites what thread 2 has already written. Is this possible? – stdout Jul 17 '19 at 15:58
  • 8
    IMPORTANT NOTE: I just noticed that if you're in main thread and use postValue it will take a few more millis compared to setValue to dispatch the data. I had this happen when I fill a layout manually, with setValue it was instant but not with post – TootsieRockNRoll Jan 27 '21 at 12:47
183

All of the above answers are correct. But one more important difference. If you call postValue() and after that you call getValue(), you may not receive the value that you set in postValue(). If the main thread had already set the value, then you will get the value that you posted, but if the main thread hadn't set the value yet, then you don't get the value that you posted. So be careful if you work in background threads.

starriet
  • 2,565
  • 22
  • 23
w201
  • 2,018
  • 1
  • 11
  • 15
  • 22
    Wish I could triple-upvote! Based on this, it appears it's best to use `setValue()` if possible, and cautiously use 'postValue()', only when necessary. Thanks – jungledev Dec 06 '18 at 15:03
  • 2
    No, here isn't any "best" way. If you work with your LiveData from background thread you should to use postValue. Also in latest version of lifecycle components it fixed... probably. – w201 Dec 14 '18 at 17:19
  • "Also in latest version of lifecycle components it fixed... probably." Do you have any more information on this? Thanks – Chris Nevill Jan 17 '19 at 17:04
  • 1
    I made some tests and seems that with last version of lib everything works as it should. – w201 Jan 19 '19 at 08:28
  • Could you show me the above concretely code? If In ViewModel, I Implemented like `noObserveLiveData.postValue("sample")`, In Activity, when I used getValue like `viewModel.noObserveLiveData.getValue` Do you mean Is it not the value that I set in postValue() ("sample")? – kwmt Sep 16 '19 at 13:20
  • @w201 when you said *"with last version of lib everything works as it should"*, does it mean your answer is not valid anymore? ...So now we CAN get the value that we set in `postValue(...)` even if there are no observers? If that's the case, I think we should modify this answer so that people don't get confused. – starriet Jan 13 '21 at 01:49
  • Basically, to deviate people from messing up with volatile keyword, they do it for you using postValue + more sophistication added. – Turkhan Badalov Nov 24 '21 at 13:24
33

setValue() is called directly from caller thread, synchronously notifies observers and changes LiveData value immediately. It can be called only from MainThread.
postValue() uses inside something like this new Handler(Looper.mainLooper()).post(() -> setValue()), so it runs setValue via Handler in MainThread. It can be called from any thread.

Ufkoku
  • 2,384
  • 20
  • 44
  • Thanks for the nice answer. So, according to @w201's answer(if it's valid until now), the `Handler`'s `post` method doesn't get called if it's not the main thread and there is no observer? – starriet Jan 13 '21 at 02:01
  • post is always called and value is updated inside LiveData. But if there is no observers or they are not in started state they will not be notified. – Ufkoku Jan 13 '21 at 09:32
  • The main issue is that post updates value asynchronously, so if you called getValue() you can get old one – Ufkoku Jan 13 '21 at 09:51
  • So, if I call `getValue()`, I might get the old value, but maybe I can get the latest value, right? So w201 said in his/her answer that *"you don't receive the value that you set in postValue()"*, but maybe I can receive the value that I set? – starriet Jan 13 '21 at 12:26
  • @starriet yes, you can get value which was set by postValue(). It is all about concurrency. If main thread has enough time to execute update triggered by postValue() then getValue() will return posted one. If you call getValue() too soon after postValue() and main thread had no time to execute posted update, then getValue() will return old value. – Ufkoku Jan 13 '21 at 19:09
  • So, if I understood correctly, setting the value has nothing to do with the existence of observers. Even if there is no observer, after the worker thread posted some value with `postValue()`, **if** the main thread executes setting the value, then we can get the value that we posted. Please let me know if I'm wrong. Thanks a lot :) – starriet Jan 14 '21 at 02:06
14

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

If you need set a value from a background thread, you can use postValue(Object)

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.

AskNilesh
  • 67,701
  • 16
  • 123
  • 163
13

This is not a direct answer to the above problem. The answers from Sagar and w201 are awesome. But a simple rule of thumb I use in ViewModels for MutableLiveData is:

private boolean isMainThread() {
    return Looper.myLooper() == Looper.getMainLooper();
}

private MutableLiveData<Boolean> mutVal = new MutableLiveData<>(false);
public LiveData<Boolean> getMutVal() { return this.mutVal;  }
public void setMutVal(boolean val) {
    if (isMainThread()) mutVal.setValue(val);
    else mutVal.postValue(val);
}

Replace mutVal with your desired value.

Himujjal
  • 1,581
  • 1
  • 14
  • 16
  • Nice, I like this. In Kotlin I created an extension that encapsulates the smart update so the numerous value updates throughout my app are a single, consistent call. – 19Craig Dec 19 '19 at 20:50
8

setValue() method must be called from the main thread. If you need to set a value from a background thread, you can use postValue().

More here.

abhiarora
  • 9,743
  • 5
  • 32
  • 57
Levon Petrosyan
  • 8,815
  • 8
  • 54
  • 65
7

postValue - can be used from anywhere

setValue - only from main/UI thread

Basically, postValue should be used only from background thread as it might be slower compared to setValue, which reacts faster.

I've wrote a snippet that handles both case:

/**
 * Live data thread-safe set-value
 * Context: https://stackoverflow.com/a/52227632/6688493
*/
fun <T> MutableLiveData<T>.assignValue(newValue: T){

    if(Looper.myLooper() == Looper.getMainLooper()) {
        this.value = newValue
    }
    else {
        this.postValue(newValue)
    }
}
touhid udoy
  • 4,005
  • 2
  • 18
  • 31
5

TL; DR

  • If you are working on the main thread, then both setValue and postValue will work in the same manner i.e. they will update the value and notify the observers.
  • If working in some background thread, then you can't use setValue. You have to use postValue here with some observer. More here
Aman Garg
  • 131
  • 2
  • 2
  • 5
    Not exactly. If you are working on the main thread then setValue and postValue have a some difference: setValue will set the value and notify the observers instantly while postValue posts the task that will set the value and notify the observes later (usually in a couple of millis). – guybydefault Aug 01 '21 at 14:46
1

In our app, we had used single LiveData that contains data for multiple views in an activity/screen. Basically N no of datasets for N no of views. This troubled us a bit because the way postData is designed for. And we have state object in LD that conveys to view about which view needs to be updated.

so LD looks like this:

LD {
   state (view_1, view_2, view_3 …),
   model_that_contains_data_of_all_views
}

There are couple of views (view_1 and view_2) that had to be updated when one event occurs..mean they should get notified at the same time when event occurs. So, I called:

postData(LD(view_1, data))
postData(LD(view_2, data)

This would not work for reasons we know.

What I understood is that basically one LD should represent only one view. Then there is no chance that you would've to call postData() twice in a row. Even if you call, the way postData handles it for you is what you would also expect (showing latest data for you in view). Everything fall well in place.

One LD -> one View. PERFECT

One LD -> multiple views THERE MAY BE A WEIRD BEHAVIOR

cgr
  • 4,578
  • 2
  • 28
  • 52
1

If setting the value will take a long time (if you have to retrieve additional data from a remote source that could be slow to respond, for example), use postValue() so you don't lock up the main thread.

When setting the value is guaranteed to be fast (as it most often is), setValue() is simplest and best.