Accurately timing your push notification

Anirban Mukherjee
6 min readJun 28, 2022

Push notifications on mobile phones is one of the most powerful tools available for retaining and winning back user engagement, increasing marketing drives or any other effort where you want to capture your user’s attention.

What is a Push Notification?

Mobile phones and tablets today have tons of apps on them, and all these apps are trying to use similar techniques to capture your user’s attention. A Push Notification typically shows up as a small view of text that suddenly appears out of nowhere on the top section of your phone, and might be accompanied by a sound too. Now, imagine yourself in your user’s shoes and you will quickly understand why Push Notifications can easily become a pain for a common mobile phone user. And once it becomes a pain, the road (of your app’s user experience) can quickly fall steep downhill.

The secret sauce here however is to know exactly when that little piece of information would be of use to your user/customer and bring it to their attention at the right time.

Imagine your customer walking to the groceries store, and just about when he enters the gate, your app pops up a Push Notification telling about the items on sale in the shop. The person would be quick to make a decision on which items they would prefer to buy then, they would find your app useful in helping them make quick decisions and the whole visit to the store becomes a lot shorter. That is exactly how powerful Push Notifications can be for app developers to gain and retain user engagement.

The Background

Recently, while developing the (Android only) Somnos — Baby Sleep sounds app we realised that it would be a tough job to retain users especially since new parents have a crazy busy schedule. Their phones and tablets will likely be filled with apps (all trying to throw out notifications); if their little one is not playing with the phone, that is :-)

With this mind, we set the following requirements on our Push Notification strategy for the app:

  1. Must be delivered at the precise time when the parent is about to take their baby to sleep.
  2. The notification must be shown to the user irrespective of whether the phone is connected to the internet or not.
  3. Clicking on the notification should take the user to different target sections within the app.
  4. The cost of the backend systems to generate and send out the push notifications, must be minimal (less than $1/month) even for a scale of 1M target users.

Architecture design

We designed this architecture using Firebase Cloud Messaging as the primary service for delivering the Push Notifications to the phones and tablets.

For simplicity, we were already using (Firebase) FireStore databases for storing DeviceTokens.

The logic flow in the backend was designed so:

  1. Google Cloud Functions (GCF) will be used as a server-less option to create and send out Push Notifications using FCM service. This will be used for send notifications to all devices running out app, irrespective of the timezone.
  2. A Google Cloud Function will trigger at 00:30hrs CET time every day, with the intention to create Push Notifications for the day after. This means, a Cloud Function running on 26.06.2022 at 00:30hrs CET will be generating and delivering Push Notifications targeted to be delivered on 27.06.2022. This ensures that all notifications are generated and pushed out before 00:00hrs on 27.06.2022 at any place in the world.
  3. The Cloud Function will create a FCM Data Message (Not a FCM Notification message) containing (in a string : string Hashmap) custom parameters that will constitute the Push Notification.
  4. The custom parameters in the FCM Data message contains various fields like notification title, body text of the notification, etc along with the(very important) time (in milliseconds in phone’s local timezone) at which time the Push Notification should be shown to the user.
  5. Once the phone receives this FCM Data message, it will extract out the parameters inside it, along with the “trigger time” (time at which the notification should be displayed).
  6. Your app on the phone will then setup a (silent) Alarm to go off on your phone at the precise “trigger time” mentioned in the FCM Data message.
  7. When the alarm goes off, there is no sound created nor even the phone’s screen need not be unlocked. However, at this time, your app is triggered to act on at the “alarm time”.
  8. At this time, your app creates a Push Notification from the custom parameters in the FCM Data message, and shows it up on the phone’s screen.
message flow

End result

With this architecture, we were able to deliver Push Notifications on the user’s device at a precisely calculated time. This resulted in immediate turn around (in large numbers) of Daily Active Users; people who had the app installed on their app but were not using the app frequently, now suddenly started to be notified about particular cool features of the app at precisely the time when they could use the app.

