Android通知Notification使用全解析,看這篇就夠了

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第1天,點選檢視活動詳情

1、效果

| | | :--: | :--: |

2、簡介

通知是 Android 在您的應用 UI 之外顯示的訊息,用於向用戶提供提醒、來自其他人的通訊或來自您的應用的其他及時資訊。使用者可以點選通知開啟您的應用或直接從通知中執行操作。

2.1、展示

  • 通知以不同的位置和格式向用戶顯示,例如狀態列中的圖示、通知抽屜中更詳細的條目、應用程式圖示上的徽章以及自動配對的可穿戴裝置。
  • 當發出通知時,它首先在狀態列中顯示為一個圖示。

2.2、操作

  • 使用者可以在狀態列上向下滑動以開啟通知抽屜,他們可以在其中檢視更多詳細資訊並根據通知執行操作。
  • 使用者可以向下拖動抽屜中的通知以顯示展開的檢視,該檢視顯示其他內容和操作按鈕(如果提供)。
  • 通知在通知抽屜中保持可見,直到被應用程式或使用者關閉。

3、功能拆解

本文將帶領實現各種常見的通知功能,以及各個Android版本需要做的適配在這裡插入圖片描述

4、功能實現

4.0、關鍵類

  1. NotificationManager 通知管理器,用來發起、更新、刪除通知
  2. NotificationChannel 通知渠道,8.0及以上配置渠道以及優先順序
  3. NotificationCompat.Builder 通知構造器,用來配置通知的佈局顯示以及操作相關

常用API,檢視第5節。 各版本適配,檢視第6節。

4.1、普通通知

在這裡插入圖片描述

kotlin private fun createNotificationForNormal() { // 適配8.0及以上 建立渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(mNormalChannelId, mNormalChannelName, NotificationManager.IMPORTANCE_LOW).apply { description = "描述" setShowBadge(false) // 是否在桌面顯示角標 } mManager.createNotificationChannel(channel) } // 點選意圖 // setDeleteIntent 移除意圖 val intent = Intent(this, MaterialButtonActivity::class.java) val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) // 構建配置 mBuilder = NotificationCompat.Builder(this@NotificationActivity, mNormalChannelId) .setContentTitle("普通通知") // 標題 .setContentText("普通通知內容") // 文字 .setSmallIcon(R.mipmap.ic_launcher) // 小圖示 .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_avatar)) // 大圖示 .setPriority(NotificationCompat.PRIORITY_DEFAULT) // 7.0 設定優先順序 .setContentIntent(pendingIntent) // 跳轉配置 .setAutoCancel(true) // 是否自動消失(點選)or mManager.cancel(mNormalNotificationId)、cancelAll、setTimeoutAfter() // 發起通知 mManager.notify(mNormalNotificationId, mBuilder.build()) } 發起一個普通通知的幾個要素: - setContentTitle 標題 - setContentText 內容 - setSmallIcon 小圖示 - setLargeIcon 大圖示 - setPriority 優先順序or重要性(7.0和8.0的方式不同) - setContentIntent 點選意圖 - setAutoCancel 是否自動取消 - notify 發起通知

4.2、重要通知

在這裡插入圖片描述

重要通知,優先順序設定最高,會直接顯示在螢幕內(前臺),而不是隻有通知抽屜裡,所以一定要謹慎設定,不要引起使用者的負面情緒。

kotlin private fun createNotificationForHigh() { val intent = Intent(this, MaterialButtonActivity::class.java) val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(mHighChannelId, mHighChannelName, NotificationManager.IMPORTANCE_HIGH) channel.setShowBadge(true) mManager.createNotificationChannel(channel) } mBuilder = NotificationCompat.Builder(this@NotificationActivity, mHighChannelId) .setContentTitle("重要通知") .setContentText("重要通知內容") .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_avatar)) .setAutoCancel(true) .setNumber(999) // 自定義桌面通知數量 .addAction(R.mipmap.ic_avatar, "去看看", pendingIntent)// 通知上的操作 .setCategory(NotificationCompat.CATEGORY_MESSAGE) // 通知類別,"勿擾模式"時系統會決定要不要顯示你的通知 .setVisibility(NotificationCompat.VISIBILITY_PRIVATE) // 螢幕可見性,鎖屏時,顯示icon和標題,內容隱藏 mManager.notify(mHighNotificationId, mBuilder.build()) } 這裡有幾個新增的配置: - setNumber 桌面通知數量 - addAction 通知上的操作 - setCategory 通知類別,"勿擾模式"時系統會決定要不要顯示你的通知 - setVisibility 螢幕可見性,鎖屏時,顯示icon和標題,內容隱藏,解鎖檢視全部

4.2.1、通知上的操作

在這裡插入圖片描述

