Jetpack Compose中使用Notification
Posted 川峰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack Compose中使用Notification相关的知识,希望对你有一定的参考价值。
发送通知相关的主要有两个关键的类 NotificationCompat.Builder
和 NotificationManagerCompat
为方便使用,首先定义一个扩展工具类来管理通知
const val MAIN_CHANNEL_ID = "MainChannel ID"
const val MAIN_CHANNEL = "MainChannel"
fun Context.buildNotification(
id: Int,
title: String,
message: String,
action: String? = null,
actionMessage: String? = null,
visibility: Int = VISIBILITY_PUBLIC,
activityIntent: Intent? = null,
isDeepLink: Boolean = false
): Notification
val notification = Notification(id, title, message, action, actionMessage,
visibility, activityIntent,isDeepLink)
notification.builder = notification.builder(this)
notification.manager = getNotificationManager()
return notification
data class Notification(
val id: Int,
var title: String,
var message: String,
var action: String? = null,
var actionMessage: String? = null,
var visibility: Int = VISIBILITY_PUBLIC,
var activityIntent: Intent? = null,
val isDeepLink: Boolean = false,
var builder: Builder? = null,
var manager: NotificationManagerCompat? = null
)
fun Notification.show(): Notification
builder?.let manager?.notify(id, it.build())
return this
fun Notification.update(
context: Context,
titleNew: String? = null,
messageNew: String? = null,
action1: String? = null,
visibleType: Int = VISIBILITY_PUBLIC
): Notification
titleNew?.let title = titleNew
messageNew?.let message = messageNew
action1?.let action = action1
if (visibleType != visibility) visibility = visibleType
manager?.notify(id, builder(context).build())
return this
fun Notification.builder(context: Context): Builder
val builder = Builder(context, MAIN_CHANNEL_ID)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(context.bitmap(R.drawable.ic_head3,200, 200))
.setPriority(PRIORITY_DEFAULT)
.setVisibility(visibility)
.setAutoCancel(true)
if (visibility == VISIBILITY_PRIVATE)
builder.setPublicVersion(
Builder(context, MAIN_CHANNEL_ID)
.setContentTitle("收到一条新的消息")
.setContentText("请解锁屏幕后查看!")
.build()
)
val flg = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
action?.let
val intent = Intent(context, NotificationReceiver::class.java).apply
putExtra(KEY_MESSAGE, actionMessage)
putExtra(KEY_NOTIFICATION_ID, id)
PendingIntent.getBroadcast(context, 0, intent, flg)
?.let builder.addAction(0, action, it)
if (isDeepLink)
activityIntent?.let
TaskStackBuilder.create(context).run
addNextIntentWithParentStack(it)
getPendingIntent(1, flg)
?.let builder.setContentIntent(it)
else
activityIntent?.let PendingIntent.getActivity(context, 1, it, flg)
?.let builder.setContentIntent(it)
return builder
fun Context.getNotificationManager(): NotificationManagerCompat
val notificationManager = NotificationManagerCompat.from(applicationContext)
// API 26 android 8.0开始必须为每个通知指定一个channel才会显示
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
val channel = NotificationChannel(MAIN_CHANNEL_ID, MAIN_CHANNEL,
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
return notificationManager
fun Context.cancelNotification(id: Int) = getNotificationManager().cancel(id)
然后定义一个ViewModel,在其中负责具体的发送通知业务
class NotificationTestViewModel: ViewModel()
var notification: Notification? = null
private fun buildNotification(context: Context, title: String, message: String)
val clickIntent = Intent(context, NotificationTestActivity::class.java)
notification = context.buildNotification(
id = 1,
title = title,
message = message,
action = "Action按钮",
actionMessage = "点击了按钮",
visibility = VISIBILITY_PUBLIC,
activityIntent = clickIntent,
)
fun showNotification(context: Context, title: String, message: String)
buildNotification(context, title, message)
notification?.show()
fun updateNotification(context: Context, titleNew: String, messageNew: String)
notification?.update(context, titleNew, messageNew)
最后在Composable中调用viewmodel的方法发送通知:
@Composable
fun NotificationTest(viewModel: NotificationTestViewModel = viewModel())
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
)
Button(onClick =
viewModel.showNotification(context,"外卖提醒", "您好,您的外卖到了!")
)
Text(text = "创建一个新通知")
Button(onClick =
viewModel.updateNotification(context,"订单提醒", "您有一条新的外卖订单,请及时接单!")
)
Text(text = "更新通知")
更新通知
发送通知后通过NotificationManagerCompat.notify(id, notification)
对相同的通知id
进行再次调用,就会更新通知中对应的属性
通知的可见性
通知有三种可见性规则,分别是:
NotificationCompat.VISIBILITY_PUBLIC
: 默认所有屏幕可见NotificationCompat.VISIBILITY_SECRET
: 锁屏下不可见NotificationCompat.VISIBILITY_PRIVATE
:锁屏可见,但是隐藏敏感或私人信息
VISIBILITY_PUBLIC
的效果:
VISIBILITY_SECRET
的效果:
VISIBILITY_PRIVATE
的效果:
要测试 VISIBILITY_PRIVATE
的效果需要先将系统设置中通知管理里的锁屏显示敏感信息的选项关闭:
然后在创建 NotificationCompat.Builder
时,需要通过 setPublicVersion
设置在锁屏界面时展示的信息:
if (visibility == VISIBILITY_PRIVATE)
builder.setPublicVersion(
Builder(context, MAIN_CHANNEL_ID)
.setContentTitle("收到一条新的消息")
.setContentText("请解锁屏幕后查看!")
.build()
)
通知中的Action
通知中最多可以添加三个Action按钮,点击时,可以执行对应的pendingIntent
,比如在前面的代码中,构建 NotificationCompat.Builder
时判断如果 Action 按钮的文字不为空,就为builder设置一个action选项:
val flg = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
action?.let
val intent = Intent(context, NotificationReceiver::class.java).apply
putExtra(KEY_MESSAGE, actionMessage)
putExtra(KEY_NOTIFICATION_ID, id)
PendingIntent.getBroadcast(context, 0, intent, flg)
?.let builder.addAction(0, action, it)
这里使用PendingIntent.getBroadcast
构建了一个用于触发广播的PendingIntent
,builder.addAction的第一个参数还可以设置一个图标的资源id。
当用户点击通知中的action按钮时,就会发送广播,然后在NotificationReceiver
中从intent查询信息进行显示即可:
const val KEY_MESSAGE = "Notification_Message"
const val KEY_NOTIFICATION_ID = "Notification_Id"
class NotificationReceiver: BroadcastReceiver()
override fun onReceive(context: Context?, intent: Intent?)
intent?.run
val msg = getStringExtra(KEY_MESSAGE)
msg?.let context?.showToast(msg)
val id = getIntExtra(KEY_NOTIFICATION_ID, 0)
context?.cancelNotification(id) // 根据需求决定要不要取消
这里收到通知后在广播接收器中弹出一个toast提示同时取消了通知,实际业务中可以根据需求决定要不要关闭通知(可能是需要后台常驻的就不要取消)
点击通知跳转具体页面
很简单,就是在构建 NotificationCompat.Builder
时,设置一个Activity
类型的PendingIntent
即可
val activityIntent= Intent(context, NotificationTestActivity::class.java)
...
activityIntent?.let PendingIntent.getActivity(context, 1, it, flg)
?.let builder.setContentIntent(it)
点击通知后就会跳转的具体的Activity页面
触发DeepLink页面
可以配合Compose导航路由的DeepLink,在点击通知时,跳转到某个导航图中的某个子路由页面中
首先需要配置Compose导航路由的DeepLink,这里使用开源库 compose-destinations 进行配置路由:
// NotificationTest.kt
@Composable
fun NotificationNavHostScreen()
Box(modifier = Modifier.fillMaxSize())
DestinationsNavHost(navGraph = NavGraphs.root)
@RootNavGraph(start = true)
@Destination
@Composable
fun NotificationTest(navigator: DestinationsNavigator,
viewModel: NotificationTestViewModel = viewModel())
...
// DetailScreen.kt
const val APP_URI ="http://my.app.com/detail/"
@Destination(deepLinks = [DeepLink(uriPattern = "$APP_URI/message")])
@Composable
fun DetailScreen(message: String)
...
在Activity中使用DestinationsNavHost
作为根布局显示
class NotificationMainActivity: ComponentActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContent
MyComposeApplicationTheme
Surface(Modifier.fillMaxSize(),
color=MaterialTheme.colorScheme.background)
NotificationNavHostScreen()
然后在构建 NotificationCompat.Builder
时,通过TaskStackBuilder
来构建pendingIntent
if (isDeepLink)
activityIntent?.let
TaskStackBuilder.create(context).run
addNextIntentWithParentStack(it)
getPendingIntent(1, flg)
?.let builder.setContentIntent(it)
在NotificationTestViewModel
中添加一个专门用于构建Deeplink的方法:
private fun buildDeepLinkNotification(context: Context, title: String, message: String)
val clickIntentDeepLink = Intent(
Intent.ACTION_VIEW,
"$APP_URI/message from NotificationTest".toUri(),
context, NotificationMainActivity::class.java
)
notification = context.buildNotification(
id = 1,
title = title,
message = message,
action = "Action按钮",
actionMessage = "点击了按钮",
visibility = VISIBILITY_PUBLIC,
activityIntent = clickIntentDeepLink,
isDeepLink = true
)
fun showNotification(context: Context, title: String, message: String)
buildDeepLinkNotification(context, title, message)
notification?.show()
这样就可以了,Manifest中无需为Activity标签配置任何额外的属性
效果如下
在通知中显示进度条
首先在构建NotificationManagerCompat
时,需要再添加一个channel2
, 并且将其importance
参数设置为NotificationManager.IMPORTANCE_LOW
(这是因为我们不想在每次更新进度条时都会发出系统提示音)
fun Context.getNotificationManager(): NotificationManagerCompat
val notificationManager = NotificationManagerCompat.from(applicationContext)
// API 26 Android 8.0开始必须为每个通知指定一个channel才会显示
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
val channel = NotificationChannel(MAIN_CHANNEL_ID, MAIN_CHANNEL,
NotificationManager.IMPORTANCE_DEFAULT
)
val channel2 = NotificationChannel(SECOND_CHANNEL_ID, SECOND_CHANNEL,
NotificationManager.IMPORTANCE_LOW
)
notificationManager.createNotificationChannel(channel)
notificationManager.createNotificationChannel(channel2)
return notificationManager
然后也需要单独为这个channel创建Builder:
fun Notification.secondChannelBuilder(context: Context): Builder
return Builder(context, SECOND_CHANNEL_ID) // 这里要使用SECOND_CHANNEL_ID
.setContentTitle("下载中")
.setContentText("$progress/$max")
.setSmallIcon(R.mipmap.ic_launcher)
.setProgress(max, progress, false)
.setPriority(PRIORITY_LOW) // 设置低优先级
.setOngoing(true) // 不允许用户取消
fun Notification.updateProgress(
context: Context,
progress: Int,
max: Int,
): Notification
this.progress = progress
this.max = max
manager?.notify(id, secondChannelBuilder(context).build())
return this
fun Notification.showFinished(context: Context): Notification
this.title = "下载完成!"
this.message = ""
// 下载完后使用MAIN_CHANNEL的builder构建更新通知
manager?.notify(id, builder(context).apply
setContentIntent(null)
clearActions()
setProgress(0, 0, false)
.build())
return this
在NotificationTestViewModel中添加一个showProgress方法:
fun showProgressJetpack Compose 中使用 Lottie 动画
Android Jetpack Compose学习—— Jetpack compose基础布局
Android Jetpack Compose学习—— Jetpack compose基础布局