Benefits of this approach (Android)

  • The actual FCM Data message can be sent at any time in the day as long as it is before the day on which the notification must be shown (to ensure we show the notification on any phone across any timezone).
  • The actual FCM Data message being already sent ahead of time, means this procedure will work even if the phone does not have internet connection at the time when the notification is intended to show up.
  • Since a single run of the Cloud function is needed in the day to send out notifications to all devices across the globe, the compute and runtime cost of the GCF goes down tremendously.
  • The network transfer costs remains low as each device is notified only 1 time per notification.
  • We can further optimise costs and performance by using “sending notifications in bulks”.
  • AlarmManager and BroadcastReceivers are native components of the Android OS, and therefore this service does not need any other libraries (apart from Google).
  • AlarmManager provides the possibility to setup an alarm at a precise time without requiring the user to grant permission, making your app experience more free from sudden alerts.

Sample code sections

The following section contains code extracts of different software modules through which the Push Notification workflow flows.

Sending a Push Data message using the FCM client object:

if _, err := fcmClient.Send(context.Background(), &msg); err != nil {log.Printf(“error sending data PSN to DeviceToken=%s, reason=%s”, msg.Token, err.Error())} else {log.Printf(“Sent FCM push data message to devicetoken=%s”, msg.Token)}

Accepting the FCM Data message from FCM sdk into your app, and setting up the alarm.

public class FCMReceiverService extends FirebaseMessagingService {  @Override
public void onMessageReceived(@NonNull RemoteMessage message) {
String triggerTimeString = messageData.get(DEF_PARAMS_KEY_NOTIF_TRIGGERAT);
String notificationTitle = messageData.get(DEF_PARAMS_KEY_NOTIF_TITLE);
String notificationBody = messageData.get(DEF_PARAMS_KEY_NOTIF_BODY);
Date triggerTime = DateTimeUtils.getDateTimeFromString(triggerTimeString);
if (triggerTime == null) return;

long triggerTimeMillis = triggerTime.getTime();
Intent intent = new Intent(BuildConfig.APPLICATION_ID + ".ALARM_START");
intent.putExtra(DEF_PARAMS_KEY_NOTIF_TITLE, notificationTitle);
intent.putExtra(DEF_PARAMS_KEY_NOTIF_BODY, notificationBody);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);

PendingIntent pendingIntent = PendingIntent.getBroadcast(context,REQUEST_CODE_ALARM_PENDING_INTENT, intent,PendingIntent.FLAG_UPDATE_CURRENT|PendingIntent.FLAG_IMMUTABLE);

//
// cancel any existing alarms before setting up this new one
//
alarmManager.cancel(pendingIntent);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTimeMillis, pendingIntent);
IntentFilter intentFilter = new IntentFilter(BuildConfig.APPLICATION_ID + ".ALARM_START");
context.registerReceiver(new NotificationAlarmReceiver(), intentFilter);
}

The trick here is to use AlarmManager.setExact() to ensure that the alarm is triggered off at the precise time. Using base functions like AlarmManager.set() will not give the precision of timing, and rather leave it to the Android OS to approximate the alarm trigger time depending on factors like battery saving, etc.

Secondly, using RTC time means we consider the System uptime since 01.01.1970. Using Elapsed time here is not the right choice, as that would make the precise timing of the alarm variable.

When the Alarm is triggered, read in the Intent extras sent in from the Alarm. Then notify the user with a Push Notification object.

public class NotificationAlarmReceiver extends BroadcastReceiver {  @Override
public void onReceive(Context context, Intent intent) {
String title = intent.getStringExtra(FCMReceiverService.DEF_PARAMS_KEY_NOTIF_TITLE);
String body = intent.getStringExtra(FCMReceiverService.DEF_PARAMS_KEY_NOTIF_BODY);
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOGTAG);
wl.acquire(10 * 1000L);

// notify the user here
PushNotificationUtils.showPushNotification(context, title, body);

wl.release();
}
}

While Push Notifications can be the most powerful tool when you are trying to regain engagement with your customers, they need to be used carefully. The precise timing of when the Push Notification is shown to the user is the secret sauce to ensuring the maximum attention of your user.

We recently released the Somnos — baby sleep sounds app worldwide as a “beta test” through Google Play Console. If you are a new or to-be parent, the app is pre-loaded with a huge library of sounds and interesting functionalities that enables you to get your baby to sleep quickly and sleep longer, while simultaneously creating a special bonding between you and your baby! Do check out the app here.

I regularly write about different topics in tech including career guidance, the latest news, upcoming technologies, and much more. This blog was originally posted in my blogs at anirban-mukherjee.com

--

--

Anirban Mukherjee

Loves writing code, building projects, writing about tech stuff, running side hustles; Engineering leader by day, nerd builder by night.