可以通過addAction在通知上新增一個自定義操作,如上圖:去看看。

可以通過PendingIntent開啟一個Activity,也可以是傳送一個廣播。

在Android10.0及以上,系統也會預設識別並新增一些操作,比如簡訊通知上的「複製驗證碼」。

4.2.2、重要性等級

在這裡插入圖片描述

  • 緊急:發出聲音並顯示為提醒通知
  • 高:發出聲音
  • 中:沒有聲音
  • 低:無聲音且不出現在狀態列中

4.3、進度條通知

在這裡插入圖片描述

```kotlin private fun createNotificationForProgress() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(mProgressChannelId, mProgressChannelName, NotificationManager.IMPORTANCE_DEFAULT) mManager.createNotificationChannel(channel) } val progressMax = 100 val progressCurrent = 30 mBuilder = NotificationCompat.Builder(this@NotificationActivity, mProgressChannelId) .setContentTitle("進度通知") .setContentText("下載中:$progressCurrent%") .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_avatar)) // 第3個引數indeterminate,false表示確定的進度,比如100,true表示不確定的進度,會一直顯示進度動畫,直到更新狀態下載完成,或刪除通知 .setProgress(progressMax, progressCurrent, false)

    mManager.notify(mProgressNotificationId, mBuilder.build())
}

``` 比較常見的就是下載進度了,比如應用內版本更新。

通過setProgress配置進度,接收3個引數: - max 最大值 - progress 當前進度 - indeterminate false表示確定的進度,比如100,true表示不確定的進度,會一直顯示進度動畫,直到更新狀態完成,或刪除通知

如何更新進度往下看。

4.4、更新進度條通知

在這裡插入圖片描述

kotlin private fun updateNotificationForProgress() { if (::mBuilder.isInitialized) { val progressMax = 100 val progressCurrent = 50 // 1.更新進度 mBuilder.setContentText("下載中:$progressCurrent%").setProgress(progressMax, progressCurrent, false) // 2.下載完成 //mBuilder.setContentText("下載完成!").setProgress(0, 0, false) mManager.notify(mProgressNotificationId, mBuilder.build()) Toast.makeText(this, "已更新進度到$progressCurrent%", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "請先發一條進度條通知", Toast.LENGTH_SHORT).show() } } 更新進度也還是通過setProgress,修改當前進度值即可。

更新分為兩種情況: 1. 更新進度:修改進度值即可 2. 下載完成:總進度與當前進度都設定為0即可,同時更新文案

注意:如果有多個進度通知,如何更新到指定的通知,是通過NotificationId匹配的。

Author:yechaoa

4.5、大文字通知

在這裡插入圖片描述

kotlin private fun createNotificationForBigText() { val bigText = "A notification is a message that Android displays outside your app's UI to provide the user with reminders, communication from other people, or other timely information from your app. Users can tap the notification to open your app or take an action directly from the notification." if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(mBigTextChannelId, mBigTextChannelName, NotificationManager.IMPORTANCE_DEFAULT) mManager.createNotificationChannel(channel) } mBuilder = NotificationCompat.Builder(this@NotificationActivity, mBigTextChannelId) .setContentTitle("大文字通知") .setStyle(NotificationCompat.BigTextStyle().bigText(bigText)) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_avatar)) .setAutoCancel(true) mManager.notify(mBigTextNotificationId, mBuilder.build()) }

  • setStyle(NotificationCompat.BigTextStyle().bigText(bigText))

通知內容預設最多顯示一行,超出會被裁剪,且無法展開,在內容透出上體驗非常不好,展示的內容可能無法吸引使用者去點選檢視,所以也有了大文字通知的這種方式,

一勞永逸的做法就是無論內容有多少行,都用大文字的這種方式通知,具體展示讓系統自己去適配。

4.6、大圖片通知

在這裡插入圖片描述

kotlin private fun createNotificationForBigImage() { val bigPic = BitmapFactory.decodeResource(resources, R.drawable.ic_big_pic) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(mBigImageChannelId, mBigImageChannelName, NotificationManager.IMPORTANCE_DEFAULT) mManager.createNotificationChannel(channel) } mBuilder = NotificationCompat.Builder(this@NotificationActivity, mBigImageChannelId) .setContentTitle("大圖片通知") .setContentText("有美女,展開看看") .setStyle(NotificationCompat.BigPictureStyle().bigPicture(bigPic)) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_avatar)) .setAutoCancel(true) mManager.notify(mBigImageNotificationId, mBuilder.build()) } 與大文字通知方式差不多

  • setStyle(NotificationCompat.BigPictureStyle().bigPicture(bigPic))

