Android 8.0 通知適配
阿新 • • 發佈:2018-12-17
下面說一下Android O (8.0)(API 26)通知的相關適配
一、分析
Android O 之前開啟一個App的設定的通知是這樣的
傳送一條通知通過下面程式碼
/* * 簡單的傳送通知 */ private void showNotification() { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.push) .setContentTitle("title") .setContentText("content") .build(); notificationManager.notify(1, notification); }
在Android O 之前呼叫上面程式碼App是可以正常收到一條通知的,但是在Android O之後呼叫上面程式碼並不會收到通知,會打印出下面的log資訊
No Channel found for pkg=com.bill.notificationtest, channelId=null, id=1, tag=null, opPkg=com.bill.notificationtest, callingUid=10246, userId=0, incomingUserId=0, notificationUid=10246, notification=Notification(channel=null pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0x00000000 vis=PRIVATE)
從Android8.0(API26)開始,Google 規定所有的通知必須分配一個渠道。每一個渠道,你都可以設定渠道中所有通知的視覺和聽覺行為。然後,使用者能夠隨意修改這些設定來決定通知的行為。
適配Android O 通知需要將App的targetSdkVersion修改為26以上,然後在Android 8.0以上的機型就可以使用,在Android 8.0以下的機型還是按照原有的規則。
下面看一下國內已經適配了Android O的App,下面為百度地圖和愛奇藝的設定通知頁,和上面頭條(未適配Android O)的對比一下,可以看到,百度地圖下面多了個類別,有百度地圖和未分類兩類,這兩個就是兩個渠道,其中"中"和"低"代表當前渠道訊息的重要等級,程式碼可以初始設定,使用者可以點進去幹預設定,並且可以單獨關閉某一個渠道的訊息,後面以使用者的設定為主。看下愛奇藝的設定下面東西更多了,有遊戲或應用下載、聊天訊息推送訊息等,這些是開發者建立的渠道組,組下面的是真正的渠道,開發者可以建立一些渠道組為渠道歸類,渠道組下面至少包含一個渠道,否則不顯示。其中渠道組不是必須的,但是渠道是必須要有的,沒有建立渠道組預設渠道放在一個叫類別的渠道組下,如百度地圖那樣。
二、適配
1、建立渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelId = "channel_chat";
String channelName = "新聊天訊息";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
notificationManager.createNotificationChannel(channel);
}
建立渠道就上面幾行程式碼,可以寫在任何地方,需要在傳送通知前呼叫,一個渠道Id系統只會建立一次,以後不會重複建立。建立渠道首先需要判斷版本,如果不判斷,在Android 8.0 以前的機型呼叫將會找不到Api會造成崩潰。然後建立渠道需要三個必填引數,channelId是渠道Id,開發者自己定義,在當前App內保證和其他渠道資訊唯一就好了,後面傳送訊息也是根據渠道Id傳送的。channelName是渠道名稱,是給使用者看的,文件建議最長為40個字元,否則可能被截斷,例如上面百度地圖App裡的百度地圖和未分類。importance是渠道訊息的重要等級,如上面百度地圖的中和低,使用者可以修改等級,具體取值如下:
IMPORTANCE_HIGH:緊急。有提示音和震動,在狀態列顯示,並會懸浮到App上。 IMPORTANCE_DEFAULT:高。有提示音和震動,在狀態列顯示。 IMPORTANCE_LOW:中。沒有提示音和震動,在狀態列顯示。 IMPORTANCE_MIN:低。沒有提示音和震動,不在狀態列顯示,摺疊在狀態列二級選單中,下拉可以看到。 IMPORTANCE_NONE:關閉通知。
NotificationChannel 還有幾個設定項,一般都不修改,須在createNotificationChannel()方法前呼叫。如下:
/*
* 為當前渠道新增一個描述資訊,使用者在點選渠道進入詳情頁,會在最下面看到
* 文件提示最長為300個字元,否則可能被截斷
*/
setDescription(String description);
/*
* 設定此渠道所屬的渠道組,僅用於展示。即不可以向一個組傳送通知,可以刪除一組渠道。
*/
setGroup(String groupId);
/*
* 設定App在桌面上顯示Icon上是否顯示角標,true:顯示(default)
*/
setShowBadge(boolean showBadge);
/*
* 設定通知的提示音,預設系統的,使用者可修改
* 注意只有在重要等級IMPORTANCE_DEFAULT和IMPORTANCE_HIGH才有提示音
*/
setSound(Uri sound, AudioAttributes audioAttributes);
/*
* 設定通知的指示燈顏色,手機需支援
*/
setLightColor(int argb);
/*
* 設定通知的震動模式
*/
setVibrationPattern(long[] vibrationPattern);
/*
* 繞過免打擾模式
* 注:Only modifiable by the system and notification ranker.(只能被系統和通知服務修改)
*/
setBypassDnd(boolean bypassDnd);
/*
* 是否在鎖定螢幕上顯示通知
* 注:Only modifiable by the system and notification ranker.(只能被系統和通知服務修改)
*/
setLockscreenVisibility(int lockscreenVisibility);
呼叫上面程式碼後會渠道就會被建立成功,在設定介面如下:
2、傳送通知
/*
* 簡單的傳送通知
*/
private void showNotification() {
String channelId = "channel_chat";
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.push)
.setContentTitle("title")
.setContentText("content")
.build();
notificationManager.notify(1, notification);
}
傳送通知只在建立Builder時多了一個channelId引數,在Android 8.0及以上機型會根據渠道Id傳送通知,Andoird 8.0以下會忽略。
3、建立渠道組
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String groupId = "group_chat";
String groupName = "聊天訊息";
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannelGroup group = new NotificationChannelGroup(groupId, groupName);
notificationManager.createNotificationChannelGroup(group);
String channelId = "channel_chat";
String channelName = "新聊天訊息";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
channel.setGroup(groupId);
notificationManager.createNotificationChannel(channel);
}
建立渠道組只需要一個groupId必填引數,並保證在App內和其他渠道組唯一。通過上面程式碼就建立了一個叫聊天訊息的渠道組,並將新聊天訊息這個渠道放到聊天訊息渠道組裡。執行上面程式碼,檢視設定頁如下:
渠道組僅用於展示,傳送通知和上面一樣。
4、刪除渠道和組
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.deleteNotificationChannel(channelId);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.deleteNotificationChannelGroup(groupId);
}
開發者可以刪除一個或一個渠道組(多個渠道),不過刪除之後會在設定頁顯示已刪除的渠道數,非常不美觀。謹慎刪除。
5、通知關閉
/*
* 判斷App通知是否開啟(總開關)
* true:開
*/
public boolean areNotificationsEnabled() {
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
return notificationManagerCompat.areNotificationsEnabled();
}
/*
* 判斷當前渠道通知是否開啟
* true:開
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public boolean areChannelsEnabled(@NonNull String channelId) {
NotificationChannel notificationChannel = notificationManager.getNotificationChannel(channelId);
if (notificationChannel != null && notificationChannel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
return false;
}
return true;
}
/*
* 跳轉到渠道設定頁
*/
public void gotoChannelSetting(@NonNull String channelId, @NonNull Context context) {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
context.startActivity(intent);
}
注:當通知總開關被關閉時即areNotificationsEnabled()返回false時,渠道開關即areChannelsEnabled("")可能是true。所以判斷渠道版本開關前應該先判斷總開關。
三、完整示例
1、在Activity的onCreate中新增如下程式碼
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 聊天訊息
String groupId_1 = "group_chat";
String groupName_1 = "聊天訊息";
NotificationChannelGroup group = new NotificationChannelGroup(groupId_1, groupName_1);
notificationManager.createNotificationChannelGroup(group);
String channelId1 = "channel_chat";
String channelName1 = "新聊天訊息";
int importance1 = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel1 = new NotificationChannel(channelId1, channelName1, importance1);
channel1.setGroup(groupId_1);
notificationManager.createNotificationChannel(channel1);
// 下載訊息
String groupId2 = "group_download";
String groupName2 = "下載訊息";
NotificationChannelGroup group2 = new NotificationChannelGroup(groupId2, groupName2);
notificationManager.createNotificationChannelGroup(group2);
String channelId_2_1 = "channel_download_complete";
String channelName_2_1 = "下載完成";
int importance_2_1 = NotificationManager.IMPORTANCE_LOW;
NotificationChannel channel_2_1 = new NotificationChannel(channelId_2_1, channelName_2_1, importance_2_1);
channel_2_1.setGroup(groupId2);
notificationManager.createNotificationChannel(channel_2_1);
String channelId_2_2 = "channel_download_error";
String channelName_2_2 = "下載失敗";
int importance_2_2 = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel_2_2 = new NotificationChannel(channelId_2_2, channelName_2_2, importance_2_2);
channel_2_2.setGroup(groupId2);
notificationManager.createNotificationChannel(channel_2_2);
// 未分類
String channelId_3 = "channel_other";
String channelName_3 = "未分類";
int importance_3 = NotificationManager.IMPORTANCE_MIN;
NotificationChannel channel_3 = new NotificationChannel(channelId_3, channelName_3, importance_3);
notificationManager.createNotificationChannel(channel_3);
}
}
2、傳送通知
/**
* 傳送通知
*
* @param channelId 渠道Id,按渠道傳送通知
*/
private void sendNotification(@NonNull String channelId) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.push)
.setContentTitle("title")
.setContentText("content")
.build();
int notifyId = new Random().nextInt(50000);
notificationManager.notify(notifyId, notification);
}
3、截圖
四、程式碼封裝
上面使用重複程式碼太多了,不方便使用,下面簡單封裝一下
/**
* Created by Bill on 2018/10/22.
* 通知管理類
*/
public class NotifyManager {
private Context context;
private NotificationManager notificationManager;
private Random random;
public NotifyManager(@NonNull Context context) {
this.context = context.getApplicationContext();
init();
}
private void init() {
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
random = new Random();
}
/**
* 建立渠道
*
* @param channelId 渠道Id
* @param channelName 渠道名
* @param importance 等級
* @param description 渠道描述
*/
public void createNotificationChannel(@NonNull String channelId, @NonNull String channelName, @ImportanceType int importance, @Nullable String description) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(channelId, channelName, importance, description, null);
}
}
/**
* 建立渠道組和一個渠道
*
* @param groupId
* @param groupName
* @param channel
*/
public void createNotificationGroupWithChannel(@NonNull String groupId, @Nullable String groupName, @NonNull ChannelEntity channel) {
ArrayList<ChannelEntity> channelList = new ArrayList<>();
channelList.add(channel);
createNotificationGroupWithChannel(groupId, groupName, channelList);
}
/**
* 建立渠道組和一組渠道
*
* @param groupId
* @param groupName
* @param channelList
*/
public void createNotificationGroupWithChannel(@NonNull String groupId, @Nullable String groupName, @NonNull ArrayList<ChannelEntity> channelList) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
if (!TextUtils.isEmpty(groupId)) {
createNotificationGroup(groupId, groupName);
}
for (ChannelEntity channel : channelList) {
createNotificationChannel(channel.getChannelId(), channel.getChannelName(), channel.getImportance(), channel.getDescription(), groupId);
}
}
}
/**
* 建立渠道,並建立組
*
* @param channelId
* @param channelName
* @param importance
* @param description
* @param groupId
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private void createNotificationChannel(@NonNull String channelId, @NonNull String channelName, @ImportanceType int importance,
@Nullable String description, @Nullable String groupId) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
if (!TextUtils.isEmpty(description))
channel.setDescription(description);
if (!TextUtils.isEmpty(groupId))
channel.setGroup(groupId);
notificationManager.createNotificationChannel(channel);
}
/**
* 建立渠道組
*
* @param groupId
* @param groupName
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private void createNotificationGroup(@NonNull String groupId, @Nullable String groupName) {
NotificationChannelGroup group = new NotificationChannelGroup(groupId, groupName);
notificationManager.createNotificationChannelGroup(group);
}
/**
* 刪除渠道
*
* @param channelId
*/
public void deleteNotificationChannel(@NonNull String channelId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.deleteNotificationChannel(channelId);
}
}
/**
* 刪除組
*
* @param groupId
*/
public void deleteNotificationChannelGroup(@NonNull String groupId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.deleteNotificationChannelGroup(groupId);
}
}
/**
* 傳送通知
*
* @param notification 通知具體內容
* @return 通知Id
*/
public int notifyNotify(@NonNull Notification notification) {
int notifyId = getRandomId();
return notifyNotify(notifyId, notification);
}
/**
* 傳送通知
*
* @param notifyId 通知Id
* @param notification 通知具體內容
* @return
*/
public int notifyNotify(int notifyId, @NonNull Notification notification) {
notificationManager.notify(notifyId, notification);
return notifyId;
}
/**
* 關閉狀態列通知的顯示
*
* @param notifyId 通知Id
*/
public void cancelNotify(int notifyId) {
notificationManager.cancel(notifyId);
}
/**
* 預設設定,呼叫方可以新增和修改
*
* @param channelId
* @return
*/
public NotificationCompat.Builder getDefaultBuilder(@NonNull String channelId) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
builder.setSmallIcon(R.drawable.push)
.setColor(Color.parseColor("#E92110"));
return builder;
}
/**
* 檢查當前渠道的通知是否可用,Android O及以上版本呼叫
* <p>
* 注:areNotificationsEnabled()返回false時,即當前App通知被關時,此方法仍可能返回true,
*
* @param channelId 渠道Id
* @return false:不可用
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public boolean areChannelsEnabled(@NonNull String channelId) {
NotificationChannel notificationChannel = notificationManager.getNotificationChannel(channelId);
if (notificationChannel != null && notificationChannel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
return false;
}
return true;
}
/**
* 檢查通知是否可用
*
* @return false:不可用
*/
public boolean areNotificationsEnabled() {
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
return notificationManagerCompat.areNotificationsEnabled();
}
/**
* 調轉到渠道設定頁
*
* @param channelId
*/
public void gotoChannelSetting(@NonNull String channelId) {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
context.startActivity(intent);
}
/**
* Generate a random integer
*
* @return int, [0, 50000)
*/
private int getRandomId() {
return random.nextInt(50000);
}
}
@IntDef({ImportanceType.IMPORTANCE_NONE,
ImportanceType.IMPORTANCE_MIN,
ImportanceType.IMPORTANCE_LOW,
ImportanceType.IMPORTANCE_DEFAULT,
ImportanceType.IMPORTANCE_HIGH})
public @interface ImportanceType {
int IMPORTANCE_NONE = 0;
int IMPORTANCE_MIN = 1;
int IMPORTANCE_LOW = 2;
int IMPORTANCE_DEFAULT = 3;
int IMPORTANCE_HIGH = 4;
}
public class ChannelEntity {
private String channelId;
private String channelName;
private int importance;
private String description;
public ChannelEntity(@NonNull String channelId, @NonNull String channelName, @ImportanceType int importance) {
this.channelId = channelId;
this.channelName = channelName;
this.importance = importance;
}
public String getChannelId() {
return channelId;
}
public String getChannelName() {
return channelName;
}
public int getImportance() {
return importance;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
呼叫如下:
private NotifyManager notifyManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
notifyManager = new NotifyManager(this);
ChannelEntity chatChannel = new ChannelEntity(Constants.CHANNEL_CHAT, "新聊天訊息", ImportanceType.IMPORTANCE_HIGH);
chatChannel.setDescription("個人或群組發來的聊天訊息");
notifyManager.createNotificationGroupWithChannel(Constants.GROUP_CHAT, "聊天訊息", chatChannel);
ArrayList<ChannelEntity> channelEntityArrayList = new ArrayList<>();
ChannelEntity downloadCompleteChannel = new ChannelEntity(Constants.CHANNEL_DOWNLOAD_COMPLETE, "下載完成", ImportanceType.IMPORTANCE_LOW);
downloadCompleteChannel.setDescription("下載完成後通知欄顯示");
channelEntityArrayList.add(downloadCompleteChannel);
ChannelEntity downloadProgressChannel = new ChannelEntity(Constants.CHANNEL_DOWNLOAD_ERROR, "下載失敗", ImportanceType.IMPORTANCE_DEFAULT);
downloadProgressChannel.setDescription("下載出現問題,下載失敗");
channelEntityArrayList.add(downloadProgressChannel);
notifyManager.createNotificationGroupWithChannel(Constants.GROUP_DOWNLOAD, "下載訊息", channelEntityArrayList);
notifyManager.createNotificationChannel(Constants.CHANNEL_OTHER, "未分類", ImportanceType.IMPORTANCE_MIN, null);
}
傳送通知:
/**
* 傳送通知
*
* @param channelId 渠道Id,按渠道傳送通知
*/
private void sendNotification() {
NotificationCompat.Builder builder = notifyManager.getDefaultBuilder(Constants.CHANNEL_DOWNLOAD_COMPLETE);
builder.setContentTitle("下載完成");
builder.setContentText("下載完成,可在我的下載中檢視");
Notification notification = builder.build();
notifyManager.notifyNotify(notification);
}