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);
}
}
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.
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.
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.
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.
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.
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.
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)
}
}
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
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
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.
Related
I need to use flow.collectLatest {} in my fragment in OnViewCreated, and then in a loop under some condition multiple times (I made a filter, so each time different data should be retrieved).
This is my code for calling collectLatest:
viewLifecycleOwner.lifecycleScope.launch {
myViewModel.myFlow.collectLatest { pagingData ->
myAdapter.submitData(pagingData)
myAdapter.notifyDataSetChanged()
}
}
I use this block of code in both onViewCreated and in the loop.
However, it gets called only once, in OnViewCreated.
In the loop, sometimes it gets called, and then it needs 2-3min to retrieve data, but most of the time nothing changes.
I guess it could be an issue related to needing much more time to retrieve data, or it just shouldn't be used this way.
Some of the possible solutions I tried, but didn't work:
using delay
adding flowOn(Despatchers.IO) in the end of the flow
switching flow call to a different thread
You don't need a loop for this, should only collect it on onViewCreated() once time. You should have two flows(one for your filter and another for your data) and to use switcMap(), and your adapter/view should call viewModel to notify any change that to be done.
Here an example:
//you can use any object type for your filter, in this example i used a sealed class
private val _transactionFilter = MutableLiveData<TransactionFilter>(
TransactionFilter.TransactionsByDate(Date())
)
val transactions: LiveData<Data> = _transactionFilter.switchMap { filter ->
//code to return data
}
Kotlin Flow's have a switchMap() too, i used liveData because flow.switchMap was experimental yet.
Another thing: you don't need call notifiyDataSetChanged() when using ListAdapter
I have created a Room database and i am trying to implement the following functions
public int getRowCountSenderHelper()
{
return senderHelperDao.checkRowCount();
}
public SenderHelper getOneChat()
{
return senderHelperDao.getOneChat();
}
Now the android is giving me this error.
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
Now i cannot use AsyncTask since they are deprecated and i cant call senderHelper.getOneChat() and senderHelperDao.checkRowCount() in a Thread because it need to return a value and i fear then when new thread goes to work in background the main thread may return empty value.
Any way to tackle this problem will be appreciated
1- Define a variable of type LiveData and make a getter method for it.
2- make return that variable.
3-use getter to get the value of LiveData from your activity.
in my Dao I've defined a Query like this to check whether the database is empty or not:
#Query("SELECT * from meal_table LIMIT 1")
suspend fun getAnyMeal(): LiveData<Array<Meal>>
Within my populateDatabse function I would like to check, whether any item is inside my database with something like this:
suspend fun populateDatabase(mealDao: MealDao) {
if ((mealDao.getAnyMeal()).size < 1)
...
}
Unforunately size doesnt work in this context unless I am doing something wrong.
If someone has a tipp on how to solve this I would apreciate it! Thank you!
Unforunately size doesnt work in this context
It is because getAnyMeal returns you LiveData which has no property named size. LiveData is an observable object. It means that LiveData object you get by calling this method will return to its observers (only the ones who "subscribed" to updates) an array of Meal objects when this array will be available.
First of all, when you are using LiveData with Room you sort of giving Room a signal that you are not requesting a response immediately. LiveData is used when you want to get updates in future that will happen on change of any object in DB. Also you may want to use LiveData when you want to execute the SELECT query asynchronously. It means you call getAnyMeal method and it does not block UI thread.
When you are using suspend keyword you can remove LiveData from return type. When suspend function is executed it will synchronously return you the result.
Solution
Update getAnyMeal() to the next form or create the new method as it is declared below:
#Query("SELECT * from meal_table LIMIT 1")
suspend fun getAnyMeal(): Array<Meal>
If you declare getAnyMeal method like this you will be able to call size property on the return type as it directly returns you an array.
On LiveData with Room:
Room doesn't support database access on the main thread unless you've called allowMainThreadQueries() on the builder because it might lock the UI for a long period of time. Asynchronous queries—queries that return instances of LiveData or Flowable—are exempt from this rule because they asynchronously run the query on a background thread when needed.
Observable queries with LiveData for more info.
LiveData from architecture components defines an Observer with nullable value for the receiver callback:
public interface Observer<T> {
/**
* Called when the data is changed.
* #param t The new data
*/
void onChanged(#Nullable T t);
}
Why is there an explicitly nullable annotation?
The doc of LiveData.observe() also says:
If LiveData already has data set, it will be delivered to the observer.
E.g. Observer waits for non-nullable updates or immediately receive previous non-nullable value, that should hold especially in Kotlin, until I define T as nullable.
The code seem to be working like that. I understand why this doesn't hold for LiveData.getValue(), which may be called manually before first data is delivered (and checks therefore for mData != NOT_SET to return a null).
So the second question is: Is is safe to assume the value is non-null in Kotlin when T is non-nullable?
I think the fact that they made it Nullable is that they wanted to add the functionality for those who want to reset the liveData by nulling it's value. Also someone might want a nullable LiveData (and use that null in observe).
If you are creating/producing the LiveData, you could assume it is null (and use the !! operator) since a null would indicate an unexpected error. Also you could create a class like NonNullLiveData that ignores the values that are null in it's setValue. That way you could be sure that you never recieve nulls in your observe (although you can't make the #Nullable go away from the observer).
Fixed in androix.lifecycle 2.0.0-beta01.
Please report android Team if you encounter any issues.
Starting from 2.0.0-beta01 and later androidx.lifecycle version the onChangedparameter no longer contains #Nullable annotation. The changes is due to the improvement request courtesy by the OP.
package androidx.lifecycle;
/**
* A simple callback that can receive from {#link LiveData}.
*
* #param <T> The type of the parameter
*
* #see LiveData LiveData - for a usage description.
*/
public interface Observer<T> {
/**
* Called when the data is changed.
* #param t The new data
*/
void onChanged(T t);
}
Is is safe to assume the value is non-null in Kotlin when T is non-nullable?
It depends, if you're the one who created the LiveData subclass or plainly using the existing MutableLiveData and you design it that it will never return null then it is safe to assume that it will never return null.
For the case that the LiveData is implicitly created by not you especially those LiveData that are provided by Libraries, I would not assume it is non-null unless the library documentation mentioned it.
This is about Task.
What's the difference between task.continueWith() and task.continueWithTask(), can you provide an example for each one?
The primary difference between continueWith and continueWithTask is one of the generic types of the Continuation you pass to it.
You can think of a Continuation as something that converts some input type to some output type. If you define a Continuation<IN, OUT>, where IN is the input type passed to its then method via a Task<IN>, and OUT is the type that method returns.
When calling continueWith, you pass a Continuation<IN, OUT>, and the then method is expected to compute and return the OUT value given a Task<IN> value as input. You might choose to do this if you don't have any blocking work to do for the conversion, such as reducing an integer array to the sum of its elements or counting the number of words in a String.
When calling continueWithTask, you pass a Continuation<IN, Task<OUT>>, and the then method is expected to return a Task<OUT> that eventually generates the OUT value, given the IN value as input. You might choose this if you are able to delegate the conversion work to an existing reusable Task.
Practically speaking, you aren't required to choose one or the other to do your work. It's a matter of preferred style, or if you have a nice Task ready to delegate your conversation rather than a Continuation. Typically you only use a Continuations if you have a pipeline of conversions to chain together.
The javadoc links here show some examples of Continuations. Also, to learn more, you can read about them in part three of my blog series. To be fair, continueWithTask is the only part of the Task API I don't directly discuss anywhere in that series, mostly because conceptually it doesn't differ very much from continueWith.
Just to add to what Doug said, I would put it like this:
continueWith will wrap the result of the then method in a Task. continueWithTask will not; it expects the then method to return a Task, and thus avoids the double wrapping of a task within a task.
continueWithTask is perfect when you want to use a Continuation and a TaskCompletionSource together.
I'd like to add, that continueWith and continueWithTask really got me into trouble, obviously because I did not truly understand the API, but also did the naming confuse me. Maybe an example of my failure can prevent others from doing the same.
tl;dr
When to use which method:
Use continueWith if you want to use the result of the previous task and return a new result within your Continuation's then method. And you need to hand it over to some other continuation or use it afterwards in listeners. The return value of continueWith is a Task that just WRAPS your return value of the then method.
Use continueWithTask if you want to use the result of the previous task,
and somewhat use it in a new task, that you create within your Continuation's then method. The return value of continueWithTask is a task that YOU create inside of then and has an own generic result.
Do NOT return a task in your continuation's then and use it with continueWith. It might compile and execute for years without a warning, but also without doing it's job.
IF you directly append a listener after continueWith, this listener
will give you a task THAT ONLY WRAPS the result of your then return
value. If this return value is a task itself, do not expect it to be
executed(!!!).
Long story!
I had a calling chain like this:
private Task<SomeResult> getTask() {
PreloadingTask.create().continueWith(additionalTask()).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
source.setResult(someResult);
} else {
source.setException(new Exception());
}
});
return source.getTask();
}
So as you can see, additionalTask() must return some sort of Continuation<IN, OUT>, that implements the method then as
#Override
public OUT then(Task<IN> task){ ... }};
In my case I did not need to check for OUT, because I just wanted to do some additional computations AFTER the PreloadingTask was done and forwards it's result to my additionalTask() continuation.
I wanted to execute the task from additionalTask(), and afterwards the onCompleteListener should have been called.
private Continuation<PreviousResult, Task<Void>> additionalTask() {
return task -> {
PreviousResult r = task.getResult();
simpleResultModification(r);
return new AdditionalTask(r);
);
};
}
What happend? The onCompleteListener was called directly, because my then method got executed and returned it's result, which was an instance of AdditionalTask.
This AdditionalTask then got wrapped into another task and handed over for the onCompleteListener as the result.
And my AdditionalTask got never executed.
That's why you should use continueWithTask if you return a task, within then.
There's a big difference between continueWith() and continueWithTask() related to task cancellation.
More info here - Task API docs.
continueWith() : If the previous Task is canceled, the returned Task will also be canceled and the Continuation would not execute.
continueWithTask() : If the previous Task is canceled, the Continuation would still execute and task.isCanceled() is true can be observed in the Continuation.