I have an application which fetches data from an API. SO basically, right now, the app works as such:
If connected to the internet, fetch data and use Android Room to store for offline use
If not connected to the internet, check if data exists in Room. If exists, display it. If it doesn't exist, display an error message.
I did some research online on how to implement an efficient offline storing policy and Google suggests to use Work Manager to queue requests and then send it when connected.
I actually want to know how to implement this ? (not the code but the logic, i.e should i schedule requests everyday to the API or every time it's connected to the internet ?)
If someone with experience with offline apps could help would be great.
My network requests are done through Retrofit and i already create a class that perform calls to the API.
Keep in mind WM (work manager) is designed to perform operations when certain conditions are met (e.g.: the user has enough battery, the display is off, etc.). So this may end up with your data not being updated when you need it. WM is good for operations you want to happen but are not critical to occur "right now". I'd say always use the Room DB as the single source of truth. If the data is in room, show it, if it's not, fetch it, if you can't, well, you tried. Send a message to the user. You can use a NetworkConnectivityListener to monitor connectivity and check if you have a pending query (you could store the parameters of this query in your Room database in another table for ease of use). So you'd query the DB, obtain the pending queries (if any) and execute them, update the data, and let the ViewModel/Repository decide if there's a context to show this data (UI).
I feel like you are very close to achieve what you need.
So in other words:
UI: Observes its viewModel for some sealed class xxx state to tell it what to do (show an empty list, show an error, pass data to a recyclerview adapter, etc.).
ViewModel: Using its viewModelScope.launch { ... } will call a repository.fetch(...) or similar. Your viewModel will fetch this data when the Fragment tells it to do so (e.g. the user pressed a button) or on some lifecycle event (onStart for example).
The Repository in this case normally exposes a flow (if you can use the experimental api) or a suspend function that can perform the following actions (that can vary depending on your business rules)
If the data is available in the Database, return it immediately.
If the data is old (or we still want to refresh it), then perform the network API (if there's connectivity to do so). If there's No connectivity, you could store this "pending" query in the database for later. You could also check if you have a pending query before doing any of this, perhaps it's outdated or perhaps you need to execute it.
In any case, once the query goes through, you insert the results in the database, and call the same method you used in step 1.
Don't forget to update your "pending" query if you had one (or if you use this).
With WorkManager, you could schedule the "fetch data from API" part to happen at some point (so your data will be kept more up to date), but I all really depends on the use-cases you have.
Related
I have a fragment and the data displayed are fetched from a network call.
I am using onSaveInstanceState in order to avoid fetching the data again when the orientation changes, but if I understand the lifecycle for fragments correctly as long as the app is never destroyed (either explicit by the user or because the Android OS kills the activity due to lack of resources) the data from the server will never be refreshed.
If I have understood this part correctly, I would need to define some way to periodically refetch the data from the server or is there another way?
There are no rules about data refresh. It depends on your app, your data, etc. If you fetch a list of receipts, you do not need to implement a refresh mechanism. But if you fetch, I don't know, the exchange rate of currencies, you must have one.
What about a pull to refresh pattern ?
I would use the Android Architecture components ViewModel and LiveData to save the data during screen rotations preventing multiple calls to the server for screen rotation changes. This is the preferred method Google seem to be pushing to their developers https://developer.android.com/topic/libraries/architecture/saving-states
To prevent the data from going stale I would either let the user decide when to update using the "Pull to Update" (as described in the other answer) or add a timer to update if the fragment has been in the foreground for an extended period of time using the method described here: https://guides.codepath.com/android/Repeating-Periodic-Tasks
I have a data field and time field ,user Enter data and time
After Writing data on firebase realtime Database I want to reset data field and Time Field to 0 after The specifed time in Time Field
I read about cloud function but I don't know to solve my problem
I'm new to firebase and it's cloud function Please help...
Data field will contain somedata
Time Field Will Contain data like 20 ,30 or 40 which represent time in mins
so after writing both to database
say if time field contain 20 ,so after 20 min to writing data in database, it should be reset to 0
Thanks in advance
The scenario you describe could be achieved in two ways. In my opinion, one can be cheaper and easier (no function involved but need to add some logic and a query to data on client side) if you could implement it which depends on your case.
Not the cheapest one: Schedule (cron type jobs) to scan your database regularly and check for the specified time and reset your data if condition is met. And this requires you to use Firebase functions and cron type services to configure it.
The cheaper way (although dependent on your case) in my mind would be to set your logic client side, so whenever a user is navigating to that data , check for the time and if condition is met reset the data client side without client noticing. This way, you don't need to set up functions, and you are not performing anything as long as no client has gone there (wherever that the data your mentioned is used in your app) Just keep in mind that this depends on whether you can have such scenario or your data has to be update regardless of users interaction with it. I have managed to redesign stuff whenever I have come across a case that needs periodic updates, etc.
More info on second option:
Imagine your users are checking for an order which can expire after some time. Your intention is to reset data when expiry time has arrived. Instead of resetting data via functions, etc, you can write logic so that whenever a client queries the orders, you check for expiry time and if expired, you perform what has to be done there and then, in addition to making sure your client won't see the expired order. Hope this makes it more clear. It's sort of a passive way of updating your data in db.
I'm using Firebase's realtime database on Android and the way I understand how it works is that even if the app disconnects from the network, Firebase will simply queue the transactions that the user has initiated and then perform then when connectivity is resumed. This works really well but if the app is closed then this queue seems to be discarded.
The Firebase docs on handling offline capabilities states the following:
Transactions are not persisted across app restarts
Even with persistence enabled, transactions are not persisted across
app restarts. So you cannot rely on transactions done offline being
committed to your Firebase Realtime Database. To provide the best user
experience, your app should show that a transaction has not been saved
into your Firebase Realtime Database yet, or make sure your app
remembers them manually and executes them again after an app restart.
But as far as I know, there is no way of knowing whether or not data has finished being written to the database.
How exactly would you go about making the app manually remembering what still needs to be written to the database? Is there some way of accessing the queue of transactions that is yet to be carried out? Or is there some way of keeping the app running in the background after being closed that could just sync the data when connectivity resumes?
Thanks in advance.
But as far as I know, there is no way of knowing whether or not data has finished being written to the database.
There actually is. The Transaction.Handler interface has a [onComplete method](https://firebase.google.com/docs/reference/android/com/google/firebase/database/Transaction.Handler.html#onComplete(com.google.firebase.database.DatabaseError, boolean, com.google.firebase.database.DataSnapshot)). The boolean that is passed to that argument is a flag to indicate if the transaction was committed:
committed
True if the transaction successfully completed, false if it was aborted or an error occurred
For more information, see the Firebase documentation on transactions.
I think I had the problem you are facing, in my case was a simple confusion. That Firebase warning is not about "transactions" in general, is about the "transaction" method provided by them.
In Android this is reference().runTransaction().
The "transaction" method is used to validate data first, by example, if more than one user can subscribe to an event simultaneously, you can make sure that the last vacant was available.
Since the "transaction" method query the database gives you the data, and the upload data, if there is no network connectivity there is no way to make sure that will work on app restart because there was never a first query to see the data you have to validate.
This seems logical to me, a "transaction" method will create a sort of bridge between the client and the database, this is not random, but because is part of the business logic, then you should warn the user visually that their changes might not be saved since it is offline, or even if it is sensitive not allow the user to do it.
In other cases, the data is indeed stored locally and then uploaded when the app is restarted. So if you do something like
reference.child(key).setValue(myObject);
Thant change will be local until the next time user has an internet connection.
You have to make sure to add the keepSynced to the references you actually need. Setting the syncing to the root, won't solve the problem as a waterfall, make sure to be specific with nodes you need to keep synced, this way the user will see the changed reflected visually in the app.
//Won't work
DatabaseReference root = FirebaseDatabase.getInstance().getReference();
root.keepSynced(true);
//This will work
root.child("event_list").keepSynced(true);
root.child("user_events").child(uid)keepSynced(true);
I'm writing my own ContentProvider which will be synced to a web service using a SyncAdapter.
Problem happens when the sync adapter is modifying the content provider's data the provider triggers a network sync when internally calling getContentResolver().notifyChange causing a sync loop.
The notifyChange with the network sync flag is required for when a client application does the modification but should be avoided when the sync adapter is modifying.
How can one, inside a contentprovider, easly tell if it's being used by a client application (which should trigger network sync upon modification) or by a sync adapter (which should not trigger network sync).
Currently I'm using different CONTENT_URI's (sync adapter accesses the data using a CONTENT_URI_NO_SYNC and client apps using a CONTENT_URI) to be able to distinguish between the two types of access and set the network sync flag accordingly.
Watch this video about REST API usage in SyncAdapters.
The method they discuss is to add a set of metadata flags columns to the database. This allows us to do 3 things.
The flags themselves allow the SyncAdapter to determine the rows that need changes and what those changes are. How do you tell the difference between a locally created row and a locally modified row? Furthermore how do you know which REST API call to make? If you just delete a row, how does your SyncAdapter know the row to be deleted if the data is now gone? Instead, set the "Should be deleted" flag, and then, when the SyncAdapter runs, it knows to push a delete to the server.
The flags allow your CursorAdapter to modify the view that is created (like adding a Spinner to show that "This row is being synced")
Finally, and this they don't point out, the flags allow you to tell why the row is being modified. If none of the flags are set and the row changes, it must have been because of an update from the server. Therefore, no need to sync to network.
So, the two workflows are as follows:
Local change
App creates new row. Row "create" flag is true.
ContentProvider stores the row, sees create flag and so it calls notifyChange(...,true);
Sync to network = true (the final parameter) causes SyncAdapter to fire.
SyncAdapter scans the database, finds the row with create flag set and performs appropriate server action. After success, SyncAdapter clears the flag.(row update on ContentProvivder)
ContentProvider sees the flag clear, no flags are left set, so it calls notifyChange(...,false);
ContentObservers see the flag change, update to look like "sync finished"
All these steps are equivalent for update / delete -- one flag per syncable row for each of create/update/delete.
Also notice the other win -- what if "Create" fails temporarily? server down... How do you know to retry? -- Simple, you don't clear the "Create" flag and you see it 15 minutes later.
Remote Change
SyncAdapter fires due to periodic sync.
SyncAdapter fetches an update from the server. Pushes changes into the database. Doesn't set any flags. ContentProvider sees the lack of flags, knows the change must have come from the server (or isn't a database change that needs to be pushed to the server), so it calls notifyChange(...,false);
ContentObservers see the content change and so they update with new row data
I have an application which uses a SyncAdapter to make a REST call to remote server, then use a Content Provider to persist the updates to the local SQLLite DB.
I can trigger the call by going the Accounts & Sync, then selecting my adapter, and using the Resync button to trigger the call
How does the system know when (how often) to make the onPerformSync() call in the SyncAdapter?
I am logging on the service to which the REST call resolves, but I am not seeing any calls unless I do it manually as described above.
Btw, i am running in the emulator at the moment, i have not installed this on a device yet
From your ContentProvider you must let your SyncAdapter know there was a change to the dataset.
You do this by calling notifyChange()
For example within your ContentProviders insert() method:
getContext().getContentResolver().notifyChange(rowUri, null);
It may not sync right away, but that is just how SyncAdapters work. The system decides when is a good time to sync. Usually it happens relatively fast in my experience. Of course you may not want it to sync every single time a change happens in your database, but you can always put code into your performSync to control the frequency of the syncs.
Hope this helps someone :)