6

I am using a foreground service with ongoing notification which updates very frequently.

When the service launches I initialize the notification manager with the notification builder and keep all the references.

The notification has custom layout with buttons. Each time the user presses a button I update the remote views and call the notificationManager.notify(service_id, notificationBuilderCompat.build()).

Eventually after many clicks I receive TransactionTooLargeException error with the size of the data parcel in bytes. Each notify() adds more bytes to this number.

What is the best approach to deal with this?

Demo of the code used:

class MyService: LifecycleService() {

var notificationBuilderCompat: NotificationCompat.Builder
var notificationManager: NotificationManager
var viewModel: ViewModel
val FOREGROUND_SERVICE = 10
val EXTRA_NOTIFICATION_TYPE = "EXTRA_NOTIFICATION_TYPE"
val NOTIFICATION_CHANNEL_ID = "channel_01"

override fun onCreate() {
    super.onCreate()

    // View model that holds the data
    viewModel = ViewModel()

    registerReceiver(broadcastReceiver, IntentFilter(NOTIFICATION_MESSAGE_INTENT)
    notificationManager = getSytemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    // In case of android O
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, "notification_name", NotificationManager.IMPORTNACE_MAX)
        notificationManager.createNotificationChannel(channel)
    }

    notificationBuilderCompat = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID)
}

ovveride fun onStartCommand(intent: Intent?, flags: Int, startId:Int): Int {

    // Received start notification command
    if (intent.action == START_FOREGROUND_ACTION) {
        setupNotification()
        viewModel.observerData.observe(this, Observer { data ->
            remoteViews.setTextViewText(R.id.notification_text, data)

            // This handles the update with the new data, and also where the exception happens
            notificationManager.notify(FOREGROUND_SERVICE, notificationBuilderCompat.build())
        })
    } else {
        stopForground(true)
        unregisterReceiver(broadcastReceiver)
        stopSelf()
    }
}

fun setupNotification() {
    remoteViews = RemoteViews(packageName, R.layout.item_notification)

    // Setting all the remote views here
    remoteViews.setImageViewResource(R.id.notification_app_icon, R.mipmap.ic_luancher)

    // Setting the click event
    val clickIntent = Intent(NOTIFICATION_MESSAGE_INTENT)
    clickIntent.putExtra(EXTRA_NOTIFICATION_TYPE, 1)
    val clickPendingIntent = PendingIntent.getBroadcast(this, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    remoteVies.setOnClickPendingIntent(R.id.notification_button, clickPendingIntent)

    notificationBuilderCompat.setContent(remoteViews)
        .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
        .setTicker("Notificaion on")
        .setCustomBigContentView(remoteViews)
        .setSmallIcon(R.drawable.icon)
        .setPriority(NotificationCompat.PRIORITY_MAX)
        .setOngoing(true)
        .setOnlyAlertOnce(true)
        .setDefaults(0)

    startForeground(FORGROUND_SERVICE, notificationBuilderCompat.build()
}

// This is the broadcast receiver class to handle the click on the notification button
val broadcastReceiver = object: BroadcastReceiver() {
    ovveride fun onReceive(context: Context?, intent: Intent?) {
        // Getting the notification type
        val notificationType = intent!!.getIntExtra(EXTRA_NOTIFICATION_TYPE, 0)

        if (notificationType == 1) {
            // Updating the data here which invokes the observer
            viewModel.updateData()
        }
    }
}

}

Ilya Sosis
  • 352
  • 1
  • 4
  • 9
  • Please provide a [mcve] showing how you are creating and raising the `Notification`, including how you are updating the `RemoteViews` on each button click. – CommonsWare Jul 13 '17 at 17:54
  • @CommonsWare I added stripped code that I use – Ilya Sosis Jul 13 '17 at 18:48
  • 'Each notify() adds more bytes to this number" -- I assume that the difference between two updates is more than just the length of the text that you are putting in `setTextViewText()`. Try temporarily removing the `ImageView` (and the `setImageViewResource()` call) from the `RemoteViews` and see what happens. Does it fail around the same time as before, or does it take a lot longer to fail when there is on `ImageView`? – CommonsWare Jul 13 '17 at 18:55
  • 2
    @CommonsWare Removing the `setImageViewResource()` allowed few more click before the exception. But the issue is still there and I can't really remove the image as I need it for the notification layout. I can't really understand why the notification accumulates bytes with each `notify()` instead of just clearing the old data. This is issue happens only for android 7.0+ but that is most of my users – Ilya Sosis Jul 13 '17 at 19:14
  • Something about what you are doing must be stacking up notification copies that are part of the transaction. I have no idea why. The image-removal test confirms that (if removing the image didn't change things, then we'd be headed down another path). You might try some other experiments to get a handle on the boundaries of the problem (e.g., switch to Java from Kotlin, switch to `Notification.Builder` from `NotificationCompat.Builder`). – CommonsWare Jul 13 '17 at 19:27
  • @IlyaSosis Did you find a way to handle this ? – Jaswanth Manigundan Aug 16 '17 at 07:34
  • 2
    @JaswanthManigundan I solved it for 99% for my users by 2 steps: 1. Made sure when I call notify I change only what needed. I removed all the unnecessary `remoteViews.setTextViewText(R.id.notification_text, data)` that I had. 2. I called the `setupNotification()` function each time it made scene. looks like it reseted transaction size and allowed me to call more `notify()` afterwards – Ilya Sosis Aug 16 '17 at 11:37

0 Answers0