创建通知

通知可在应用程序未使用时提供有关事件的简短及时的信息。
此页面教您如何使用Android 4.0(API级别14)及更高版本的各种功能创建通知。

请注意,此页面上的代码使用Android支持库中的NotificationCompat API。 这些API允许您添加仅在较新版本的Android上可用的功能,同时仍然提供与Android 4.0(API级别14)的兼容性。 但是,一些新功能(如内联回复操作)会导致旧版本的无操作。

一、添加支持库

  dependencies {  implementation "com.android.support:support-compat:27.1.1"  }

注意:com.android.support组中的其他库还包括support-compat作为传递依赖项。 因此,如果您已经在使用其他支持库API,则可以访问NotificationCompat,而无需上面显示的确切依赖项。

二、创建基本通知

最基本和紧凑形式的通知(也称为折叠表单)显示图标,标题和少量内容文本。 在本部分中,您将学习如何创建用户可以单击以在应用中启动活动的通知。

图1.带有标题和文本的通知
设置通知内容

首先,您需要使用NotificationCompat.Builder对象设置通知的内容和通道:

  • 一个小图标,由setSmallIcon()设置。 这是唯一需要的用户可见内容。

  • 标题,由setContentTitle()设置。

  • 正文文本,由setContentText()设置。

  • 通知优先级,由setPriority()设置。 优先级决定了Android 7.1及更低版本通知的侵入程度。 (对于Android 8.0及更高版本,您必须设置频道重要性 - 显示在下一部分中。)

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle(textTitle)
        .setContentText(textContent)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT);

请注意,NotificationCompat.Builder构造函数要求您提供通道ID。 这是Android 8.0(API级别26)及更高版本兼容所必需的,但旧版本会忽略这一点。

默认情况下,通知的文本内容将被截断以适合一行。 如果希望通知更长,可以通过添加带有setStyle()的样式模板来启用可扩展通知。 例如,以下代码创建一个更大的文本区域:

 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Much longer text that cannot fit one line...")
        .setStyle(new NotificationCompat.BigTextStyle()
                .bigText("Much longer text that cannot fit one line..."))
        .setPriority(NotificationCompat.PRIORITY_DEFAULT);

有关其他大型通知样式的详细信息,包括如何添加图像和媒体播放控件,请参阅使用可扩展的详细信息创建通知。

创建频道并设置重要性

Android 8.0及更高版本上发送通知之前,必须通过将NotificationChannel实例传递给createNotificationChannel()来向系统注册应用程序的通知通道。 因此,以下代码被SDK_INT版本上的条件阻止:

private void createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is new and not in the support library
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        CharSequence name = getString(R.string.channel_name);
        String description = getString(R.string.channel_description);
        int importance = NotificationManager.IMPORTANCE_DEFAULT;
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
        channel.setDescription(description);
        // Register the channel with the system; you can't change the importance
        // or other notification behaviors after this
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }
}

由于必须在Android 8.0及更高版本上发布任何通知之前创建通知渠道,因此您应该在应用启动后立即执行此代码。 重复调用此方法是安全的,因为创建现有通知通道不会执行任何操作。

请注意,NotificationChannel构造函数需要一个重要性,使用NotificationManager类中的一个常量。 此参数确定如何中断用户以查找属于此通道的任何通知 - 尽管您还必须使用setPriority()设置优先级以支持Android 7.1及更低版本(如上所示)。

虽然您必须如此处所示设置通知重要性/优先级,但系统不保证您将获得警报行为。 在某些情况下,系统可能会根据其他因素更改重要性级别,并且用户可以始终重新定义给定通道的重要性级别。

设置通知的点按操作

每个通知都应响应点按,通常是在您的应用中打开与通知相对应的活动。 为此,您必须指定使用PendingIntent对象定义的内容意图,并将其传递给setContentIntent()

以下代码段显示了当用户点按通知时如何创建打开活动的基本意图:

