I'm using Android Jetpack's new Paging library to display a list of items received from the API.
I want the data on the current page(say page 3) to be refreshed every few minutes without refreshing the whole list with invalidate() function, as this is taking the Recycler view to its first page.
The problem I see here is the Paging library assumes the data is immutable.
https://developer.android.com/reference/android/arch/paging/DataSource#updating-paged-data
On googling, I can see the workaround is to use Room and display the UI based on DB updates from the API call.
Is there any other suggestions to solve this without using a local store.
The only way to get rid of this issue is to introduce a persistence layer with Room. https://github.com/googlesamples/android-architecture-components/tree/master/PagingWithNetworkSample#paging-with-database-and-network
As per docs here : https://developer.android.com/reference/androidx/paging/DataSource#updating-paged-data
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.
https://developer.android.com/topic/libraries/architecture/paging/data#consider-content-updates
As you construct observable PagedList objects, consider how content
updates work. If you're loading data directly from a Room database
updates get pushed to your app's UI automatically.
Related
About Single Sources of Truth Google document said:
Using this model, the database serves as the single source of truth, and other parts of the app access it using our UserRepository. Regardless of whether you use a disk cache, we recommend that your repository designate a data source as the single source of truth for the rest of your app
https://developer.android.com/jetpack/guide?gclid=CjwKCAjwo4mIBhBsEiwAKgzXOH1Pq--Ws1PLzUiSP4RmDE6ByKfEi6mdXu5g86btqveIdJvvrgYuxBoCz8wQAvD_BwE&gclsrc=aw.ds#connect-viewmodel-repository
According to the document I save all data when I fected data from remote server and I only get data from room When I need to use in acitivty(In fact I collect flow which is defined in viewmodel).
It seems so good! It avoids the different data sources mix up together! But actually I found some strange question gradually:
In my App, I have a list that the server may change it(Because we have data manager website that admin can update or delete data). So in order to get the newest list data from server, I must clear all data stored in room and fect data again from remote server. This operation seems redundant: "why could I get data directly from remote server", I mean, I only get data from remote source is also a single sources truth. And also it cause a promble: my app will flash a moment because clear data make list empty and fect data from server make list full!
The most important thing is that it seems like the local data is not necessary because I must stay the newest list from remote server.
Some people may say that save data into room can make us app available offlice. I agree that, But in this place, my item of list is represent a image url, and after click the item, the app will jump to a new activity and display a ImageView base on the url we get from the list. If app offlice, the ImageView couldn't load the url also.
I am so confused I couldn't load all image url(use base64-url to avoid load invalid) in a moment also, because the data is so much. And if I say I need a search function in this list and I need load so much unbelievable data into my room, It seems so unreal and event fantasy!
In brief:
Room is a nessary? Couldnt just fect data from remote?
If room is nessary, how to solve problem I met, do my incorrect useage cause the problem?
Hi #psycongroo as I Understood your problem, and I want to share my experience:
You can handle any error with loading URL with placeholder I mean if you got an error with no Internet connection user will see placeholder, but in general libs like Picasso or Glide can cache images when it`s load one time, so the user will see the Image.
The question about why we need to use room instead of fetch data from remote directly. So from your question I don`t understand why you need to drop your local changes even they are completely new, user can have a low internet connection so he will see an empty list instead of previous data with for example progress indicator. And also if the user doesn't have the internet at all you can show some dialog to explain what the problem but old data is still present. If you are using, for example, RecyclerView you can update data with Paging 3 from google, and they update the only necessary items from your list.
P.S. let me know if that help, or you have another question.
I implemented a recycler view adapter using Android paging library 3 with Room and RemoteMediator, but my problem is that, the list takes a little while to load during initial load. If I removed the RemoteMediator with just data source from Room, the list loaded immediately.
What I want is to load data from Room first, then only fetch from server and insert into database afterwards if reaching end of page. Does anyone knows how to do this?
What version of the library are you using? As of 3.0.0-alpha03, REFRESH loads triggered by RemoteMediator and PagingSource are launched asynchronously so they shouldn't block each other.
If you're encountering this bug on alpha03, please share your setup and file an issue here: https://issuetracker.google.com/issues/new?component=413106&template=1096385
In Android paging library, how do I invalidate the subset of the dataset in a PagedList or a DataSource? I write PagedList or DataSource because I don't know which one I should call.
For example, I'm making a QA App called BufferOverflow.
I have a PageKeyedDataSource that load data over the network. It support pagination by the index on the API. In that App, I have a Top Question screen that has "infinite" list of top questions. Of course, the top question is updated periodically.
How to:
Update the dataset at page 1?
Update the dataset at page 10?
I understand that the benefit of using PagedList is it will do a calculation on which item is new/old, using DiffUtil. But, how do I tell it the dataset on certain key or position has changed?
Is what I'm asking impossible? Should I implement custom paging behaviour on my own?
Note:
I don't want to invalidate all the data.
adapter.getCurrentList().getDataSource().invalidate();
This will invalidate the whole data on data source. While I just want to invalidate certain part of the data.
I think API that I'm looking for is similar to RecyclerView.Adapter's
list.adapter.notifyItemChanged(0);
But of course, its not Adapter's responsibility to update the dataset. It's either on the PagedList, DataSource, or Dao classes..
The only way to solve this issue is to introduce a persistence layer with Room.
https://github.com/googlesamples/android-architecture-components/tree/master/PagingWithNetworkSample#paging-with-database-and-network
As per docs here: https://developer.android.com/reference/androidx/paging/DataSource#updating-paged-data
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 the 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.
https://developer.android.com/topic/libraries/architecture/paging/data#consider-content-updates
As you construct observable PagedList objects, consider how content
updates work. If you're loading data directly from a Room database
updates get pushed to your app's UI automatically.
I am currently running into some issues regarding the realm database and I don't know what the best practices are to tackle this problem.
So I have setup an app which communicates to a server and gets Post Objects through Retrofit stored into my Realm. This post feed is a core part of my app and I want to keep things locally to stay attractive while being offline. The thing is that I cannot store the entire feed list locally as this would be a massive chunk of memory. But I want to make sure that the User gets this content while scrolling through a recycler-view.
The recycler-view currently only shows local Posts and updates them if refresh is forced. I want to implement a load on scroll mecanism that loads Post objects into it while scrolling but this scroll objects should not be stored locally when the app closes.
I thought of creating an additionally in-memory Realm but there is another problem: A Post Object contains a ForeignKey to a UserObject. When I download this dynamic PostObjects and a UserObject changed over this time on the server I want to be able to reflect this changes to every other User on the local persisten Realm too. (To avoid having 2 different UserObjects for the same User)
My best idea i came up so far is to have a
Boolean field on the PostObjects set to true or false to indicate temporary state or not. After the applications closes I would drop all the temporary entries. Is this a viable solution or do I miss anything? I hope you understand my problem and can help.
Edit: My Realm Database contains two relevant objects:
User
Has a name (primarykey), id (global from server), imageUrls etc..
Post
Has a User who created it, again an id and its data (text, date, images...)
Edit2 : What I really need is a type of in-memory Realm that allows me on start up to clone another realm that is used to have data in case of no connectivity. Before the in-memory realm gets deleted I would then override the offline realm with the 5-10 last post entries of the temporary realm. Is such a thing possible?
I'm working on a movie app, when the user first open the app I would fetch the most current popular movie data and populate it in my ListView. The initial fetch provide a list of movies, but only provide the basic information to fill a ListView (id, name, rating, release data, poster image url). To get the remaining data (run time, tagline, description, movie website, etc), I would have to fetch from /movies/{id} endpoint for each of the movies. Also, for a list of trailer, I would need to fetch from /movies/{id}/videos. Currently I'm fetching the movie detail data when they select the movie on the list using AsyncTask and then setting the Views with the data on the initial detail loading. I'm caching the data in a Content Provider and SQLite.
My questions are:
How do I prefetch the data after getting the initial list of movie
from the first fetch? I'm currently using an AsyncTask for the
initial fetch, do I just start a list of AsyncTask in the middle of
the initial task?
Should I even be using AsyncTask for this situation or are there
better alternatives? It seems like a SyncAdapter is a good idea for
the initial fetch, but not sure what is best to use for the
additional detail/trailer.
I just started working with Android a few weeks ago, so I don't know if I should even be doing things this way or if there are better solutions. Currently everything works, but I'm not sure if I'm going down the right path. I did read this, but wanted a more specific way on how to prefetch data.
This sounds like a good use-case for Volley.
[1] - After you have done the initial fetch you can create your Volley RequestQueue and fire off a bunch of Requests (String/JSON) that you can then parse / cache the result as you already do. Volley is asynchronous, so you do not need to worry about where you add them to the queue, but the callbacks are performed on the UI thread so make sure they are reasonable (they may cause animation jank)
[2] - See above.