To Synk or not to Synk
A guide to intelligent syncing of data in your offline-first Android apps
To build an offline-first Android app, which is usable in flaky or no internet connectivity, we need to locally cache our app’s data. Majority of the time, this data needs to be synced as much as possible and practical with fresh data from some type of back-end.
This is where the dilemma for us Android developers kicks in: Sync the data inadequately and your user sees stale data; Sync too many times and drain the user’s battery as well as waste your server bandwidth.
What are some of the strategies we can implement to handle syncing in our Android apps?
- Server pings clients when data changes — The official google/iosched app was the first open source application where I saw this being implemented. The server sent a silent GCM to all the users triggering them to sync the data. This approach makes sense if your data (like the I/O Schedule) does not change very often.
- Client auto-fetches data at regular intervals — There are brilliant tools like evernote/android-job, firebase/firebase-jobdispatcher-android and the newly released WorkManager for exactly this use-case. Your app syncs data with your back-end in the background at regular intervals and under specific constraints. This approach is best when the data in the back-end changes at somewhat periodic intervals, but may lead to display of stale data when the user actually uses the app if the latest data has not been fetched for a large window.
- Client fetches data each time the app is launched — This approach makes sure your app has the most updated data locally. You can fetch relevant data at particular screens, render the data on the UI and update the local data. Although, if the user opens the same screen every 5 minutes, triggering a refresh of data that has not yet changed, it leads to wastage of the user’s battery as well as your server bandwidth. Some backend systems do inform you the cache window during which you are to consider the cache as the source of truth not ping the servers. OKHttp automagically handles this for you. But not all back-end systems are build with this in mind and we may want to handle the logic at the Android end.
Many a times, one of the above strategies works well for our use-case; But for others, there is need for a more hybrid solution.
Following is my attempt at such a hybrid solution.
Synk is a simple Kotlin file to abstract an intelligent hybrid syncing logic for your Android apps. It makes sure your Android app does not make repeated calls to the same back-end API for the same data in a particular time period.
Each time a sync is successful with the back-end, we save the timestamp. Next time a sync is triggered, we check if the time passed since the last successful sync is greater than our defined sync-window.
- Add the following Synk.kt file to your project:
Note: Synk depends on JodaOrg/joda-time to handle the syncing time-stamps.
2. Initialize Synk in your application file:
Synk.init(context = this)
3. When a remote call is successful, tell Synk about it:
Synk.syncSuccess(key = "posts")
4. Before your app re-fetches the same data, ask Synk if that is necessary:
5. If the sync process fails, tell Synk about it:
Synk.syncFailure(key = "posts")
The saved datetime can be used to display when the data was last synced with the back-end to an offline user.
If you are a user of Rx for your network calls, below is an extension function for updating your remote sync status in Synk.
- Add the following extension function to your project:
2. Update your Rx remote sync calls as follows
You can find a working implementation of Synk in the following commit of my Posts showcase project
Making remote sync intelligent. Fixes #13, #14 · karntrehan/Posts@5ae6966
Posts - A sample Android app using Kotlin, Dagger 2, RxJava, RxAndroid, Retrofit and Android Architecture Components…
Hopefully this will help you sync data intelligently between your apps and back-end systems.
With heavy inputs from Cyril Pillai