// Create an explicit intent for an Activity in your app
Intent intent = new Intent(this, AlertDetails.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        // Set the intent that will fire when the user taps the notification
        .setContentIntent(pendingIntent)
        .setAutoCancel(true);

请注意,此代码调用setAutoCancel(),它会在用户点击时自动删除通知。

上面显示的setFlags()方法有助于在用户通过通知打开您的应用后保留用户的预期导航体验。 但是,您是否要使用它取决于您正在启动的活动类型,可能是以下之一:

  • 专门用于响应通知的活动。 在正常的应用程序使用期间,用户没有理由导航到此活动,因此活动会启动新任务,而不是添加到应用程序的现有任务和后台堆栈。 这是上面示例中 创建的意图类型。

  • 应用的常规应用流程中存在的活动。 在这种情况下,启动活动应该创建一个后备堆栈,以便保留用户对Back和Up按钮的期望。

显示通知

要显示通知,请调用NotificationManagerCompat.notify(),向其传递通知的唯一ID和NotificationCompat.Builder.build()的结果。 例如:

NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
// notificationId is a unique int for each notification that you must define
notificationManager.notify(notificationId, mBuilder.build());

请记住保存传递给NotificationManagerCompat.notify()的通知ID,因为如果要更新或删除通知,以后需要它。

注意:从Android 8.1(API级别27)开始,应用程序无法每秒发出超过一次的通知。 如果您的应用在一秒钟内发布了多个通知,则它们都会按预期显示,但每秒只有第一个通知发出声音。

三、添加操作按钮

通知最多可以提供三个操作按钮,允许用户快速响应,例如暂停提醒或甚至回复短信。 但是,这些操作按钮不应复制用户点击通知时执行的操作。

图2.带有一个操作按钮的通知

要添加操作按钮,请将PendingIntent传递给addAction()方法。 这就像设置通知的默认点击操作一样,除了启动活动之外,您还可以执行各种其他操作,例如启动在后台执行作业的BroadcastReceiver,以便操作不会中断已打开的应用程序。

例如,以下代码显示如何将广播发送到特定接收器:

Intent snoozeIntent = new Intent(this, MyBroadcastReceiver.class);
snoozeIntent.setAction(ACTION_SNOOZE);
snoozeIntent.putExtra(EXTRA_NOTIFICATION_ID, 0);
PendingIntent snoozePendingIntent =
        PendingIntent.getBroadcast(this, 0, snoozeIntent, 0);

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .addAction(R.drawable.ic_snooze, getString(R.string.snooze),
                snoozePendingIntent);

有关构建BroadcastReceiver以运行后台工作的更多信息,请参阅“广播”指南。

如果您尝试使用媒体播放按钮(例如暂停和跳过曲目)来构建通知,请参阅如何使用媒体控件创建通知。

四、添加直接回复操作

Android 7.0(API级别24)中引入的直接回复操作允许用户直接在通知中输入文本,该通知会在不打开活动的情况下传送到您的应用。 例如,您可以使用直接回复操作让用户回复文本消息或从通知中更新任务列表。

图3.点击“Reply”按钮打开文本输入

直接回复操作在通知中显示为打开文本输入的附加按钮。 当用户完成输入时,系统会将文本响应附加到您为通知操作指定的意图,并将意图发送到您的应用程序。

添加回复按钮

要创建支持直接回复的通知操作:

1、创建一个可以添加到通知操作的RemoteInput.Builder实例。 此类的构造函数接受系统用作文本输入键的字符串。 稍后,您的掌上电脑应用程序使用该键来检索输入的文本。

// Key for the string that's delivered in the action's intent.
private static final String KEY_TEXT_REPLY = "key_text_reply";

String replyLabel = getResources().getString(R.string.reply_label);
RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
        .setLabel(replyLabel)
        .build();

2、为回复操作创建PendingIntent

// Build a PendingIntent for the reply action to trigger.
PendingIntent replyPendingIntent =
        PendingIntent.getBroadcast(getApplicationContext(),
                conversation.getConversationId(),
                getMessageReplyIntent(conversation.getConversationId()),
                PendingIntent.FLAG_UPDATE_CURRENT);

警告:如果您重新使用PendingIntent,则用户可以回复与他们认为的对话不同的对话。 您必须为每个对话提供不同的请求代码,或者在您对任何其他对话的回复意图调用equals()时提供不返回true的意图。 会话ID经常作为intentextras包的一部分传递,但在调用equals()时会被忽略。

3、使用addRemoteInput()RemoteInput对象附加到操作。

// Create the reply action and add the remote input.
NotificationCompat.Action action =
        new NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
                getString(R.string.label), replyPendingIntent)
                .addRemoteInput(remoteInput)
                .build();

4、将操作应用于通知并发出通知。

// Build the notification and add the action.
Notification newMessageNotification = new Notification.Builder(mContext, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_message)
        .setContentTitle(getString(R.string.title))
        .setContentText(getString(R.string.content))
        .addAction(action)
        .build();

// Issue the notification.
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(notificationId, newMessageNotification);

