Exploring the benefits of offline based push notifications

Everwell Engineering
Pulse by Everwell
Published in
5 min readJul 21, 2023

In today’s fast-paced world, mobile apps play a crucial role in delivering timely information and engaging users. Push notifications are a powerful tool that enable developers to provide real-time updates and seamless app experience.

While the benefits of push notifications cannot be undermined, a common limitation is the ability of the device to stay connected to the internet. The situation can arise in scenarios of low connectivity like militarised areas or even areas like the airplane where connectivity is limited.

In this blog post, using the task manager and and background tasks in Android, we will try to present and counter the challenges that online push notifications can present. We will also discuss the advantages of using Worker for background task execution. Let’s dive in!

Relying solely on online push notifications can present challenges, such as difficulties with in-app translations, supporting multiple time zones, and delivering notifications when devices are not connected to the internet. We tried to dig deeper into each of these problems to validate if offline based push notifications can actually help in solving them.

  • Overcoming In-App Translation Issues: When notifications are received online, handling dynamic translations becomes complex. However, with offline push notifications, you can seamlessly integrate translations into the app, providing a consistent and localised user experience
  • Supporting Multiple Time Zones: Online push notifications rely on server-side processing, making it challenging to handle notifications based on different time zones. This limitation often leads to difficulties in delivering time-sensitive notifications to users across various regions. Offline push notifications can help address this issue by leveraging device-side processing, allowing you to deliver notifications based on the user’s local time zone.
  • Uninterrupted Notifications Regardless of Internet Connectivity: If a user’s device is offline, the notifications cannot be delivered, resulting in missed opportunities to engage users. Offline push notifications overcome this limitation by storing notifications locally on the device. This ensures that even if the device is offline, notifications are queued and delivered as soon as the device regains connectivity.

Simplifying Medication Management with Custom Multi-Medicine Reminders At Everwell

For us in Everwell, we care deeply about the medication adherence reminders which allow users to take their dosage in a timely manner. Our system had several limitations like the ones listed above which we wanted to solve.

We improved our medication management with custom multi-medicine reminders by switching to offline notification, enabling users to create personalised reminders for different medications and times. This scalable solution allowed each device to handle notifications independently, ensuring accurate and timely reminders without relying on backend jobs. In-app translations for notifications were also utilised.

Building Offline Scheduled Notifications

To leverage offline scheduling we had multiple responsibilities which we had to solve independently before stitching these components together for the entire workflow.

1. Data Management: To gather data we used classes like Worker and WorkManager component from the Android jetpack library. It enables you to asynchronously perform background tasks in a flexible and reliable manner, independent of the application’s lifecycle, and optimised for efficient resource usage.

The worker offloads resource-intensive operations from the main thread, ensuring smooth interaction and responsiveness. With its lifecycle-awareness and flexible scheduling capabilities, it provides a robust solution for handling background tasks efficiently and reliably.

Here is a sample extension the Worker class and overrides the doWork() method where you can define the tasks to be executed to gather the required data

class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

companion object {
fun schedule(context: Context) {
val workManager = WorkManager.getInstance(context)
val request = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
workManager.enqueue(request)
}
}



override fun doWork(): Result {
// Perform your background task here like fetching notification related data (title, description, reminder time)
NotificationReceiver.schedule(this.applicationContext, reminderTime, uniqueCode)
return Result.success()
}
}

2. Periodic Job Scheduler and Notification Receiver: AlarmManager, a system service was our first choice to solve how to schedule periodic jobs as it has the capability to schedule at specific time slots as well as recurring intervals.

By setting up an alarm with the desired interval, you can ensure that the NotificationManager is triggered periodically to show notification. However, on further testing we realised that when the device reboots, all scheduled alarms set using AlarmManager are cleared.

To counter the issue we utilised the BroadcastReceiver. The BroadcastReceiver receives a lifecycle hook whenever the device is restarted and can be used to schedule the alarms again with the AlarmManager, ensuring that the notifications are delivered as intended.

<receiver android:name=".NotificationReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
class NotificationReceiver : BroadcastReceiver() {
companion object {
fun schedule(context: Context, eventTime: Long, code: Int) {
//eventTime - should be in millisecond
//code - unique code will add new job and duplicate code will replace existing job
val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val alarmIntent = Intent(context.applicationContext, NotificationReceiver::class.java)
//pass intent if extra data is needed
val pendingIntent = PendingIntent.getBroadcast(context, code, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)
manager.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + eventTime, DAY_INTERVAL, pendingIntent)
}
}

override fun onReceive(context: Context?, intent: Intent?) {
if ("android.intent.action.BOOT_COMPLETED" == intent!!.action) {
context?.let { MyWorker.schedule(it) }
} else {
//Trigger notification using NotificationManager
}
}
}

3. Displaying notifications using Notification Manager: Once the data is gathered by the worker, the next workflow is to display these notifications via the Notification Manager with the desired content, including title, text, icon, and actions.

object NotificationManagerHelper {
private const val CHANNEL_ID = "YourChannelId"
private const val CHANNEL_NAME = "YourChannelName"
private const val CHANNEL_DESCRIPTION = "YourChannelDescription"

fun createNotificationChannel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = CHANNEL_DESCRIPTION
}

val notificationManager =
context.getSystemService(NotificationManager::class.java)
notificationManager?.createNotificationChannel(channel)
}
}

fun showNotification(context: Context, title: String, message: String) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(translate(title))
.setContentText(translate(message))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)

val notificationManager = NotificationManagerCompat.from(context)
notificationManager.notify(notificationId, builder.build())
}
}

We used the NotificationManager to inject translations to the title and message allowing for localised content to be provided for better engaging users.
To invoke this functionality just need to call MyWorker.schedule(context)

Conclusion

Offline push notifications and background tasks using Worker significantly enhanced the user experience in our Android app. We were able to realise reliable message delivery, increased user engagement, and real-time updates even in challenging network conditions. The Worker also simplified the execution of background tasks, optimised battery usage and allowed for flexible scheduling which was an added cherry on top.

Credits: Saurav Kumar

--

--