I am using live data to sync qty change between duplicate items on the same view. So if there are 7 items observing qty updates, and 3 of them have the same ids, if one of them is triggered all of the 7 will follow even though 3 of them should be refreshed.
My question is, would it be possible to subscribe to a specific value, instead of the whole field kind of like a topic subscription just so that those 3 items with the same id would only be triggered
When you observe a LiveData, your listener will get all updates to the object stored in the LiveData with every update. It's not possible to tell a LiveData to be "smart" about which specific nested values you want.
If you want to transform the object inside the LiveData into a different object that meets your needs, you can use Transformations.map() to create a new LiveData based on the contents of the one you have now. It will be up to you to make sure the code only generates new LiveData objects if something of interest changes in the original.
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
According to a response made by Yigit Boyar from Google, Live Data is not the best use case for a chat app, because it may lose displaying some items if they come at the same time. He recommends using the new Google's Paging Library. I've been using ItemKeyedDataSource for my inbox(all the people that have message the user), and the inside chat(the messages themselves). The problems are the following:
1- From the chat, when the user scrolls downwards, the user retrieves old messages, which means that the insertion of those messages should be in position 0 of the adapter, not sequentially like the paging library does. How can I alternate the position of the inserted items to be sequentially for new messages, and in position 0 for old messages?
2- From the inbox(people that have message the user), again I'm using ItemKeyedDataSource here, the problem is that I want to maintain the multiple document listener from the repository (I'm using Firebase Firestore), so I can detect every time a new person talks to the user. The problem is that callback.onResult is called only once, and fails when Firebase sends another user. How can I maintain an update-able list?
I understand that this answer is probably too late, but maybe it can help someone in future.
Position of item in RecyclerView is determined by the position of corresponding data object (of type T) inside PagedList<T>. PagedList is designed to look alike good old List<T>, but can be thought of as an "endless" list of elements.
PagedList gets its elements by pages on demand through something called DataSource.Factory. A Factory is used because DataSource by itself can only grow in one direction. If you need to prepend elements in PagedList, or change or remove existing elements you must invalidate the DataSource and a new instance will be created through DataSource.Factory.
So, to insert your data elements where you want them you should implement your own DataSource and DataSource.Factory by subclassing these base classes.
Note: Room, data persistence library from AndroidX, provides facilities to automatically generate instances of these classes for your data. You can write SQL query like this:
SELECT * FROM messages WHERE threadId=:threadId ORDER BY timestamp DESC
then get DataSource.Factory from this, use the factory to create LivaData<PagedList<Message>> and finally use the paged list to display messages in a RecyclerView in a chat application. Then, when you insert, update or remove data inside DB these changes will automatically propagate to the UI. This can be very useful.
I recommend you to read a few related examples a do codelabs:
https://codelabs.developers.google.com/codelabs/android-paging/#0
https://github.com/googlesamples/android-architecture-components/tree/master/PagingSample
https://github.com/googlesamples/android-architecture-components/tree/master/PagingWithNetworkSample
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 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
I have a project that loads a list from the server. This data will eventually be stored into a database, but for now is stored in memory in a MutableLiveData. A RecyclerView's adapter is watching the data and displaying it. So far everything is working as expected, using a FAB the user can post a new entry which will go at the top of the list, on success I get a 200 and here's the main part where I'm getting lost...
When I want to add a single item to a list stored in a LiveData, the observer is unaware of the delta. I currently make a call to RecyclerView.Adapter.notifyDataSetChanged(), though the ideal in my case would be to call notifyItemInserted(0) or in other cases I can see various other notifications. What the best way to do this? The lifecycle architecture library appears to be very well thought of, I assume I'm missing something simple. I can't imagine having to manually perform a diff between the lists?