天天看點

Android 8.0+(一) 通知欄适配

    對于通知欄,大家都不陌生,應該算是裝置的基礎功能元件了吧,像推送消息,任務提醒,鬧鐘提示等等都需要借助裝置的通知欄展現給使用者,是以說一個醒目且友好的通知是很重要的,但是在這個android生态比較混亂的環境下,很多亂七八糟的通知,我明明不想接收,卻見天兒的被各種app推送轟炸。

    在Android 8.0 之前,有些做得好的廠商會針對每一個app提供一個通知的開關,但是這個權限被關了以後,就再也沒法接收到通知了,以緻于後面app的任何通知都無法展現給使用者,除非使用者再次開啟app的通知開關。

    也許Google意識到了這個問題,是以在Android 8.0上,NotificationChannel 應運而生。針對 targetSdkVersion在26以及上的裝置,如果想要顯示app通知,必須要注冊對應通知的channel,一個app可以注冊多個channel,而使用者則可以自行設定某一個channel的開關狀态,如果某一個channel的通知被關閉,那麼該channel的任何通知都不會展現給使用者,而其他channel的通知則不受影響

    一圖了解通知的基本組成

Android 8.0+(一) 通知欄适配

    在Android 8.0之前,如果想将消息顯示在通知欄上面,基本上是先建立一個 Notification ,然後由NotificationManager直接show() 是沒有任何問題的

public void notify(int id) {
    Notification notification = new NotificationCompat.Builder(this)
                .setAutoCancel(true)
                .setSmallIcon(getSmallIcon())
                .setLargeIcon(getLargeIcon())
                .setContentTitle(title)
                .setContentText(body)
                .setContentIntent(pendingIntent)
                .build();

    getManager().notify(id, notification);
 }
           

   但是如果在Android 8.0上執行這段代碼是不會顯示通知的,需要設定NotificationChannel才可以 ,而且設定channel之前,必須先注冊channel

NotificationChannel channel = new NotificationChannel(
                “DEFAULT_CHANNEL_ID”,
                "推送通知", 
                NotificationManager.IMPORTANCE_HIGH
);
 getManager().createNotificationChannel(channel);
           

  可以看到,NotificationChannel構造方法有三個參數,NotificationChannel(String id, CharSequence name,int  importance), 

       id:channel的唯一标示,同一app的不同channel ,id不能重複

       name:channel的描述資訊

       importance:該channel通知的重要程度

  最後通過NotificationManager注冊該channel, 注意: 必須在notify() 之前建立channel, 重複建立已有的channel不會有任何影響 

  現在,我們已經建立了一個channel,同樣的,android 8.0及以上和7.0及以下 建立Notification的方式也有差別 

private NotificationCompat.Builder getNotificationBuilderByChannel(String channelId) {
        NotificationCompat.Builder builder;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder = new NotificationCompat.Builder(getApplicationContext(), channelId);
        } else {
            builder = new NotificationCompat.Builder(this).setSound(soundUri);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//8.0以下 && 7.0及以上 設定優先級
                builder.setPriority(NotificationManager.IMPORTANCE_HIGH);
            } else {
                builder.setPriority(NotificationCompat.PRIORITY_HIGH);
            }
        }
        return builder;
    }
           

      由于8.0以後,NotificationCompat.Builder(Context context) 已經被廢棄,是以建立方式還是采用最新的API比較好,雖然8.0以下并沒有channel之說,但你如果看下源碼,會發現其實NotificationCompat.Builder(Context context) 内部調用了NotificationCompat.Builder(Context context,String channelId) ,隻不過channelId 傳入的是個null而已 , 是以現在來看NotificationCompat.Builder的建立方式是一樣的 。

/** @deprecated */
        @Deprecated
        public Builder(Context context) {
            this(context, (String)null);
        }
           

     另外需要注意的是,各版本設定通知的優先級的方式也有所不同,8.0及以上通知的優先級是在channel中設定的,8.0以下優先級是在NotificationCompat.Builder中設定的

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(DEFAULT_CHANNEL,
                    "我的通知Channel", NotificationManager.IMPORTANCE_HIGH);         
    }


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//8.0以下 && 7.0及以上 設定優先級
             builder.setPriority(NotificationManager.IMPORTANCE_HIGH);
        } else {
             builder.setPriority(NotificationCompat.PRIORITY_HIGH);
    }
           

至于優先級中各個屬性的具體介紹,請參考Android官方文檔 android developers (不需要翻牆)

可能你也注意到了,為什麼我隻在8.0以下設定了.setSound(soundUri); 那麼 8.0+ 怎麼設定通知的聲音?

這是因為8.0+ 是在建立channel的時候設定通知音效的

AudioAttributes att = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_NOTIFICATION)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                    .build();
NotificationChannel channel = new NotificationChannel(DEFAULT_CHANNEL,"我的通知 
                    Channel", NotificationManager.IMPORTANCE_HIGH);

channel.setSound(soundUri, att); 
getManager().createNotificationChannel(channel);
           

channel.setSound(soundUri, att);  除了設定音效的uri以外,還需要設定音頻屬性  ,這種方式有一種好處就是你可以給不同的channel設定不同的通知音效,如果沒有設定自定義的uri,則将使用手機預設的通知鈴聲

    現在,我們的通知欄已經适配了各個版本的api ,但是在某些手機上(oppo。。。),在應用安裝完成以後,其通知預設是關閉狀态,此時我們可以通過api判斷通知開關是否開啟,以此引導使用者手動去設定中開啟通知

NotificationManagerCompat.from(context).areNotificationsEnabled();
           

    你應該也注意到了,我這裡建立一個Notification使用的是NotificationCompat.Builder ,其實可以通過另一種方式 Notification.Builder 來建立 ,注意,這裡選擇前者的原因是NotificationCompat是相容低版本的,而且内部幫我們做了一些api版本相關的封裝,這裡引用官網的一段文檔看下解釋

Android 8.0+(一) 通知欄适配

 各版本的通知欄建立方式已經介紹完了,另外,自android8.0開始,支援在app啟動圖示上面顯示通知紅點,使用者可以長按應用程式圖示來檢視該應用程式的通知。然後,使用者可以通過左右滑動來關閉或處理來自該菜單的通知。下面簡單介紹下如何在通知欄顯示大文本和大圖

 通知欄顯示文本預設隻能顯示一行,如果我們的文本太長了,将導緻文本顯示不全(以...結尾),此時我們可以在建立Notification時設定通知顯示的樣式,以此來實作大文本的顯示

builder.setStyle(new NotificationCompat
                       .BigTextStyle()//大文本樣式
                       .setBigContentTitle(title)//通知展開時顯示的title
                       .bigText(body)//通知展開時顯示的全部文本
);
           

Android通知欄支援顯示大圖 ,但是隻能接收Bitmap類型的參數,是以如果你想要顯示網絡圖檔的話,需要自行将圖檔轉換為Bitmap

builder.setStyle(new NotificationCompat
                       .BigPictureStyle()//大圖模式
                       .setBigContentTitle(title)
                       .bigLargeIcon(imgBitmap)通知展開時顯示的縮略圖
                       .bigPicture(imgBitmap)//通知展開時顯示的大圖
);
           

最後來看下實作的效果吧

Android 8.0+(一) 通知欄适配