系统会在用户触发通知操作时提示用户输入响应,如图3所示。

从回复中检索用户输入

要从通知的回复UI接收用户输入,请调用RemoteInput.getResultsFromIntent(),并将其传递给BroadcastReceiver收到的Intent:

private CharSequence getMessageText(Intent intent) {
    Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
    if (remoteInput != null) {
        return remoteInput.getCharSequence(KEY_TEXT_REPLY);
    }
    return null;
 }

处理完文本后,必须通过使用相同的ID和标记(如果使用)调用NotificationManagerCompat.notify()来更新通知。 这是隐藏直接回复UI并向用户确认他们的回复已被正确接收和处理所必需的。

// Build a new notification, which informs the user that the system
// handled their interaction with the previous notification.
Notification repliedNotification = new Notification.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_message)
        .setContentText(getString(R.string.replied))
        .build();

// Issue the new notification.
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(notificationId, repliedNotification);

使用此新通知时,请使用传递给接收方的onReceive()方法的上下文。

您还应该通过调用setRemoteInputHistory()将回复附加到通知的底部。 但是,如果您正在构建消息传递应用程序,则应创建消息传递样式通知并将新消息附加到对话。

有关消息传递应用程序通知的更多建议,请参阅消息传递应用程序的最佳实践。

五、添加进度条

通知可以包括动画进度指示器,向用户显示正在进行的操作的状态。

图4.操作期间和之后的进度条。

如果您可以随时估计操作的完成程度,请通过调用setProgress(max,progress,false)使用指标的“确定”形式(如图4所示)。 第一个参数是“完整”值(例如100); 第二个是当前完成的数量,最后一个表示这是一个确定的进度条。

当您的操作继续时,使用更新的进度值连续调用setProgress(max,progress,false)并重新发出通知。

 ...  
NotificationManagerCompat notificationManager =  NotificationManagerCompat.from(this);
NotificationCompat.Builder mBuilder =  new  NotificationCompat.Builder(this, CHANNEL_ID); 
mBuilder.setContentTitle("Picture Download")  
.setContentText("Download in progress") 
.setSmallIcon(R.drawable.ic_notification) 
.setPriority(NotificationCompat.PRIORITY_LOW); 
 // Issue the initial notification with zero progress  
int PROGRESS_MAX =  100;
int PROGRESS_CURRENT =  0;
mBuilder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT,  false); 
notificationManager.notify(notificationId, mBuilder.build()); 
// Do the job here that tracks the progress. 
// Usually, this should be in a worker thread
// To show progress, update PROGRESS_CURRENT and update the notification with: 
// mBuilder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false); 
// notificationManager.notify(notificationId, mBuilder.build());

// When done, update the notification one more time to remove the progress bar 
mBuilder.setContentText("Download complete")  
.setProgress(0,0,false); 
notificationManager.notify(notificationId, mBuilder.build());

在操作结束时,进度应该等于最大值。 您可以在操作完成时退出进度条,也可以将其删除。 在任何一种情况下,请记住更新通知文本以显示操作已完成。 要删除进度条,请调用setProgress(0,0,false)

注意:由于进度条要求您的应用程序不断更新通知,因此此代码通常应在后台服务中运行。

要显示不确定的进度条(未指示完成百分比的条),请调用setProgress(0,0,true)。 结果是一个与上面的进度条具有相同样式的指示器,除了进度条是一个不表示完成的连续动画。 进度动画将一直运行,直到您调用setProgress(0,0,false),然后更新通知以删除活动指示符。

六、设置系统范围的类别

Android使用一些预定义的系统范围类别来确定当用户启用“请勿打扰”模式时是否使用给定通知打扰用户。

如果您的通知属于NotificationCompat中定义的预定义通知类别之一(例如CATEGORY_ALARMCATEGORY_REMINDERCATEGORY_EVENTCATEGORY_CALL),则应通过将适当的类别传递给setCategory()来声明它。

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setCategory(NotificationCompat.CATEGORY_MESSAGE);

当设备处于“请勿打扰”模式时,系统将使用有关您的通知类别的信息来决定是否显示通知。

但是,您不需要设置系统范围的类别,并且只有在通知与NotificationCompat中定义的类别之一匹配时才应该这样做。

七、设置锁定屏幕可见性

要控制锁定屏幕中通知中可见的详细程度,请调用setVisibility()并指定以下值之一:

  • VISIBILITY_PUBLIC显示通知的完整内容。

  • VISIBILITY_SECRET在锁定屏幕上不显示此通知的任何部分。

  • VISIBILITY_PRIVATE显示基本信息,例如通知的图标和内容标题,但隐藏通知的完整内容。

