I have a RecyclerView with elements updated via WebSocket. The update can affect several elements of the list in different places. I'd like to used DiffUtil to update elements of the RecycleView. The socket update itself doesn't contain the whole list element structure but just a few fields. So in order to update I need get the current data list from the adapter, look up for a elements that needs to be updated, update the fields and pass the new list into DiffUtils to compare with the current one. The problem is that when I update object it also automatically updates in a RecyclerView adapter because it's kept as a reference. So when I get the update from WebSocket I already don't have an "old" list to be compared with updated one.
Finally, I have found solution on Medium. Here it is https://android.jlelse.eu/rxjava-and-immutable-diffcallback-in-android-f4637078b03b
Related
Working on a project where we need to have a list of items in a view model. The type is a custom data class.
Until now, I have used a MutableLiveData to store the state of the list. The list will be updated when a user scans an item on an RFID reader that we have connected to the device. When scanned, an item will be fetched from a server and the list will be updated accordingly.
I want to try and move on to using a StateFlow instead, but I'm running into an issue here.
When a new item is scanned, I'm updating the mutable list with the add command and later on updating the corresponding item in the list. This does not trigger the LiveData or StateFlow observers/collectors - but with LiveData I could assign the list to itself after I'm done with updating it. (_listOfItems.value = _listOfItems.value) as this would notify observers.
This does not work with StateFlow however, since it only trigger when the reference changes (which it doesn't since it's the same list, just with new/changed items in it).
I'm aware that there are probably some issues with using a mutable list and coroutines to update the same list, since it might collide if trying to update the list at the same time, or something like that.
So, generally the question is: what's the best approach when having a list in a view model and having the UI update whenever the content of the list changes?
It would be impossible to use a MutableList in a StateFlow and make it update because StateFlow always does an equals comparison with the previous value, and since the previous value is an instance of the same list, they will always be considered equal.
You could make this work with SharedFlow, which does not compare values with the previous.
It is however very error prone to use mutable classes with a SharedFlow/StateFlow (or even LiveData) in general because the data could be getting read from and written to from multiple different places. Very hard to manage.
One solution is to create a copy of the list every time you send it to the flow:
myMutableStateFlow.value = myMutableList.toList()
If you want code that's even easier to manage, possibly at the expense of performance, don't use mutable Lists at all. Use read-only Lists. You could put it in a var property, or just modify the value of your StateFlow directly.
myMutableStateFlow.value += someNewItems
// where myMutableStateFlow's type is a List, not MutableList
I'm working with the Room library. When I fetch some data, I get a List of Entities, that I present in a RecyclerView. The user is able to delete elements presented in that RecyclerView, which is implemented with a #Delete method in the DataAccessObject.
In order to also remove the deleted element from the RecyclerView, I also have to remove the element from the List of Entities mentioned earlier. And this what I don't like. I have to manually keep two data structures in sync.
Is there a way to link the RecyclerView directly to the a certain set of data in the database? Without an intermediate data structure I have to sync manually, thus when I delete an element in the database, this is immediately reflected in the RecyclerView.Adapter?
Room supports observable queries. So instead of returning list of entities in your dao object, return LiveData<List<Entity>> or one of the supported RxJava2 types and you'll get updates every time data is changed in the db, e.g. when item is removed.
It's convenient to use this api with ListAdapter, which wraps AsyncListDiffer to calculate diff on every list update, so you'll get RecyclerView animations for free.
I was reading the doc about observing queries.
Query<Task> query = taskBox.query().equal(Task_.completed, false).build();
subscription = query.subscribe().observer(data -> updateUi(data));
From what i understand , this code returns all the data every time. but for RecyclerView add/remove animation to work, we need to know which data is changed and we need to know what kind of change is happened to data (remove/change/add).
is there anyway to get changed data only?
It's not a responsibility of ObjectBox to define a change. There is DiffUtil that responsible for that in android. If you google that you can find tons of examples (e.g. sample). The only advice there is to put DiffUtil payload to background thread if your list contains lots of items or items are fat(contain dozens of fields).
I am currently creating a new adapter with the list of objects returned by Parse and then doing a .setAdapter(). This unsettles me because it seems like I'm recreating the adapter unnecessarily as opposed to modifying the existing list reference then just calling .notifyDataSetChanged().
The only other thing I can think of is comparing the old list with the new and adding/deleting objects to update the state of the old list based on the objects that I'm returned by Parse. Is the best practice way to do this?
I'm also hesitant to just add the item to my list because I don't want to go down this path of possibly having inconsistent data between the Parse server and my client.
I guess what I'm ultimately looking for is whether there is a callback Parse provides if the list of objects gets updated.
[screenshot here] I just have this visceral feeling that I'm committing an Android crime
With ListView we have had a good native pattern to map some data from db to list:
DB -> ContentProvider -> CursorLoader -> CursorAdapter -> ListView
This approach was good in terms of data layer separation, performance and automatic data updates. But this pattern doesn't really fit new RecyclerView. There are some approaches to mimic old behavior:
Using the recyclerview with a database
But there is a problem with using old style notifyDataSetChanged with RecyclerView. It can't use ItemAnimator features, it loses scrolling position, and it's just ineffective.
So, how we can benefit from finegraned change notifications while using DB wrapped in ContentProvider? Cursor is static, and to get new data from it we need to get new Cursor. So, it seems that we will need an custom intermediate data layer, which will merge data from Cursors and expose the List of entities to RecyclerView.Adapter. Also, we will have to manually map ContentObserver onChange() events to RecyclerView notifications. This also means that we will have to get rid of CursorLoader. That is an incredible amount of work for such basic task.
Is there any better solution?
You can use the DiffUtils class to compute the differences between the old and new cursor.
When using it you just need to implement two methods :
areItemsTheSame() to know if two items represent the same logical item (even if the content is different). Usually you would base the answer on an identifying field of your item;
areContentsTheSame() to know if two items representing the same logical item have unmodified content.
Once the differences are computed, you can then just apply it to your adapter and it will automatically call the notifyItemChanged(), notifyItemInserted() or notifyItemRemoved() accodingly.