What's the correct logic for updating a view on user event ?
Let's say :
In an app who didn't use cache (Room for example).
A list of object is displayed in a recyclerview. User touch the cross to delete an element.
What to do ?
Case 1 : Send a request to the server for deleting this element and on 200 response, updating the recyclerview (So delete locally the element in list after request). Which can lead to a slow experience depends on the internet connection quality.
Case 2 : Delete locally the element, notify the recyclerview and after that sending the request (So it's completely transparent for the user). In few cases the response can fail (timeout, etc.) so it's necessary to reinsert the element in the list and notify again the recyclerview.
As a young developer, I don't know which way to choose. I have a preference for the second case because the negative side is rarer but I am not sure of myself.
Thank's for your answer.
Related
I have a list with pagination which I implemented using Paging library. Items on this list can be modified (changed/deleted).
According to official documentation, I'm first changing in-memory list cache from which my DataSource gets pages and after that calling datasource.invalidate() in order to create new pair PagedList/DataSource:
If you have more granular update signals, such as a network API signaling an update to a single item in the list, it's recommended to load data from network into memory. Then present that data to the PagedList via a DataSource that wraps an in-memory snapshot. Each time the in-memory copy changes, invalidate the previous DataSource, and a new one wrapping the new state of the snapshot can be created.
It works and looks WELL if user modifies items on first page.
However, if user is on page two or further during datasource.invalidate() he will be thrown at the end of the first page.
Debugging shows this happens because new PagedList has only first page when it's submitted to PagedListAdapter.submitList. Adapter compares old and new lists and removes all items not from first page. It happens always but not visible for user if he is on the first page.
So to me, it looks like new pair PagedList/DataSource have no idea about number of pages which fetched previous pair and datasource.invalidate() doesn't fit for the situation in docs. Behavior that I see acceptable for cases then user updates all list (like swipe-to-refresh) but not
an update to a single item in the list
Has anybody faced such issue or somehow archived things I want? Maybe I'm missing some trick which helps me to get new PagedList already with all pages.
For clarification: library version 2.1.0. Custom PageKeyedDataSource based on in-memory cache and remote servise (No Room)
I want to share my research in case anybody is interested:
Issue ("lack of feature") is known, at least I've found the couple related discussions on official tracker one two
If you are using PositionalDataSource or ItemKeyedDataSource you should dig into the direction of requestedStartPosition/requestedInitialKey from initial params as this answer says. I didn't have much time to build the whole solution but those params are indeed different for initial load after invalidation
About my case : PageKeyedDataSource. Here you can read that there is no similar to requestedInitialKey params in this type of data source. Still, I found a solution which fits me, very simple, although, feels like a dirty trick:
When loadInitial() is called after invalidate() in-memory cache returns all already loaded pages instead of just first one.
At first I was worry that something will break if, for example, requestedLoadSize is 5 but the result is 50 items list but turns out it's just a hint and it can be ignored. Just don't forget to pass nextPageKey which corresponds to the last cached page and not the first one.
Hope it will help
With observable method you will only get first page list items....if you want to edit other items you can get that list by adapter.currentlist method.
Example:
private fun list():MutableList<String>{
val list = mutableListOf<String>()
for (value in videosAdapter.currentList.orEmpty()) {
val abc = value.snippet.resourceId.videoId
list.add(abc)
}
return list
}
I am new to android. In my android app, I showing the products list. Now I can sync all the products with a single API call. In slow networks, the app getting broken with the timeout error. I want to sync the products by chunks of data and I want to show a loader also. How can I achieve this?
Thanks in advance.
You're looking for something called Pagination. You can get a specific list/chunk of products from your API with a specified 'page' parameter. The 'page' parameter usually starts off with a value of 0 (page=0), then when the user scrolls to the bottom of the list, you increment the 'page' parameter and get the next chunk of data (page=1), So on as the user scrolls to the bottom of the list.
To show a lading to let the user know of data being loaded from the API, simply use a progress bar that can be shown whenever the API call is in progress and hidden when the API call is finished.
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 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?
How to create offline first RecyclerView in Android so the users still can see the list when they don't have any internet connection?
I already know how to get parse JSON and populate it in RecyclerView, the problem is, users still can't access it when offline. And if I use cache using SQLite, how to update only the latest data from the server?
for example, I already got 3 data from server and populate it in RecyclerView like this:
Data 3
Data 2
Data 1
After that, there is an update from the server so there are 4 data from the server. I only want to add the 4th data without load from the 1st. And its going to be like:
Data 4 --> only add this to the RecyclerView without reload past data
Data 3
Data 2
Data 1
Regards,
Elmer
I have done something similar in this project:
https://github.com/isaacurbina/MyMovies/blob/master/app/src/main/java/com/mobileappsco/training/mymovies/Fragments/ResultsFragment.java
Basically, I use an AsyncTask to load data as the user scrolls down, loading more content (kind of like 9GAG or Pinterest do) managing a "page" counter.
Then, as I receive the data, I join it to the List object using list.addAll(results), being results another ArrayList<> of the same kind.
Then you can use adapter.notifyDataSetChanged() on your RecyclerViewAdapter and it will add them super fast, or you can use an animation to show them being added slowly.
I hope it helps, kind regards!