Skip to content

SDK-324 - Updated way of handling notifications to not use AsyncTask#981

Open
franco-zalamena-iterable wants to merge 6 commits intomasterfrom
SDK-324-push-async-fix
Open

SDK-324 - Updated way of handling notifications to not use AsyncTask#981
franco-zalamena-iterable wants to merge 6 commits intomasterfrom
SDK-324-push-async-fix

Conversation

@franco-zalamena-iterable
Copy link
Contributor

@franco-zalamena-iterable franco-zalamena-iterable commented Jan 26, 2026

🔹 Jira Ticket(s) if any

✏️ Description

Migrates push notification handling from the deprecated AsyncTask to Android's WorkManager with expedited work support.


Why

AsyncTask was deprecated in API 30. Firebase and Android documentation explicitly recommend WorkManager for processing FCM messages:

"you should avoid long-running tasks (such as fetching images from a server to display in a notification) in onMessageReceived and instead schedule a task using WorkManager to handle any tasks that might take more than a couple of seconds to complete." - Firebase FCM docs

"If processing of the message payload takes longer than a few seconds, you should make sure that the processing continues in a valid process lifecycle. If you don't make sure this occurs, background execution limits may cause processing to be paused or terminated before the task completes, which can result in delayed or missing user notifications." - Firebase FCM docs

"Start an expedited job using the Android WorkManager to ensure that your high priority notification gets prioritized processing time to ensure your notification rendering runs to completion." - Firebase Blog

"In most cases, your best option for running background tasks is to use WorkManager." - Android Background Tasks


How

  • IterableFirebaseMessagingService - replaced AsyncTask execution with enqueueNotificationWork(), which schedules an expedited OneTimeWorkRequest via WorkManager. Includes a fallback that posts directly if scheduling fails.
  • IterableNotificationWorkScheduler (new) - schedules expedited work with OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST, ensuring notifications are always processed even when quota is exhausted.
  • IterableNotificationWorker (new) - Worker subclass that deserializes notification data, builds the notification, and posts it. Overrides getForegroundInfo() for pre-Android 12 compatibility.
  • IterableNotificationHelper - added null-safety checks on extras parameter.

Since minSdkVersion is 21, devices running Android 5–11 require getForegroundInfo() to avoid IllegalStateException when running expedited work:

"Failing to implement the corresponding getForegroundInfo method can lead to runtime crashes when calling setExpedited on older platform versions." - Android WorkManager docs


Dependencies

  • androidx.work:work-runtime:2.9.0
  • androidx.work:work-testing:2.9.0 (test only)

Copy link
Contributor

@davidtruong davidtruong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove unnecessary logic for ghost silent notifications from the NotificationWorker.


Bundle extras = IterableNotificationHelper.mapToBundle(messageData);
return IterableNotificationHelper.isGhostPush(extras);
private static void enqueueNotificationWork(@NonNull final Context context, @NonNull final Bundle extras, boolean isGhostPush) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can remove the isGhostPush boolean here since notifications only get queued for non ghost push notifications.

* to comply with Firebase best practices. This class is kept for backwards compatibility only.
*/
@Deprecated
class IterableNotificationManager extends AsyncTask<IterableNotificationBuilder, Void, Void> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this deprecated class? Is it even visible to be used by customers?


if (isGhostPush) {
IterableLogger.d(TAG, "Ghost push detected, skipping notification display")
return Result.success()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unnecessary to create the IterableNotificationWorker only to skip the notification display. Please remove the flow of ghost/silent notifications from your IterableNotificationWorker class. If you want to log anything related to silent notifications, you can do so inside of handleMessageReceived.

The ghost push notification logic is filtered out already in IterableFirebaseMessagingService.handleMessageReceived to process silent notifications with different notificationTypes.

context.getApplicationContext(), extras);
new IterableNotificationManager().execute(notificationBuilder);

enqueueNotificationWork(context, extras);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow the exact pattern that firebase wants? Didn't it only want to schedule the notificationWork only if there was something necessary to run in the background such as image fetching. Otherwise display the notification immediately?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great catch!

I tried replacing the async task that we had before with the new WorkScheduler 1:1, we were previously not respecting the documentation and that passed by me unnoticed.

I will update the code to follow firebase best practices

context.getApplicationContext(), extras);
new IterableNotificationManager().execute(notificationBuilder);

if (IterableNotificationHelper.hasAttachmentUrl(extras)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified that only the image attachment needs to schedule the NotificationWork.
Sounds are set as part of the notification category so it doesn't need to be scheduled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah from what i checked we don't need to do the sound async, only images

private static void enqueueNotificationWork(@NonNull final Context context, @NonNull final Bundle extras) {
IterableNotificationWorkScheduler scheduler = new IterableNotificationWorkScheduler(context);

scheduler.scheduleNotificationWork(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the scheduler actually create the notification on device?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it know the format of how to render the notification since it doesn't call the IterableNotificationHelper.createNotification function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you check IterableNotificationWorker.doWork() you can see we are building the notification there, the unused createNotification apparently is there for backwards compatibility since 2019, that actually should be deprecated in my opinion, we can use this PR to do that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments