前言
Notification,相信大部分 学Android都对他都很熟悉,而网上很多关于Notification的使用教程都是基于Android 8.0以下的,而 现在普遍的Android设备基本都在Android 6.0到Android9.0,而本节给大家介绍的是Android 8.0以下,Android8.0以上的适配;
具体可以查看
[官方文档] developer.android.google.cn/guide/topic…
通知栏介绍
通知栏的作用
通知栏的主要目的就是将一些重要的信息即使告诉用户,通知栏的设计非常巧妙,不用占用空间,只是在通知栏显示,当用户下拉就可以看到了,如果用户设置了通知的程度为最高的话,当受到消息会在当前界面显示内容(时间为2秒),之后就会隐藏到通知栏中。
通知栏适配
这里主要的难点是8.0(targetSdkVersion=26) ,在8.0以下的话我们可以在应用中找到显示通知,来进行关闭,这样的话所有的推送我们都收不到(包括一些重要的信息),所以8.0之后google新增了通知渠道,就是每条通知都要属于一个对应的渠道。每个App都可以自由地创建当前App拥有哪些通知渠道,但是这些通知渠道的控制权都是掌握在用户手上的。
一定要适配吗?
Google这次对于8.0系统通知渠道的推广态度还是比较强硬的。
首先,如果你升级了appcompat库,那么所有使用appcompat库来构建通知的地方全部都会进行废弃方法提示,如下所示:
上图告诉我们,此方法已废弃,需要使用带有通知渠道的方法才行。
使用方法
创建通知渠道
首先需要确保targetSdkVersion已经指定到了26或者更高,如下所示:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.notificationtest"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
创建渠道代码
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);
}
@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);
}
代码不长,我来简单解释下。这里我们在MainActivity中创建了两个通知渠道,首先要确保的是当前手机的系统版本必须是Android 8.0系统或者更高,因为低版本的手机系统并没有通知渠道这个功能,不做系统版本检查的话会在低版本手机上造成崩溃。
创建一个通知渠道的方式非常简单,这里我封装了一个createNotificationChannel()方法,里面的逻辑相信大家都看得懂。需要注意的是,创建一个通知渠道至少需要渠道ID、渠道名称以及重要等级这三个参数,其中渠道ID可以随便定义,只要保证全局唯一性就可以。渠道名称是给用户看的,需要能够表达清楚这个渠道的用途。重要等级的不同则会决定通知的不同行为,当然这里只是初始状态下的重要等级,用户可以随时手动更改某个渠道的重要等级,App是无法干预的。
封装工具类
记录一下,以便后面学习
/**
* 通知工具类
*
* @author iwen大大怪
* @create 2021/11/16 15:36
*/
public class QlNotificationUtil {
private static final String TAG = "QlNotificationUtil";
/**
* 请求代码
*/
public static final int RequestCode = 1;
/**
* 新消息
*/
public static final String NEW_MESSAGE = "chat";
/**
* 群消息
*/
public static final String NEW_GROUP = "chat_group";
/**
* 其他消息
*/
public static final String OTHER_MESSAGE = "other";
/**
* 提示
*/
public static final String Ticker = "您有一条新的消息";
/**
*
*/
public static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
/**
* 发布通知
*/
public static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
/**
* notifyId
*/
public static int notifyId = 0;
/**
* 适配 Android8.0 创建通知渠道
* tips:可以写在MainActivity中,也可以写在Application中,实际上可以写在程序的任何位置,
* 只需要保证在通知弹出之前调用就可以了。并且创建通知渠道的代码只在第一次执行的时候才会创建,
* 以后每次执行创建代码系统会检测到该通知渠道已经存在了,因此不会重复创建,也并不会影响任何效率。
*/
public static void setNotificationChannel(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelId = NEW_MESSAGE;
String channelName = "新消息通知";
createNotificationChannel(context, channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
channelId = OTHER_MESSAGE;
channelName = "其他通知";
createNotificationChannel(context, channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
}
}
/**
* 创建配置通知渠道
*
* @param channelId 渠道id
* @param channelName 渠道name
* @param importance 优先级
*/
@TargetApi(Build.VERSION_CODES.O)
private static void createNotificationChannel(Context context, String channelId, String channelName, int importance) {
// createNotificationGroup(context,channelId,channelName);
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
// 禁止该渠道使用角标
channel.setShowBadge(false);
// 设置渠道组id
// channel.setGroup(channelId);
// 配置通知渠道的属性
// channel .setDescription("渠道的描述");
// 设置通知出现时的闪灯(如果 android 设备支持的话)
channel.enableLights(true);
// 设置通知出现时的震动(如果 android 设备支持的话)
channel.enableVibration(true);
// 如上设置使手机:静止1秒,震动2秒,静止1秒,震动3秒
// channel.setVibrationPattern(new long[]{1000, 2000, 1000,3000});
// 设置锁屏是否显示通知
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
// 设置呼吸灯颜色
channel.setLightColor(Color.BLUE);
// 设置是否可以绕过请勿打扰模式
channel.setBypassDnd(true);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
/**
* 创建渠道组(若通知渠道比较多的情况下可以划分渠道组)
*
* @param groupId 渠道组id
* @param groupName 渠道组name
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static void createNotificationGroup(Context context, String groupId, String groupName) {
NotificationChannelGroup group = new NotificationChannelGroup(groupId, groupName);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannelGroup(group);
}
/**
* 发送通知(刷新前面的通知)
*
* @param context 上下文
* @param contentTitle 标题
* @param contentText 内容
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void show(Context context, String contentTitle, String contentText, Class<?> cls) {
show(context, contentTitle, contentText, null, 0, NEW_MESSAGE, cls);
}
/**
* 发送自定义通知(刷新前面的通知)
*
* @param context 上下文
* @param contentTitle 标题
* @param contentText 内容
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void show(Context context, String contentTitle, String contentText, RemoteViews views, Class<?> cls) {
show(context, contentTitle, contentText, views, 0, NEW_MESSAGE, cls);
}
/**
* 发送通知(刷新前面的通知,指定通知渠道)
*
* @param contentTitle 标题
* @param contentText 内容
* @param channelId 渠道id
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void show(Context context, String contentTitle, String contentText, String channelId, Class<?> cls) {
show(context, contentTitle, contentText, null, 0, channelId, cls);
}
/**
* 发送自定义通知(刷新前面的通知,指定通知渠道)
*
* @param contentTitle 标题
* @param contentText 内容
* @param channelId 渠道id
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void show(Context context, String contentTitle, String contentText, RemoteViews views, String channelId, Class<?> cls) {
show(context, contentTitle, contentText, views, 0, channelId, cls);
}
/**
* 发送多条通知(默认通知渠道)
*
* @param contentTitle 标题
* @param contentText 内容
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void showMuch(Context context, String contentTitle, String contentText, Class<?> cls) {
show(context, contentTitle, contentText, null, ++notifyId, NEW_MESSAGE, cls);
}
/**
* 发送多条自定义通知(默认通知渠道)
*
* @param contentTitle 标题
* @param contentText 内容
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void showMuch(Context context, String contentTitle, String contentText, RemoteViews views, Class<?> cls) {
show(context, contentTitle, contentText, views, ++notifyId, NEW_MESSAGE, cls);
}
/**
* 发送多条通知(指定通知渠道)
*
* @param contentTitle 标题
* @param contentText 内容
* @param channelId 渠道id
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void showMuch(Context context, String contentTitle, String contentText, String channelId, Class<?> cls) {
show(context, contentTitle, contentText, null, ++notifyId, channelId, cls);
}
/**
* 发送多条自定义通知(指定通知渠道)
*
* @param contentTitle 标题
* @param contentText 内容
* @param channelId 渠道id
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void showMuch(Context context, String contentTitle, String contentText, String channelId, RemoteViews views, Class<?> cls) {
show(context, contentTitle, contentText, views, ++notifyId, channelId, cls);
}
/**
* 发送通知(设置默认:大图标/小图标/小标题/副标题/优先级/首次弹出文本)
*
* @param contentTitle 标题
* @param contentText 内容
* @param notifyId 通知栏id
* @param channelId 设置渠道id
* @param cls 意图类
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void show(Context context, String contentTitle, String contentText, RemoteViews views, int notifyId, String channelId, Class<?> cls) {
show(context, 0, 0, contentTitle, null, contentText, NotificationManager.IMPORTANCE_DEFAULT, null, views, notifyId, channelId, cls);
}
/**
* 发送通知
*
* @param largeIcon 大图标
* @param smallIcon 小图标
* @param contentTitle 标题
* @param subText 小标题/副标题
* @param contentText 内容
* @param priority 优先级
* @param ticker 通知首次弹出时,状态栏上显示的文本
* @param notifyId 定义是否显示多条通知栏
* @param cls 意图类
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void show(Context context, int largeIcon,
int smallIcon, String contentTitle,
String subText, String contentText,
int priority, String ticker, RemoteViews view,
int notifyId, String channelId, Class<?> cls) {
//flags
// FLAG_ONE_SHOT:表示此PendingIntent只能使用一次的标志
// FLAG_IMMUTABLE:指示创建的PendingIntent应该是不可变的标志
// FLAG_NO_CREATE : 指示如果描述的PendingIntent尚不存在,则只返回null而不是创建它。
// FLAG_CANCEL_CURRENT :指示如果所描述的PendingIntent已存在,则应在生成新的PendingIntent,取消之前PendingIntent
// FLAG_UPDATE_CURRENT : 指示如果所描述的PendingIntent已存在,则保留它,但将其额外数据替换为此新Intent中的内容
PendingIntent pendingIntent = null;
// 添加隐示意图
if (cls != null) {
Intent intent = new Intent(context, cls);
pendingIntent = PendingIntent.getActivity(context, RequestCode, intent, FLAG_UPDATE_CURRENT);
}
// 获取通知服务管理器
NotificationManager manager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
// 判断应用通知是否打开
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (!openNotificationChannel(context, manager, channelId)) {
return;
}
}
// 创建 NEW_MESSAGE 渠道通知栏 在API级别26.1.0中推荐使用此构造函数 Builder(context, 渠道名)
NotificationCompat.Builder builder = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) ? new NotificationCompat.Builder(context, channelId) : new NotificationCompat.Builder(context);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), largeIcon == 0 ? R.mipmap.app_icon : largeIcon)) // 设置自动收报机和通知中显示的大图标。
.setSmallIcon(smallIcon == 0 ? R.mipmap.app_icon : smallIcon) // 小图标
.setContentText(TextUtils.isEmpty(contentText) ? null : contentText) // 内容
.setContentTitle(TextUtils.isEmpty(contentTitle) ? null : contentTitle) // 标题
.setSubText(TextUtils.isEmpty(subText) ? null : subText) // APP名称的副标题
.setPriority(priority) // 设置优先级 PRIORITY_DEFAULT
.setTicker(TextUtils.isEmpty(ticker) ? Ticker : ticker) // 设置通知首次弹出时,状态栏上显示的文本
.setContent(view)
.setWhen(System.currentTimeMillis()) // 设置通知发送的时间戳
.setShowWhen(true)// 设置是否显示时间戳
.setAutoCancel(true)// 点击通知后通知在通知栏上消失
.setDefaults(Notification.PRIORITY_HIGH)// 设置默认的提示音、振动方式、灯光等 使用的默认通知选项
.setContentIntent(pendingIntent) // 设置通知的点击事件
// 锁屏状态下显示通知图标及标题
// 1、VISIBILITY_PUBLIC 在所有锁定屏幕上完整显示此通知
// 2、VISIBILITY_PRIVATE 隐藏安全锁屏上的敏感或私人信息
// 3、VISIBILITY_SECRET 不显示任何部分
//.setVisibility(Notification.VISIBILITY_PRIVATE)// 部分手机没效果
.setFullScreenIntent(pendingIntent, true)// 悬挂式通知8.0需手动打开
// .setColorized(true)
// .setGroupSummary(true)// 将此通知设置为一组通知的组摘要
// .setGroup(NEW_GROUP)// 使用组密钥
// .setDeleteIntent(pendingIntent)// 当用户直接从通知面板清除通知时 发送意图
// .setFullScreenIntent(pendingIntent,true)
// .setContentInfo("大文本")// 在通知的右侧设置大文本。
// .setContent(RemoteViews RemoteView)// 设置自定义通知栏
// .setColor(Color.parseColor("#ff0000"))
// .setLights()//希望设备上的LED闪烁的argb值以及速率
// .setTimeoutAfter(3000)//指定取消此通知的时间(如果尚未取消)。
;
// 通知栏id
manager.notify(notifyId, builder.build()); // build()方法需要的最低API为16 ,
}
/**
* 判断应用渠道通知是否打开(适配8.0)
*
* @return true 打开
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static Boolean openNotificationChannel(Context context, NotificationManager manager, String channelId) {
// 判断通知是否有打开
if (!isNotificationEnabled(context)) {
toNotifySetting(context, null);
return false;
}
// 判断渠道通知是否打开
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = manager.getNotificationChannel(channelId);
if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
// 没打开调往设置界面
toNotifySetting(context, channel.getId());
return false;
}
}
return true;
}
/**
* 判断应用通知是否打开
*
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static boolean isNotificationEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
return notificationManagerCompat.areNotificationsEnabled();
}
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
Class appOpsClass = null;
/* Context.APP_OPS_MANAGER */
try {
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, context.getApplicationInfo().uid, context.getPackageName()) == AppOpsManager.MODE_ALLOWED);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 手动打开应用通知
*/
public static void toNotifySetting(Context context, String channelId) {
Intent intent = new Intent();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //适配 8.0及8.0以上(8.0需要先打开应用通知,再打开渠道通知)
if (TextUtils.isEmpty(channelId)) {
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
} else {
intent.setAction(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {// 适配 5.0及5.0以上
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra("app_package", context.getPackageName());
intent.putExtra("app_uid", context.getApplicationInfo().uid);
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {// 适配 4.4及4.4以上
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.fromParts("package", context.getPackageName(), null));
} else {
intent.setAction(Settings.ACTION_SETTINGS);
}
context.startActivity(intent);
}
}
使用方法
- 先初始化
- 直接调用
show
方法
// 通知栏初始化(适配8.0)
QlNotificationUtil.setNotificationChannel(getContext());
// 发送同一个通知
QlNotificationUtil.show(getContext(),"收到一条聊天消息", "今天中午吃什么?",null);
// 发送多个通知
QlNotificationUtil.showMuch(getContext(),"收到一条订阅消息", "今天中午吃什么?",null);
好啦,今天的介绍就到这里啦~