Notification/NotificationChannel/PendingIntent(通知)-Android
Notification(通知)-Android
https://blog.csdn.net/qq_35507234/article/details/90676587
https://blog.csdn.net/yh18668197127/article/details/86299290
https://blog.csdn.net/dsc114/article/details/51721472
https://blog.csdn.net/guolin_blog/article/details/79854070
概述
当应用程序在后台运行,希望向用户发出一些提示学习,就需要借助Notification(通知)来实现。在发出一条通知后,手机最上方的状态栏会显示一个通知的图标,下拉状态栏后就可以看到通知的详细内容。
视图分类
Notification有两种视觉风格
- 标准视图(Normal View):在Android中各版本是通用
- 大视图(Big view):仅支持Android4.1+的版本。
标准视图
一个标准视图显示的大小保持在64dp高
1代表: 通知标题
2代表: 大图标
3代表: 通知内容
4代表: 通知消息
5代表: 小图标
6代表: 通知时间,一般为系统时间,也可以使用setWhen()设置。
大视图
细节区域只能显示256dp高度的内容,并且只对Android4.1+之后的设备才支持.
它比标准视图不一样的地方,均需要使用setStyle()方法设定,它大致的效果如下:
Android为我们提供了三个实现类,用于显示不同的场景。分别是:
-
Notification.BigPictureStyle
:在细节部分显示一个256dp高度的位图。 -
Notification.BigTextStyle
:在细节部分显示一个大的文本块。 -
Notification.InboxStyle
:在细节部分显示一段行文本 -
Notification.FLAG_SHOW_LIGHTS
:三色灯提醒,在使用三色灯提醒时候必须加该标志符 -
Notification.FLAG_ONGOING_EVENT
:发起正在运行事件(活动中) -
Notification.FLAG_INSISTENT
:让声音、振动无限循环,直到用户响应 (取消或者打开) -
Notification.FLAG_ONLY_ALERT_ONCE
:发起Notification后,铃声和震动均只执行一次 -
Notification.FLAG_AUTO_CANCEL
:用户单击通知后自动消失 -
Notification.FLAG_NO_CLEAR
:只有全部清除时,Notification才会清除 ,不清楚该通知(QQ的通知无法清除,就是用的这个。还有百度通知栏里面的搜索框也是这个)。
使用方法:在设置完属性后,设置
Notification notification = builder.build();
notification.flags = Notification.FLAG_ONLY_ALERT_ONCE;
基本方法
通知可以在活动里面创建,也可以在广播接收器里面创建,也可以在服务里面创建。
判断Android版本
//Android版本>=8.0
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
NotificationCompat的Builder构造器:
其中一些方法说明如下
方法 | 定义 |
---|---|
setAutoCancel(boolean boolean) | 设置点击通知后自动清除通知 |
setContent(RemoteView view) | 设置自定义通知 |
setContentTitle(String string) | 设置通知的标题内容 |
setContentText(String string) | 设置通知的正文内容 |
setContentIntent(PendingIntent intent) | 设置点击通知后的跳转意图 |
setWhen(long when) | 设置通知被创建的时间 |
setSmallIcon(int icon) | 设置通知的小图标 注意:只能使用纯alpha图层的图片进行设置,小图标会显示在系统状态栏上 |
setLargeIcon(Bitmap icon) | 设置通知的大图标,下拉系统状态栏时就能看见 |
setPriority(int pri) | 设置通知的重要程度 |
setStyle(Style style) | 设置通知的样式 比如设置长文字、大图片等等 |
setVisibility(int defaults) | 设置默认 |
setLight(int argb, int onMs, int offMs) | 设置呼吸闪烁效果 |
setSound(Uri sound) | 设置通知音效 |
setVibrate(long[] pattern) | 设置震动效果,数组包含手机静止时长和震动时长 下标0代表手机静止时长 下标1代表手机整的时长 下标2代表手机静止时长 下标3,4,5.......以此类推 还需要在AndroidManifest.xml中声明权限: <uses-permission android:name="android.permission.VIBRATE"/> |
setColor(int argb) | 设置通知栏颜色 |
setCategory(String category) | 设置通知类别 |
setFullScreenIntent(PendingIntent intent, boolean b) | 设置弹窗显示 |
setPriority常亮值
其中setPriority
(int pri)方法参数一共有5个常量值可选,调用NotificationCompat的常量值,如下所示:
public class NotificationCompat {
.......
//默认的重要程度,和不设置效果是一样的
public static final int PRIORITY_DEFAULT = 0;
//最低的重要程度,系统可能只会在特定的场合显示这条通知
public static final int PRIORITY_MIN = -2;
//较低的重要程度,系统可能会将这类通知缩小,或改变其显示的顺序
public static final int PRIORITY_LOW = -1;
//较高的重要程度,系统可能会将这类通知放大,或改变其显示的顺序
public static final int PRIORITY_HIGH = 1;
//最高的重要程度,表示这类通知消息必须让用户看到,甚至做出响应
public static final int PRIORITY_MAX = 2;
}
注意:当设置最高重要程度后,其显示效果和QQ发送好友消息一样,如果正在其他APP内,消息会显示在屏幕上让用户看见
使用步骤
1.创建一个NotificationManager
创建一个NotificationManager来对通知进行管理。通过调用Context.getSystemService(String s)方法获取到NotificationManager实例对象,字符串s参数用于确定获取系统的哪个服务,这里传入Context.NOTIFICATION_SERVICE即可,如下所示:
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
2.使用Builder构造器来创建Notification对象
在这里需要注意的是,为了解决API不稳定性问题和新老版本的兼容问题,使用
support-v4
提供的NotificationCompat
类的Builder
构造器来创建Notification对象,可以保证程序在所有的版本上都能正常工作。同时,Android8.0开始,废弃了Builder(@NonNull Context context)
方法,改用public Builder(@NonNull Context context, @NonNull String channelId
),如下所示:
//创建Notification,传入Context和channelId
Notification notification = new NotificationCompat.Builder(this, "chat")
.setAutoCancel(true)
.setContentTitle("收到聊天消息")
.setContentText("今天晚上吃什么")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
//在build()方法之前还可以添加其他方法
.build();
3. 调用NotificationManager的notify(int id, Notification notification)让通知显示
notify()方法接收两个参数,其中id表示每个通知所指定的id,要不一样。代码如下:
notificationManager.notify(1, notification);
使用此方法前,需要将NotificationChannel
(通知渠道创建出来)
4.让通知从状态栏消失方法
有两种方法:
- 上面创建Notification时,添加
setAutoCancel(true)
, - 通过动态代码方式,
NotificationManager.cancel(int id)
,如下:
//传入对应通知的id
notificationManager.cancel(1);
NotificationChannel(通知渠道)-Android
注意:Android8.0也就是API26开始要求通知设置Channel,否则会报错
概述
从Android 8.0系统开始,Google引入了通知渠道这个概念。
什么是通知渠道呢?顾名思义,就是每条通知都要属于一个对应的渠道。每个App都可以自由地创建当前App拥有哪些通知渠道,但是这些通知渠道的控制权都是掌握在用户手上的。用户可以自由地选择这些通知渠道的重要程度,是否响铃、是否振动、或者是否要关闭这个渠道的通知。
即 NotificationChannel
其实是把 Notification 分了个类别,设置不同优先级,开关之类的。如果你的 app 适配了的话,用户可以关掉不喜欢的通知,以提高用户体验。
拥有了这些控制权之后,用户就再也不用害怕那些垃圾推送消息的打扰了,因为用户可以自主地选择自己关心哪些通知、不关心哪些通知。
举个具体的例子,我希望可以即时收到支付宝的收款信息,因为我不想错过任何一笔收益,但是我又不想收到支付宝给我推荐的周围美食,因为我没钱只吃得起公司食堂。这种情况,支付宝就可以创建两种通知渠道,一个收支,一个推荐,而我作为用户对推荐类的通知不感兴趣,那么我就可以直接将推荐通知渠道关闭,这样既不影响我关心的通知,又不会让那些我不关心的通知来打扰我了。
对于每个App来说,通知渠道的划分是非常需要仔细考究的,因为通知渠道一旦创建之后就不能再修改了,因此开发者需要仔细分析自己的App一共有哪些类型的通知,然后再去创建相应的通知渠道。
这里我们来参考一下Twitter的通知渠道划分,如下所示:
可以看到,Twitter就是根据自己的通知类型,对通知渠道进行了非常详细的划分,这样用户的自主选择性就比较高了,也就大大降低了用户不堪其垃圾通知的骚扰而将App卸载的概率。
定义
-
通知渠道:Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。要了解如何实现通知渠道的信息,请参阅通知渠道指南。
-
通知标志:Android 8.0 引入了对在应用启动器图标上显示通知标志的支持。通知标志可反映某个应用是否存在与其关联、并且用户尚未予以清除也未对其采取行动的通知。通知标志也称为通知点。要了解如何调整通知标志,请参阅通知标志指南。
-
休眠:用户可以将通知置于休眠状态,以便稍后重新显示它。重新显示时通知的重要程度与首次显示时相同。应用可以移除或更新已休眠的通知,但更新休眠的通知并不会使其重新显示。
-
通知超时:现在,使用 setTimeoutAfter() 创建通知时您可以设置超时。您可以使用此函数指定一个持续时间,超过该持续时间后,通知应取消。如果需要,您可以在指定的超时持续时间之前取消通知。
-
通知设置:当您使用
Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCESIntent
从通知创建指向应用通知设置的链接时,您可以调用setSettingsText
() 来设置要显示的文本。此系统可以提供以下 Extra 数据和 Intent,用于过滤应用必须向用户显示的设置:EXTRA_CHANNEL_ID
、NOTIFICATION_TAG
和NOTIFICATION_ID
。 -
通知清除:系统现在可区分通知是由用户清除,还是由应用移除。要查看清除通知的方式,您应实现
NotificationListenerService
类的新onNotificationRemoved
() 函数。 -
背景颜色:您现在可以设置和启用通知的背景颜色。只能在用户必须一眼就能看到的持续任务的通知中使用此功能。例如,您可以为与驾车路线或正在进行的通话有关的通知设置背景颜色。您还可以使用 Notification.Builder.setColor() 设置所需的背景颜色。这样做将允许您使用 Notification.Builder.setColorized() 启用通知的背景颜色设置。
-
消息样式:现在,使用 MessagingStyle 类的通知可在其折叠形式中显示更多内容。对于与消息有关的通知,您应使用 MessagingStyle 类。您还可以使用新的 addHistoricMessage() 函数,通过向与消息相关的通知添加历史消息为会话提供上下文。
创建NotificationChannel
public class NotificationUtil {
private static final int NOTIFICATION_MUSIC_ID = 10000;
private static NotificationManager notificationManager;
......
//初始化NotificationManager
private static void initNotificationManager(Context context){
if (notificationManager == null){
notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
}
//判断是否为8.0以上:Build.VERSION_CODES.O为26
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//创建通知渠道ID
String channelId = "musicNotification";
//创建通知渠道名称
String channelName = "音乐播放器通知栏";
//创建通知渠道重要性
int importance = NotificationManager.IMPORTANCE_DEFAULT;
createNotificationChannel(context, channelId, channelName, importance);
}
}
//创建通知渠道
@TargetApi(Build.VERSION_CODES.O)
private static void createNotificationChannel(Context context, String channelId, String channelName, int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
//channel有很多set方法
......
//为NotificationManager设置通知渠道
notificationManager.createNotificationChannel(channel);
}
}
说明:这里传入的channelId要和创建的通知channelId一致,才能为指定通知建立通知渠道
NotificationChannel的方法列表
方法 | 定义 |
---|---|
getId() | 获取ChannelId |
enableLights(boolean boolean) | 是否开启指示灯(是否在桌面icon右上角展示小红点) |
setLightColor() | 设置指示灯颜色 |
enableVibration() | 是否开启整的 |
setVibrationPattern() | 设置震动频率 |
setImportance() | 设置频道重要性 |
getImportance() | 获取频道重要性 |
setSound() | 设置声音 |
getSound() | 获取声音 |
setGroup() | 设置 ChannleGroup |
getGroup() | 得到 ChannleGroup |
setBypassDnd() | 设置绕过免打扰模式 |
canBypassDnd() | 检测是否绕过免打扰模式 |
getName() | 获取通知渠道名称 |
setLockScreenVisibility() | 设置是否应在锁定屏幕上显示此频道的通知 |
getLockscreenVisibility() | 检测是否应在锁定屏幕上显示此频道的通知 |
setShowBadge() | 设置是否显示角标 |
canShowBadge() | 检测是否显示角标 |
重要程度
初始化的时候需要设置重要程度
数值越高,提示权限就越高,最高的支持发出声音和悬浮通知,如下所示:
public class NotificationManager {
......
//开启通知,不会弹出,发出提示音,状态栏中显示
public static final int IMPORTANCE_DEFAULT = 3;
//开启通知,会弹出,发出提示音,状态栏中显示
public static final int IMPORTANCE_HIGH = 4;
//开启通知,不会弹出,不发出提示音,状态栏中显示
public static final int IMPORTANCE_LOW = 2;
public static final int IMPORTANCE_MAX = 5;
//开启通知,不会弹出,但没有提示音,状态栏中无显示
public static final int IMPORTANCE_MIN = 1;
//关闭通知
public static final int IMPORTANCE_NONE = 0;
public static final int IMPORTANCE_UNSPECIFIED = -1000;
}
发出通知
调用NotificationManager的notify()方法即可
删除NotificationChann
调用NotificationManager
的deleteNotificationChannel(int chatChannelId)
即可。
渠道组
创建
// 创建一个渠道组
NotificationChannelGroup channelGroup = new NotificationChannelGroup("测试组ID", "渠道组名");
// 绑定渠道组
channel.setGroup("测试组ID");
notificationManager.createNotificationChannelGroup(channelGroup);
查询删除渠道和渠道组
// 查询所有当前用户的所有渠道
notificationManager.getNotificationChannels();
// 查询所有当前用户的所有渠道组
notificationManager.getNotificationChannelGroups();
// 根据ID删除渠道
notificationManager.deleteNotificationChannel("渠道ID");
// 根据ID删除渠道组
notificationManager.deleteNotificationChannel("测试组ID");
案例
案例1:
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送聊天通知"/>
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private NotificationManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelId = "chat";
String channelName = "聊天消息";
int importance = NotificationManager.IMPORTANCE_HIGH;
createNotificationChannel(channelId, channelName, importance);
channelId = "subscribe";
channelName = "订阅消息";
importance = NotificationManager.IMPORTANCE_DEFAULT;
createNotificationChannel(channelId, channelName, importance);
}
Button chat = findViewById(R.id.chat);
chat.setOnClickListener(this);
Button get = findViewById(R.id.get);
get.setOnClickListener(this);
}
@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel(String channelId, String channelName, int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
NotificationManager notificationManager = (NotificationManager) getSystemService(
NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.chat: //聊天消息
Notification notification = new NotificationCompat.Builder(this, "chat")
.setAutoCancel(true)
.setContentTitle("收到聊天消息")
.setContentText("今天晚上吃什么")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
//设置红色
.setColor(Color.parseColor("#F00606"))
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
.build();
manager.notify(1, notification);
break;
case R.id.get: //订阅消息
Notification notificationGet = new NotificationCompat.Builder(this, "subscribe")
.setAutoCancel(true)
.setContentTitle("收到订阅消息")
.setContentText("新闻消息")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setContentIntent(pendingIntentGet)
.build();
manager.notify(2, notificationGet);
break;
}
}
}
注意这里出现了大图标和小图标,聊天消息之所以是红色,因为setColor为红色,详情见:
https://blog.csdn.net/guolin_blog/article/details/50945228
使用RemoteViews自定义Notification
需要使用RemoteViews.RemoteViews
描述了一个视图层次的结构,可以显示在另一个进程。
RemoteViews提供了多个构造函数,一般使用RemoteViews(String packageName,int layoutId)。第一个参数为包的名称,第二个为layout资源的Id。当获取到RemoteViews对象之后,可以使用它的一系列setXxx()方法通过控件的Id设置控件的属性。最后使用NotificationCompat.Builder.setContent(RemoteViews)方法设置它到一个Notification中。
PendingIntent
对于一个通知而言,它显示的消息是有限的,一般仅用于提示一些概要信息。但是一般简短的消息,并不能表达需要告诉用户的全部内容,所以需要绑定一个意图,当用户点击通知的时候,调用一个意图展示出一个Activity用来显示详细的内容。而Notification中,并不使用常规的Intent去传递一个意图,而是使用PendingIntent。
Intent和PendingIntent的区别:
PendingIntent可以看做是对Intent的包装,通过名称可以看出PendingIntent用于处理即将发生的意图,而Intent用来用来处理马上发生的意图。而对于通知来说,它是一系统级的全局通知,并不确定这个意图被执行的时间。当在应用外部执行PendingIntent时,因为它保存了触发应用的Context,使得外部应用可以如在当前应用中一样,执行PendingIntent里的Intent,就算执行的时候响应通知的应用已经被销毁了,也可以通过存在PendingIntent里的Context照常执行它,并且还可以处理Intent说带来的额外信息。
因此可以将PendingIntent看做是延迟执行的Intent。
创建PendingIntent
获取PendingInten实例可以根据需求从如下方法中获取:
PendingInteng.getBroadcast(contex, requestCode, intent, flags)
PendingInteng.getService(contex, requestCode, intent, flags)
PendingInteng.getActivity(contex, requestCode, intent, flags)
PendingInteng.getActivities(contex, requestCode, intent, flags)
其中flags属性参数用于确定PendingIntent的行为:
FLAG_ONE_SHOT: 表示返回的PendingIntent仅能执行一次,执行完后自动消失
FLAG_NO_CREATE: 表示如果描述的PendingIntent不存在,并不创建相应的PendingIntent,而是返回NULL
FLAG_CANCEL_CURRENT: 表示相应的PendingIntent已经存在,则取消前者,然后创建新的PendingIntent
FLAG_UPDATE_CURRENT: 表示更新的PendingIntent,如果构建的PendingIntent已经存在,则替换它,常用。
获取到PendingIntent实例后,通过Builder构造器的setContentIntent(PendingIntent intent)方法,构建一个PendingIntent。
案例
修改上面案例的MainActivity,在代码中添加如下方法, 点击通知消息后,就可以跳转到NotificationActivity对应的界面了:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private NotificationManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
......
}
@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel(String channelId, String channelName, int importance) {
......
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.chat:
Intent intent = new Intent(this, NotificationActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this, "chat")
......
.setContentIntent(pendingIntent)
.build();
manager.notify(1, notification);
break;
case R.id.get:
Intent intentGet = new Intent(this, NotificationActivity.class);
PendingIntent pendingIntentGet = PendingIntent.getActivity(this, 0, intentGet, 0);
Notification notificationGet = new NotificationCompat.Builder(this, "subscribe")
......
.setContentIntent(pendingIntentGet)
.build();
manager.notify(2, notificationGet);
break;
}
}
}