有一個注意的點,當已有多條通知時,預設是合併的,並不是展開的,所以可以通過setContentText("有美女,展開看看")加個提示。

  • 當前應用的通知不超過3條,會展開
  • 超過3條,通知會聚合並摺疊

4.7、自定義通知

在這裡插入圖片描述

```kotlin private fun createNotificationForCustom() { // 適配8.0及以上 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(mCustomChannelId, mCustomChannelName, NotificationManager.IMPORTANCE_DEFAULT) mManager.createNotificationChannel(channel) }

    // 適配12.0及以上
    mFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        PendingIntent.FLAG_IMMUTABLE
    } else {
        PendingIntent.FLAG_UPDATE_CURRENT
    }

    // 新增自定義通知view
    val views = RemoteViews(packageName, R.layout.layout_notification)

    // 新增暫停繼續事件
    val intentStop = Intent(mStopAction)
    val pendingIntentStop = PendingIntent.getBroadcast(this@NotificationActivity, 0, intentStop, mFlag)
    views.setOnClickPendingIntent(R.id.btn_stop, pendingIntentStop)

    // 新增完成事件
    val intentDone = Intent(mDoneAction)
    val pendingIntentDone = PendingIntent.getBroadcast(this@NotificationActivity, 0, intentDone, mFlag)
    views.setOnClickPendingIntent(R.id.btn_done, pendingIntentDone)

    // 建立Builder
    mBuilder = NotificationCompat.Builder(this@NotificationActivity, mCustomChannelId)
        .setSmallIcon(R.mipmap.ic_launcher)
        .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_avatar))
        .setAutoCancel(true)
        .setCustomContentView(views)
        .setCustomBigContentView(views)// 設定自定義通知view

    // 發起通知
    mManager.notify(mCustomNotificationId, mBuilder.build())
}

``` 假如是一個播放器的前臺通知,預設的佈局顯示已經不滿足需求,那麼就用到自定義佈局了。

通過RemoteViews構建自定義佈局view。因為RemoteViews並不是一個真正的view,它只是一個view的描述,所以事件處理上還是要藉助PendingIntent

  • setCustomContentView 預設佈局顯示,即摺疊狀態下的佈局
  • setCustomBigContentView 展開狀態下的佈局

摺疊狀態下,可能會展示一些基礎資訊,拿播放器舉例,比如當前歌曲名稱、歌手、暫停、繼續、下一首等,就差不多展示不下了。 展開狀態下,就可以提供更多的資訊,比如專輯資訊,歌手資訊等

這兩種狀態下預設的佈局高度: - 摺疊檢視佈局,48dp - 展開檢視佈局,252dp

4.8、更新自定義通知

在這裡插入圖片描述

```kotlin private fun updateNotificationForCustom() { // 傳送通知 更新狀態及UI sendBroadcast(Intent(mStopAction)) }

private fun updateCustomView() {
    val views = RemoteViews(packageName, R.layout.layout_notification)
    val intentUpdate = Intent(mStopAction)
    val pendingIntentUpdate = PendingIntent.getBroadcast(this, 0, intentUpdate, mFlag)
    views.setOnClickPendingIntent(R.id.btn_stop, pendingIntentUpdate)
    // 根據狀態更新UI
    if (mIsStop) {
        views.setTextViewText(R.id.tv_status, "那些你很冒險的夢-停止播放")
        views.setTextViewText(R.id.btn_stop, "繼續")
        mBinding.mbUpdateCustom.text = "繼續"
    } else {
        views.setTextViewText(R.id.tv_status, "那些你很冒險的夢-正在播放")
        views.setTextViewText(R.id.btn_stop, "暫停")
        mBinding.mbUpdateCustom.text = "暫停"
    }

    mBuilder.setCustomContentView(views).setCustomBigContentView(views)
    // 重新發起通知更新UI,注意:必須得是同一個通知id,即mCustomNotificationId
    mManager.notify(mCustomNotificationId, mBuilder.build())
}

```

上面提到,因為RemoteViews並不能直接操作view,所以可以通過廣播的方式,對該條通知的構建配置重新設定,以達到更新的效果。

遠古時期v4包裡還有MediaStyle,AndroidX已經下掉了。

5、常用API

| API | 描述 | |--|--| | setContentTitle | 標題 | | setContentText | 內容 | | setSubText | 子標題 | | setLargeIcon | 大圖示 | | setSmallIcon | 小圖示 | | setContentIntent | 點選時意圖 | | setDeleteIntent | 刪除時意圖 | | setFullScreenIntent | 全屏通知點選意圖,來電、響鈴 | | setAutoCancel | 點選自動取消 | | setCategory | 通知類別,適用“勿擾模式” | | setVisibility | 螢幕可見性,適用“鎖屏狀態” | | setNumber | 通知項數量 | | setWhen | 通知時間 | | setShowWhen | 是否顯示通知時間 | | setSound | 提示音 | | setVibrate | 震動 | | setLights | 呼吸燈 | | setPriority | 優先順序,7.0 | | setTimeoutAfter | 定時取消,8.0及以後 | | setProgress | 進度 | | setStyle | 通知樣式,BigPictureStyle、BigTextStyle、MessagingStyle、InboxStyle、DecoratedCustomViewStyle | | addAction | 通知上的操作,10.0 | | setGroup | 分組 | | setColor | 背景顏色 |

