8

Bottom Line Question

If I'm using MutableLiveData<List<Article>>, is there a way to properly notify observers when the title/content of an Article has changed, a new Article has been added, and an Article has been removed?

It seems the notifications are only possible when an entirely new collection is set on the LiveData, which would seem to result in a really inefficient UI refresh.

Hypothetical Example

Suppose the following...

My LiveData class looks something like this:

public class ArticleViewModel extends ViewModel {

    private final MutableLiveData<List<Article>> mArticles = new MutableLiveData<>();

}

I want to display the Articles in a list by using the RecyclerView. So any time my Fragment observes a change in the ArticleViewModel's LiveData it calls the following method on my custom RecyclerView.Adapter class:

public class ArticleRecyclerViewAdapater extends RecyclerView.Adapter<Article> {

    private final ArrayList<Article> mValues = new ArrayList<>();


    public void resetValues(Collection<Article> articles) {
        mValues.clear();
        mValues.addAll(articles);
        notifyDataSetChanged();
    }
}

Finally, my application will allow the user to add a new Article, delete an existing Article, and change an existing Article's name (which needs to be updated in the RecyclerView list). How can I do that properly?

Add/Remove Article

It seems the LiveData construct doesn't notify observers if you add/remove an item from the underlying Collection. It seems you'd have to call LiveData#setValue, perhaps the ArticleViewModel would need a method that looks something like this:

public void deleteArticle(int index) {
    final List<Article> articles = mArticles.getValue();
    articles.remove(index);
    mArticles.setValue(articles);
}

Isn't that really inefficient because it would trigger a complete refresh in the RecyclerView.Adapter as opposed to just adding/removing a single row?

Change Name

It seems the LiveData construct doesn't notify observers if you change the contents of an item in the underlying collection. So if I wanted to change the title of an existing Article and have that reflected in the RecyclerView then my ArticleViewModel would have to modify the object and call LiveData#setValue with the entire collection.

Again, isn't this really inefficient because it would trigger a complete refresh in the RecyclerView.Adapter?

Ed LaFave
  • 359
  • 3
  • 9
  • 1
    You can use diffutil over recyclerview old data and the new data that comes through observer. That ways you don't have to clear the underlying data set and call notifydatasetcahnged() . Diffutil will take care of appropriate notify data calls. – Gautam May 31 '18 at 17:17
  • Refer to this [answer](https://stackoverflow.com/a/62785873/1903740) and use it with DiffUtil – Mohamad Bdour Jul 08 '20 at 00:51

2 Answers2

0

Case1:

When you add or delete So when you add or delete the element in the list you don't change the refernce of list item so every time you modify the liveData item you have to update live data value by calling setValue method(if you are updating the item on main thread)or Post value(when you are updating the value on background thread)

The problem is that it is not efficient

Solution Use diffutil

Case 2:When you are updating the item property by editing the item.

The Problem

LiveData will only alert its observers of a change if the top level value has changed. In the case of a complex object, though, that means only when the object reference has changed, but not when some property of the object has changed.

Solution

To observe the change in property you need PropertyAwareMutableLiveData

class PropertyAwareMutableLiveData<T: BaseObservable>: MutableLiveData<T>() {
private val callback = object: Observable.OnPropertyChangedCallback() {
    override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
        value = value
    }
}
override fun setValue(value: T?) {
    super.setValue(value)

    value?.addOnPropertyChangedCallback(callback)
}
}

Two things to note here:

1.First is that our value is a generic type which implements the BaseObservable interface. This gives us access to the OnPropertyChangedCallback.

2.Next is that, whenever some property of the BaseObservable changes, we simply reset the top level value property to be its current value, thus alerting the observers of the LiveData that something has changed.

Roshan Kumar
  • 46
  • 1
  • 6
0

LiveData will only notify when its wrapped object reference is changed. When you assign a new List to a LiveData then it will notify because its wrapped object reference is changed but if add/remove items from a LiveData's List it will not notify because it still has the same List reference as wrapped object. So you can overcome this problem by making an extension of MutableLiveData as explained here in another stackoverflow question.

Md. Yamin Mollah
  • 1,135
  • 10
  • 21