1. 程式人生 > >Android O(8.0)通知欄適配

Android O(8.0)通知欄適配

從一個bug說起,前陣子拿Nexus 6P(Android 8.0)進行通知推送的測試,發現無法彈出通知欄。

專案跑在Android 8.0模擬器上彈出了Toast:
Developer warning for package “xxx.xxx.xxx”
Failed to post notification on channel “12345”
See log for more details

然後檢視log,system_process顯示一條error log:
NotificationService: No Channel found for pkg=xxx.xxx.xxx, channelId=12345, id=12345, tag=null, opPkg=xxx.xxx.xxx, callingUid=10085, userId=0, incomingUserId=0, notificationUid=10085, notification=Notification…

跟進NotificationManager的notify方法,呼叫了NotificationManagerService的enqueueNotificationWithTag將通知入隊,最後在NotificationManagerService的enqueueNotificationInternal方法中發現了error log的蹤跡,擷取程式碼片如下圖:這裡寫圖片描述

見圖可知,是由於此條通知沒有查詢到應用中對應的NotificationChannel的原因,而無法彈出來。那NotificationChannel是個什麼鬼,查閱官文得知,這是Android O新增的通知渠道,其允許您為要顯示的每種通知型別建立使用者可自定義的渠道。使用者介面將通知渠道稱之為通知類別。

先貼出我猜到坑的錯誤程式碼:

/**
 * 錯誤示例
 */
public static void showNotificationWrong(Context context) {
    int notificationId = 0x1234;
    NotificationCompat.Builder builder = new NotificationCompat.Builder(context, Integer.toString(notificationId));

    builder.setSmallIcon(android.R.drawable.stat_notify_chat);

    NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
    notificationManagerCompat.notify(notificationId, builder.build());
}

這裡的NotificationCompat及NotificationManagerCompat來自官方提供的相容庫:
com.android.support:appcompat-v7:26.1.0
想著最新的Compat庫相容應該做得是最好的,然後錯就錯在這句程式碼:
NotificationCompat.Builder(context, Integer.toString(notificationId));
一眼看到Builder建構函式的第二個引數,傳的是id,就直接把notificationId傳進去了,其實細看傳的是channelId,我這裡把channelId和notificationId概念搞混了。channelId傳null,或者只有一個引數的Builder的構造方法,都不會出現錯誤,修正程式碼如下:

NotificationCompat.Builder builder = new NotificationCompat.Builder(context, null);

//或者
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

原理就是:NotificationChannel是Android O新增的特性,為了相容老程式碼,如果channelId為null的話,Android O會把通知歸到“Other Channel”上。
PS:將targetSdkVersion提到26以上的話,就必須設定channel了,不能為null。

bug解了,現在來“肢解”下Android O的通知欄。

1. 從通知欄的使用出發
如下圖,通過傳統方式彈出通知後,我們可以通過側滑通知,拉出通知的設定選項。

第一個像時鐘一樣的按鍵,可以設定這條通知的延時展示,點選之後,你可以選擇這條通知需要延時的時間。如圖:

第二個設定鍵,則可以對通知展示的設定,見圖:

這張圖顯示的“其他”代表此條通知的渠道是“其他”,下面的描述含義是:來自此應用的2個通知類別(Channel)中的1個,旁邊的開關即代表你可以對此類別進行開關。

點選下方的ALL CATEGORIES按鍵,則進入應用通知的設定頁,如圖:

這裡,就可以對應用每個類別(Channel)的通知的開啟關閉,重要程度,桌面圖示是否展示小紅點(雖然不是紅色的)做一些設定。

2.新的重要特性:NotificationChannel
(1)建立NotificationChannel
如果你需要傳送屬於某個自定義渠道的通知,你需要在傳送通知前建立自定義通知渠道,示例如下:

//ChannelId為"1",ChannelName為"Channel1"
NotificationChannel channel = new NotificationChannel("1",
                "Channel1", NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(true); //是否在桌面icon右上角展示小紅點
channel.setLightColor(Color.GREEN); //小紅點顏色
channel.setShowBadge(true); //是否在久按桌面圖示時顯示此渠道的通知
notificationManager.createNotificationChannel(channel);

(2)向NotificationChannel傳送通知

public static void showChannel1Notification(Context context) {
    int notificationId = 0x1234;
    Notification.Builder builder = new Notification.Builder(context,"1"); //與channelId對應
    //icon title text必須包含,不然影響桌面圖示小紅點的展示
    builder.setSmallIcon(android.R.drawable.stat_notify_chat)
            .setContentTitle("xxx")
            .setContentText("xxx")
            .setNumber(3); //久按桌面圖示時允許的此條通知的數量
    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(notificationId, builder.build());
}

(3)刪除NotificationChannel

NotificationChannel mChannel = mNotificationManager.getNotificationChannel(id);
mNotificationManager.deleteNotificationChannel(mChannel);