设置VISIBILITY_PRIVATE后,您还可以提供隐藏某些详细信息的备用通知内容版本。 例如,SMS应用程序可能会显示一条通知,显示您有3条新短信,但会隐藏邮件内容和发件人。 要提供此备用通知,请首先使用NotificationCompat.Builder创建备用通知。 然后使用setPublicVersion()将备用通知附加到正常通知。

但是,用户始终可以最终控制其通知是否在锁定屏幕上可见,甚至可以根据应用的通知渠道控制该通知。

八、更新通知

要在发出通知后更新此通知,请再次调用NotificationManagerCompat.notify(),并向其传递一个通知,该通知具有您之前使用的相同ID。 如果先前的通知已被取消,则会创建新通知。

您可以选择调用setOnlyAlertOnce(),这样您的通知就会在第一次出现通知时中断用户(有声音,振动或视觉线索),而不是以后的更新。

警告:Android在更新通知时应用速率限制。 如果过于频繁地发布通知更新(许多不到一秒钟),系统可能会删除一些更新。

九、删除通知

在发生以下情况之一之前,通知仍然可见:

  • 用户驳回通知。

  • 用户单击通知,并在创建通知时调用setAutoCancel()

  • 您可以为特定通知ID调用cancel()。 此方法还会删除正在进行的通知。

  • 您调用cancelAll(),它会删除您之前发出的所有通知。

  • 如果在使用setTimeoutAfter()创建通知时设置超时,则系统会在指定的持续时间过后取消通知。 如果需要,您可以在指定的超时持续时间过去之前取消通知。

十、消息传递应用的最佳做法

使用此处列出的最佳做法,快速参考为消息传递和聊天应用创建通知时应注意的事项。

使用MessagingStyle

Android 7.0(API级别24)开始,Android提供专门用于消息传递内容的通知样式模板。 使用NotificationCompat.MessagingStyle类,您可以更改通知上显示的多个标签,包括会话标题,其他消息和通知的内容视图。

以下代码段演示了如何使用MessagingStyle类自定义通知的样式。

Notification notification = new Notification.Builder(this, CHANNEL_ID)
        .setStyle(new NotificationCompat.MessagingStyle("Me")
        .setConversationTitle("Team lunch")
        .addMessage("Hi", timestamp1, null) // Pass in null for user.
        .addMessage("What's up?", timestamp2, "Coworker")
        .addMessage("Not much", timestamp3, null)
        .addMessage("How about lunch?", timestamp4, "Coworker"))
        .build();

Android 8.0(API级别26)开始,使用NotificationCompat.MessagingStyle类的通知以折叠形式显示更多内容。 您还可以使用addHistoricMessage()方法通过向消息传递相关通知添加历史消息来为对话提供上下文。

使用NotificationCompat.MessagingStyle时:

  • 调用MessagingStyle.setConversationTitle()为具有两个以上人员的组聊天设置标题。 一个好的会话标题可能是群聊的名称,如果没有特定的名称,则可能是会话参与者的列表。 如果没有这个,该消息可能被误认为属于与对话中最近消息的发送者的一对一对话。

  • 使用MessagingStyle.setData()方法包括媒体消息,如图像。 目前支持模式图像/ *的MIME类型。

使用直接回复

直接回复允许用户内联回复邮件。

  • 用户回复内联回复操作后,使用MessagingStyle.addMessage()更新MessagingStyle通知,不要撤消或取消通知。 不取消通知允许用户从通知中发送多个回复。

  • 要使内联回复操作与Wear OS兼容,请调用Action.WearableExtender.setHintDisplayInlineAction(true)

  • 使用addHistoricMessage()方法通过向通知添加历史消息来为直接回复对话提供上下文。

启用智能回复

要启用智能回复,请在回复操作上调用setAllowGeneratedResponses(true)。 当通知桥接到Wear OS设备时,这会导致用户可以使用智能回复响应。 智能回复响应由使用NotificationCompat.MessagingStyle通知提供的上下文的完全监视机器学习模型生成,并且没有数据上载到因特网以生成响应。

添加通知元数据

分配通知元数据,以告知系统在设备处于“请勿打扰”模式时如何处理应用程序通知。 例如,使用addPerson()setCategory(Notification.CATEGORY_MESSAGE)方法覆盖“请勿打扰”模式。

你可能感兴趣的:(创建通知)