6、各版本適配

自Android 4.0支援通知以來,幾乎每個版本都有各種改動,也是苦了開發了...

6.1、Android 5.0

6.1.1、重要通知

Android 5.0開始,支援重要通知,也稱擡頭通知。

6.1.2、鎖屏通知

Android 5.0開始,支援鎖屏通知,即鎖屏時顯示在鎖屏桌面。

從8.0開始,使用者可以通過通知渠道設定啟用或禁止鎖屏通知...

6.1.3、勿擾模式

5.0開始,勿擾模式下會組織所有聲音和震動,8.0以後可以根據渠道分別設定。

6.2、Android 7.0

6.2.1、設定通知優先順序

7.1及以下: kotlin mBuilder = NotificationCompat.Builder(this@NotificationActivity, mNormalChannelId) ... .setPriority(NotificationCompat.PRIORITY_DEFAULT) // 7.0 設定優先順序

8.0及以上改為渠道。

6.2.2、回覆操作

7.0引入直接回復操作的功能 kotlin private val KEY_TEXT_REPLY = "key_text_reply" var replyLabel: String = resources.getString(R.string.reply_label) var remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run { setLabel(replyLabel) build() }

6.2.3、訊息通知

7.0開始支援訊息型別通知MessagingStyle kotlin var notification = NotificationCompat.Builder(this, CHANNEL_ID) .setStyle(NotificationCompat.MessagingStyle("Me") .setConversationTitle("Team lunch") .addMessage("Hi", timestamp1, null) // Pass in null for user. .addMessage("What's up?", timestamp2, "Coworker") .addMessage("Not much", timestamp3, null) .addMessage("How about lunch?", timestamp4, "Coworker")) .build() 從8.0開始,訊息型別的展示方式為摺疊型別...

6.2.4、通知分組

7.0開始,通知支援分組,適用多個通知的情況。

6.3、Android 8.0

6.3.1、建立通知渠道

建立通知渠道,以及重要性

kotlin if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(mHighChannelId, mHighChannelName, NotificationManager.IMPORTANCE_HIGH) mManager.createNotificationChannel(channel) }

刪除渠道

kotlin notificationManager.deleteNotificationChannel(id)

6.3.2、通知角標

8.0開始,支援設定通知時桌面的角標是否顯示 kotlin val mChannel = NotificationChannel(id, name, importance).apply { description = descriptionText setShowBadge(false) }

6.3.3、通知限制

8.1開始,每秒發出的通知聲音不能超過一次。

6.3.4、背景顏色

8.0開始,設定通知的背景顏色。

6.4、Android 10.0

6.4.1、新增操作

kotlin mBuilder = NotificationCompat.Builder(this@NotificationActivity, mHighChannelId) ... .addAction(R.mipmap.ic_avatar, "去看看", pendingIntent)// 通知上的操作

6.4.2、全屏意圖

10.0全屏意圖需要在manifest中申請USE_FULL_SCREEN_INTENT許可權

6.5、Android 12.0

6.5.1、解鎖裝置

12.0及以上,可以設定需要解鎖裝置才能操作:setAuthenticationRequired

kotlin val moreSecureNotification = Notification.Builder(context, NotificationListenerVerifierActivity.TAG) .addAction(...) // from a lock screen. .setAuthenticationRequired(true) .build()

6.5.2、自定義通知

從12.0開始,將不支援完全自定義的通知,會提供 Notification.DecoratedCustomViewStyle替代...

6.5.3、PendingIntent

12.0需要明確設定flag,否則會有報錯:

java.lang.IllegalArgumentException: com.example.imdemo: 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.

kotlin mFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_IMMUTABLE } else { PendingIntent.FLAG_UPDATE_CURRENT } val intentStop = Intent(mStopAction) val pendingIntentStop = PendingIntent.getBroadcast(this@NotificationActivity, 0, intentStop, mFlag) views.setOnClickPendingIntent(R.id.btn_stop, pendingIntentStop)

梳理完適配,真的覺得Androider苦😭

7、Github

http://github.com/yechaoa/MaterialDesign

程式碼也有詳細的註釋,歡迎star

8、參考文件

9、最後

寫作不易,感謝點贊支援 ^ - ^