Różnica setValue () i postValue () w MutableLiveData

113

Istnieją dwa sposoby, aby zmienić wartość MutableLiveData. Ale jaka jest różnica między setValue()& postValue()in MutableLiveData.

Nie mogłem znaleźć dokumentacji dla tego samego.

Oto klasa MutableLiveDataAndroida.

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
źródło

Odpowiedzi:

191

Na podstawie dokumentacji:

setValue () :

Ustawia wartość. Jeśli istnieją aktywni obserwatorzy, wartość zostanie do nich wysłana. Ta metoda musi być wywołana z głównego wątku.

postValue () :

Wysyła zadanie do głównego wątku, aby ustawić daną wartość. Jeśli wywołasz tę metodę wiele razy, zanim główny wątek wykonał opublikowane zadanie, wysłana zostanie tylko ostatnia wartość.

Podsumowując, kluczową różnicą byłoby:

setValue()metodę należy wywołać z głównego wątku. Ale jeśli potrzebujesz ustawić wartość z wątku w tle, postValue()należy użyć.

Sagar
źródło
„wysłana zostanie tylko ostatnia wartość”. Nie mogę być tego pewien, czytając kod. Wygląda więc na to, że gdy pierwszy wątek ma trafić w wewnętrzny zsynchronizowany blok wewnątrz postValue (), następne okno procesora może zostać potencjalnie przekazane wątkowi 2, który wysyła inną wartość. Wątek 2 może wtedy zakończyć zsynchronizowany blok, a program planujący daje pierwszemu wątkowi okno do samodzielnego uruchomienia. Teraz zastępuje to, co już napisał wątek 2. czy to możliwe?
stdout
103

Wszystkie powyższe odpowiedzi są prawidłowe. Ale jeszcze jedna ważna różnica. Jeśli zadzwonisz postValue()na pole, które nie ma obserwatorów, a następnie zadzwonisz getValue(), nie otrzymasz wartości, którą ustawiłeś postValue(). Dlatego zachowaj ostrożność, jeśli pracujesz w tle bez obserwatorów.

w201
źródło
3
Chciałbym móc zagłosować trzykrotnie za! Na tej podstawie wydaje się, że najlepiej jest używać, setValue()jeśli to możliwe, i ostrożnie używać metody „postValue ()” tylko wtedy, gdy jest to konieczne. Dzięki
jungledev
1
Nie, tutaj nie ma żadnego „najlepszego” sposobu. Jeśli pracujesz z LiveData z wątku w tle, powinieneś użyć postValue. Również w najnowszej wersji komponentów cyklu życia zostało to naprawione ... prawdopodobnie.
w201
„Również w najnowszej wersji komponentów cyklu życia to naprawiło ... prawdopodobnie.” Czy masz więcej informacji na ten temat? Dzięki
Chris Nevill
1
Zrobiłem kilka testów i wygląda na to, że w ostatniej wersji lib wszystko działa tak, jak powinno.
w201
Czy możesz mi pokazać konkretnie powyższy kod? Jeśli w ViewModel, zaimplementowałem w ten sposób noObserveLiveData.postValue("sample"), w działaniu, kiedy użyłem metody getValue w stylu viewModel.noObserveLiveData.getValueCzy masz na myśli Czy to nie jest wartość, którą ustawiłem w postValue () ("sample")?
kwmt
15

setValue()jest wywoływana bezpośrednio z wątku wywołującego, synchronicznie powiadamia obserwatorów i LiveDatanatychmiast zmienia wartość. Można go wywołać tylko z MainThread.
postValue()używa wewnątrz czegoś takiego new Handler(Looper.mainLooper()).post(() -> setValue()), więc działa setValueprzez HandlerMainThread. Można go wywołać z dowolnego wątku.

Ufkoku
źródło
11

setValue()

Ustawia wartość. Jeśli istnieją aktywni obserwatorzy, wartość zostanie do nich wysłana.

Ta metoda musi być wywołana z głównego wątku .

postValue

Jeśli potrzebujesz ustawić wartość z wątku w tle, możesz użyć postValue(Object)

Wysyła zadanie do głównego wątku, aby ustawić daną wartość.

Jeśli wywołasz tę metodę wiele razy, zanim główny wątek wykonał opublikowane zadanie, wysłana zostanie tylko ostatnia wartość.

AskNilesh
źródło
6

Nie jest to bezpośrednia odpowiedź na powyższy problem. Odpowiedzi udzielone przez Sagar i w201 są niesamowite. Ale prosta zasada, której używam w ViewModels dla MutableLiveData, to:

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);
}

Zastąp mutValżądaną wartością.

Himujjal Upadhyaya
źródło
Fajnie, podoba mi się to. W Kotlin stworzyłem rozszerzenie, które zawiera inteligentną aktualizację, dzięki czemu liczne aktualizacje wartości w całej mojej aplikacji są jednym, spójnym połączeniem.
19Craig
4

setValue()metodę należy wywołać z głównego wątku. Jeśli potrzebujesz ustawić wartość z wątku w tle, możesz użyć postValue().

Więcej tutaj .

Levon Petrosyan
źródło
0

W naszej aplikacji użyliśmy pojedynczych danych LiveData, które zawierają dane z wielu widoków na aktywności / ekranie. Zasadniczo N liczba zbiorów danych dla N liczba widoków. Trochę nas to zaniepokoiło, ponieważ sposób, w jaki postData jest przeznaczony. I mamy obiekt stanu w LD, który przekazuje pogląd o tym, który widok należy zaktualizować.

więc LD wygląda tak:

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

Istnieje kilka widoków (widok_1 i widok_2), które musiały zostać zaktualizowane, gdy wystąpi jedno zdarzenie… oznacza, że ​​powinny otrzymać powiadomienie w tym samym czasie, gdy wystąpi zdarzenie. Więc zadzwoniłem:

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

To nie zadziała z powodów, które znamy.

Zrozumiałem, że zasadniczo jeden LD powinien reprezentować tylko jeden pogląd. Wtedy nie ma szans, żebyś musiał wywołać postData () dwa razy z rzędu. Nawet jeśli zadzwonisz, sposób, w jaki postData obsługuje to za Ciebie, jest tym, czego również byś się spodziewał (pokazując najnowsze dane w widoku). Wszystko jest na swoim miejscu.

Jeden LD -> jeden widok. IDEALNY

Jeden LD -> wiele widoków MOŻE BYĆ DZIWNE ZACHOWANIE

cgr
źródło