介绍
本文主要实现:
- 判断是否开启通知权限;
- 发送通知;
- 点击通知跳转页面;
- 关闭通知;
实现效果
用小米手机(Android12)进行测试;
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
概念
NotificationManager:是状态栏通知的管理类,负责发通知、清除通知等操作。
NotificationChannel: Android O 引入了 通知渠道(Notification Channels),以提供统一的系统来帮助用户管理通知,如果是针对 android O 为目标平台时,必须实现一个或者多个通知渠道,以向用户显示通知。比如聊天软件,为每个聊天组设置一个通知渠道,指定特定声音、灯光等配置。
NotificationChannelGroup:对通知渠道进行分组,但是本文没有实现 。
Notification:通知信息类,它里面对应了通知栏的各个属性。
PendingIntent:一种特殊的 Intent ,字面意思可以解释为延迟的 Intent ,用于在某个事件结束后执行特定的 Action 。如:带 Action 的通知,当用户点击通知时,才会执行。
代码
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_sendNotification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送通知" />
<Button
android:id="@+id/btn_cancelNotification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="取消通知" />
</LinearLayout>
NotificationSetUtil 文件判断是否开启消息通知,没有开启的话跳转到手机系统设置界面;
一般情况下,Api 19 以前是没有通知管理的,默认都是开启,不用管。
Api 19 – 24 虽加入了通知管理功能,但没有开放检测是否开启了通知的接口,开发者只能用反射来获取权限值。
Api 24 以上,NotificationManager 提供了 areNotificationsEnabled()方法检测通知权限。
public class NotificationSetUtil {
//判断是否需要打开设置界面
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static boolean isOpenNotificationSetting(Context context) {
if (!isNotificationEnabled(context)) {
String message = "通知权限未开启,需要开启权限!";
showDialog(context,message); //如果没有通知权限,弹出对话框
return false;
} else {
return true; //有通知权限
}
}
/**
* 显示设置开启通知权限的dialog
*/
private static void showDialog(Context context,String message){
AlertDialog.Builder builder = new AlertDialog.Builder(context);
AlertDialog alert = builder.setIcon(R.mipmap.ic_dog)
.setTitle("系统提示:")
.setMessage(message)
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(context, "你点击了取消按钮~", Toast.LENGTH_SHORT).show();
}
})
.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(context, "你点击了确定按钮~", Toast.LENGTH_SHORT).show();
gotoSet(context);//打开手机设置页面
}
}).create(); //创建AlertDialog对象
alert.show(); //显示对话框
}
//判断该app是否打开了通知
/**
* 可以通过NotificationManagerCompat 中的 areNotificationsEnabled()来判断是否开启通知权限。NotificationManagerCompat 在 android.support.v4.app包中,是API 22.1.0 中加入的。而 areNotificationsEnabled()则是在 API 24.1.0之后加入的。
* areNotificationsEnabled 只对 API 19 及以上版本有效,低于API 19 会一直返回true
* */
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static boolean isNotificationEnabled(Context context) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
return NotificationManagerCompat.from(context).areNotificationsEnabled();
}else if (Build.VERSION.SDK_INT >= 19) {
String CHECK_OP_NO_THROW = "checkOpNoThrow";
String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
/* Context.APP_OPS_MANAGER */
try {
Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE,
String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
int value = (Integer) opPostNotificationValue.get(Integer.class);
return ((Integer) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}else {
return true;
}
}
}
activity文件
public class NotificationActivity extends AppCompatActivity {
Button btn_sendNotification;
Button btn_cancelNotification;
String channel_id = "notificationChannelId"; //NotificationChannel的ID
String channel_name = "notificationChannel的名称"; //NotificationChannel的名称
String channel_desc = "notificationChannel的描述"; //NotificationChannel的描述
String notification_title = "notification的标题";
String notification_text = "notification的内容";
int notificationId = 10086;
NotificationManager notificationManager; //NotificationManager:是状态栏通知的管理类,负责发通知、清除通知等操作。
private Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notification_layout);
context = this;
//初始化绑定控件
btn_sendNotification = findViewById(R.id.btn_sendNotification);
btn_cancelNotification = findViewById(R.id.btn_cancelNotification);
//创建通知渠道
createNotificationChannel();
//点击发送通知
btn_sendNotification.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (NotificationSetUtil.isOpenNotificationSetting(context)){
Toast.makeText(context, "已开启通知权限", Toast.LENGTH_SHORT).show();
//发送通知
sendNotification();
}
}
});
//点击取消通知
btn_cancelNotification.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//除了可以根据ID来取消Notification外,还可以调用cancelAll();关闭该应用产生的所有通知
notificationManager.cancel(notificationId); //取消Notification
}
});
}
/**
* 创建通知渠道
*/
private void createNotificationChannel() {
//Android8.0(API26)以上需要调用下列方法,但低版本由于支持库旧,不支持调用
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_HIGH;
//获得通知渠道对象
NotificationChannel channel = new NotificationChannel(channel_id, channel_name, importance);
//通知渠道设置描述
channel.setDescription(channel_desc);
// 设置通知出现时声音,默认通知是有声音的
channel.setSound(null, null);
// 设置通知出现时的闪灯(如果 android 设备支持的话)
channel.enableLights(true);
channel.setLightColor(Color.RED);
// 设置通知出现时的震动(如果 android 设备支持的话)
channel.enableVibration(true);
channel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
//设置自定义的提示音
//注意:通知渠道需要设置NotificationManager.IMPORTANCE_DEFAULT以上级别,才能设置铃声
channel.setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.biaobiao),Notification.AUDIO_ATTRIBUTES_DEFAULT);
//获得NotificationManager对象
notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
//在 notificationManager 中创建该通知渠道
notificationManager.createNotificationChannel(channel);
} else {//Android8.0(API26)以下
notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
}
}
/**
* 发送通知
*/
private void sendNotification() {
//定义一个PendingIntent点击Notification后启动一个Activity
Intent it = new Intent(this, MainActivity.class);
PendingIntent pit = PendingIntent.getActivity(this, 0, it, PendingIntent.FLAG_IMMUTABLE);
//配置通知栏的各个属性
Notification notification = new NotificationCompat.Builder(this, channel_id)
.setContentTitle(notification_title) //标题
.setContentText(notification_text) //内容
.setWhen(System.currentTimeMillis()) //设置通知时间,不设置默认当前时间
.setSmallIcon(R.mipmap.ic_launcher) //设置小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_dog)) //设置大图标
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE) //设置默认的三色灯与振动器
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true) //设置点击通知后,通知自动消失
.setContentIntent(pit) //设置PendingIntent
.build();
//用于显示通知,第一个参数为id,每个通知的id都必须不同。第二个参数为具体的通知对象
notificationManager.notify(notificationId, notification);
Toast.makeText(context, "发送通知成功!", Toast.LENGTH_SHORT).show();
}
}
遇到的问题
- PendingIntent遇到了报错,并根据提示使用了FLAG_IMMUTABLE;
PendingIntent pit = PendingIntent.getActivity(this, 0, it, PendingIntent.FLAG_IMMUTABLE);
java.lang.IllegalArgumentException: com.example.helloworld: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.
本文参考
菜鸟教程:Notification(状态栏通知)详解
Android Notification 详解
如何自定义Android推送提示音,让你的应用与众不同
Android O(8.0) Notification解决方案
NotificationSetUtilDemo【判断APP通知栏权限是否开启,以及如何跳转到应用程序设置界面】