WorkManager 在工作完成后保持通知

WorkManager keep notification after work is done

我想在 Worker 运行 处于后台时显示通知。我可以用类似下面的代码来做到这一点:

override suspend fun doWork(): Result {
    val manager = NotificationManagerCompat.from(applicationContext)
    val channel = "some_channel"
    val id = 15
    val builder = NotificationCompat.Builder(applicationContext, channel)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        manager.createNotificationChannel(NotificationChannel(channel, "DefaultName", NotificationManager.IMPORTANCE_LOW))

    builder
        .setOnlyAlertOnce(true)
        .setOngoing(true)
        .setAutoCancel(false)
        .setPriority(NotificationCompat.PRIORITY_HIGH)
        .setSmallIcon(android.R.drawable.ic_dialog_alert)
        .setTicker("Background Task")
        .setContentText("Starting")
        .setProgress(0, 0, true)

    setForeground(ForegroundInfo(id, builder.build()))

    delay(500)

    manager.notify(id, builder.setContentText("Progress A").setProgress(20, 0, false).build())
    for (i in 1..20) {
        delay(100)
        manager.notify(id, builder.setProgress(20, i, false).build())
    }

    delay(500)

    manager.notify(id, builder.setContentText("Progress B").setProgress(2, 0, false).build())
    delay(1000)
    manager.notify(id, builder.setProgress(2, 1, false).build())
    delay(1000)
    manager.notify(id, builder.setProgress(2, 2, false).build())

    delay(1000)

    manager.notify(id, builder.setContentText("Done!").setProgress(0, 0, false).build())

    delay(500)

    return Result.success()
}

但我也想在通知中显示工作人员的结果并保留直到用户看到并清除它,但通知总是在工作结束时清除。

如何让通知保持活动状态? .setOngoing(true).setAutoCancel(false) 没有帮助。

我发现的一种解决方法是使用 BroadcastReceiver 接收最终通知并显示它。这是可能的,因为 Notification 扩展了 Parcelable。注意:让 NotificationReceiver 使用与您的 Worker 使用的不同的通知 ID,否则 Worker 将关闭通知。

class NotificationReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == ACTION_SEND_NOTIFICATION) {
            val notification = intent.getParcelableExtra<Notification>(EXTRA_NOTIFICATION)
            val id = intent.getIntExtra(EXTRA_ID, DEFAULT_ID)
            if (notification == null)
                Log.e(TAG, "EXTRA_NOTIFICATION ($EXTRA_NOTIFICATION) was not provided!")
            else
                NotificationManagerCompat.from(context).notify(id, notification)
        }
    }

    companion object {
        const val TAG = "NotificationReceiver"

        const val ACTION_SEND_NOTIFICATION = "intent.action.SEND_NOTIFICATION"

        const val EXTRA_NOTIFICATION = "notification_parcel"
        const val EXTRA_ID = "notification_id"
        const val DEFAULT_ID = 1010
    }

}
<receiver android:name=".NotificationReceiver" android:enabled="true" />
    // At the end of doWork, before returning
    applicationContext.sendBroadcast(
        Intent(applicationContext, NotificationReceiver::class.java).setAction(
            NotificationReceiver.ACTION_SEND_NOTIFICATION
        ).putExtra(NotificationReceiver.EXTRA_NOTIFICATION, builder.build())
    )

我认为我发现的一种更简单的方法是使用不同的 id (use .setOngoing(false)):

通知最终通知
manager.notify(
    id + 1,
    builder.setContentText("Done!")
        .setAutoCancel(true)
        .setOngoing(false)
        .build()
)
return Result.success()

或者,稍微延迟后发送:

CoroutineScope(Main).launch {
    delay(200)
    manager.notify(
        id,
        builder.setContentText("Done!")
            .setAutoCancel(true)
            .setOngoing(false)
            .build()
    )
}
return Result.success()