1. 程式人生 > >Android通知欄介紹與適配總結(上篇)

Android通知欄介紹與適配總結(上篇)

此文已由作者黎星授權網易雲社群釋出。

歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。


由於歷史原因,Android在釋出之初對通知欄Notification的設計相當簡單,而如今面對各式各樣的通知欄玩法,谷歌也不得不對其進行更新迭代調整,增加新功能的同時,也在不斷地改變樣式,試圖迎合更多人的口味。本文總結了Android通知欄的版本迭代過程,在通知欄開發過程中所遇到的各種各樣的坑,以及一些解決技巧,特別的,對於大眾期盼的Android 7.0的到來,通知欄又會發生怎樣的改變呢?接下來一一進行介紹。


Android通知欄發展歷史

首先來看一張各個Android版本通知欄訊息的全家福。

201808280938219a7e1e18-1357-4512-848d-fb991ce4951a.jpg


點選檢視大圖


Android通知欄從最初的Android1.1系統一直到如今的7.X版本,發生了翻天覆地的變化。從圖中可以看出,1.X-2.2版本的通知欄採用了白色背景和黑色字型;2.3-4.X版本,預設背景變成了黑色,而主標題採用白色字型,內容為灰色字型。從Android5.0開始,又更改為白色背景和黑色字型。當然,這只是原生的Android系統通知欄預設顏色,許多廠商對每個Android的版本都嘗試了各式各樣的修改,在此不一一介紹。


下面分別介紹每個版本的更新和修改記錄。


Android 1.X 修改記錄^1

Android 1.X版本也就是第一個Android誕生的版本。從Android1.1版本開始,提供基本的通知欄訊息功能,包含小圖示、主標題、副標題和時間這四個元素。右上角有一個清除通知欄訊息的按鈕。需要說明的是,Android從一開始就提供了清除通知欄訊息的功能並且保留至今,而iOS到現在都沒有提供清除按鈕。


Android 2.X 修改記錄^2

Android 2.X版本的通知欄訊息功能上並未發生變化,右上角的“clear notifications”縮減為了“clear”。2.2版本以前沿用了1.5的通知欄樣式,從2.3版本開始重新設計,改成了暗色背景。


Android 3.X 修改記錄^3

Android 3.X版本是專為Pad而設計的系統。通知欄訊息帶來了一些新的功能。


  • 非永久的通知欄訊息的右邊增加了“X”按鈕,點選後該條通知可以立即清除。

  • 增加了RemoteControlClient,即遠端控制媒體應用的功能。

  • 增加了LargeIcon,可以使用大圖展示通知欄訊息。


Android 4.1 修改記錄^4

Android 4.1版本的通知欄在3.X版本的基礎上進行了大量修改。增加了不少新功能。

  • 增加了Style

  • 增加了通知欄按鈕

  • 支援通知欄展示的優先順序配置

  • 通知欄背景改為黑色透明


通知欄樣式

Android 4.1通知欄最大的變化就是增加了豐富多樣的Style樣式。通過設定樣式,可以展示更大區域的通知訊息,如展示大圖和多行文字,也可以展示類似郵箱收發信的樣式,同時支援自定義按鈕並增加點選事件。但需要注意的是,只有最頂部的那條通知欄訊息可以預設展示Style樣式,其他訊息預設是以普通樣式展示。Style可以通過Notification.Builder.setStyle(Style)進行設定。具體支援的樣式有:


Notification.BigPictureStyle

大圖樣式,即除了普通的通知欄訊息內容外,可以在通知欄訊息下方展示一張大圖,最大高度支援256dp。


Notification.BigTextStyle

多行文字樣式,可以支援多行文字的展示。經測試,在不同手機上能夠支援的行數不一樣,測試過的機子,最大支援12行。


Notification.InboxStyle

收件箱樣式。支援展示具有一串訊息內容的會話樣式,適用於簡訊、郵件、IM等。


通知欄按鈕

通知欄訊息不管是普通樣式還是Style樣式,都支援兩個按鈕同時出現在一條通知欄訊息的底部,通過這兩個按鈕,可以自定義一系列動作,包括回覆資訊和郵件,點贊等。通過Notification.Builder.addAction(Action)新增按鈕。


通知欄優先順序

Android 4.1通知欄增加了優先順序的配置,優先順序高的訊息可以展示在最上方。谷歌設計優先順序的初衷是根據不同的優先順序來防止使用者整天被各種莫名其妙的通知欄訊息騷擾,重要的通知則應該適當提高優先順序,使得使用者可以快速地看到並回應,不重要的通知則降低優先順序,防止使用者被打擾。優先順序一共有5個級別,分別是:

// 預設優先順序
public static final int PRIORITY_DEFAULT = 0;
// 低優先順序
public static final int PRIORITY_LOW = -1;
// 最低優先順序
public static final int PRIORITY_MIN = -2;
// 高優先順序
public static final int PRIORITY_HIGH = 1;
// 最高優先順序
public static final int PRIORITY_MAX = 2;

Android 4.3 修改記錄^6

Android 4.3通知欄沒有發生大的變化。主要增加了兩個小功能。


  • 增加了Notification AccessApi,允許可穿戴裝置遠端控制通知欄訊息。

  • 增加了NotificationListenerService,允許接收到系統通知欄列表的變化

Android 5.X 修改記錄^7

Android 5.X系統相較於以前的版本,可以說是一個真正可以和iOS抗衡的系統。材料設計給Android系統注入了新的活力,相應的通知欄訊息也相較於上一個版本進行了改版。所發生的變化有:


  • 通知欄修改為白色背景,暗色字型,以適應材料設計風格。

  • 系統會忽略所有non-alpha通道的圖示,包括按鈕圖示和主圖示。

  • 可以通過setColor()方法在圖示後設置一個背景色。

  • 通知訊息的聲音將通過STREAM_RING或者STREAM_NOTIFICATION控制,以前是通過STREAM_MUSIC控制。

  • 鎖屏狀態下,可以控制通知欄訊息的隱私程度。

  • 移除了RemoteControlClient,更改為NotificationCompat.MediaStyle實現。

  • 增加了Heads-up通知,即通過狀態列浮動視窗展示通知訊息。


Android 6.X 修改記錄^8

  • 移除了Notification.setLatestEventInfo()方法,通過持有Notification.Builder,然後使用build()方法可以更新同一個通知欄例項。

  • 允許使用者控制應用通知的優先順序。

  • 加入了免打擾模式(Do Not Disturb)。

  • 增加了getActiveNotifications()方法獲取當前展示的通知訊息。


Android 7.X 修改記錄

  • 通知欄樣式全面改版,小圖示在左上角,大圖示在右邊,小圖示、App應用名、副標題、數量和時間在第一行,第二行是主標題,第三行是內容。

  • 增加了Notification.DecoratedCustomViewStyle()和Notification.DecoratedMediaCustomViewStyle(),幫助更好的裝飾帶有RemoteViews的通知欄訊息。

  • 需要動態設定Builder.setShowWhen(true)才會顯示時間。

  • 支援Action的直接回復,通過RemoteInput實現,且回覆的訊息內容支援立即新增到通知欄。

  • 支援通知訊息組,相似的訊息在達到一定數量後會按照訊息組來顯示。

  • 增加了NotificationManager.areNotificationsEnabled告知應用是否開啟了通知許可權。

Android通知欄踩坑與填坑指南


魅族5.X手機,大圖顯示問題

問題詳情

Flyme系統對原生Android原始碼做了修改,採用BigPictureStyle方式顯示大圖通知欄的時候,訊息與大圖重合了,如下圖。

20180828093844dee3d5e5-439d-4d75-aae8-ef2f9a20105f.jpg


解決方案

首先說一下為什麼會有解決方案。展示大圖這個功能開發完成後,拿去給產品演示。碰巧產品的機型就是一魅族手機T_T,結果當然是不能接受的,然後又一個巧合的事情出現了,那就是產品的手機裡,京東App推了一條帶大圖的廣告,他們居然能夠解決這個問題!於是,我開始研究解決方案。


首先,通過BigPictureStyle來實現大圖功能肯定是走不通的,因為事實就擺著行不通的嘛。京東的App肯定是通過RemoteViews來實現的。於是,開始走彎路,嘗試通過RemoteViews來展示大圖。但是谷歌規定,自定義佈局展示的通知欄訊息最大高度是64dp。那麼,京東的App是怎麼實現的?在嘗試了各種方法以後,最後又是通過投機取巧的方式解決了問題:

private void showBigPictureNotificationWithMZ(Context context) {
    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    Notification.Builder builder = new Notification.Builder(context);
    Notification notification = generateNotification(builder);
    notification.bigContentView = mRemoteViews;
    notificationManager.notify(notifyId, notification);
}

需要先生成Notification的例項,然後手動給notification.bigContentView賦值,再notify,就可以了


頂部狀態列(StatusBar)小圖示顯示異常

問題詳情

當通知來的時候,如果不在通知欄瀏覽,會在頂部狀態列出現一個向上翻滾動畫的通知訊息,這條通知訊息左邊是一個小圖示。部分系統這個小圖示顯示異常,是一個純灰色的正方形,如下圖。

20180828093901b939eff5-555f-43c6-adcd-b0e925892ca3.png



解決方案

首先產生灰色圖示的原因就是5.0系統引入了材料設計,谷歌強制使用帶有alpha通道的圖示,並且RGB的alpha值必須是0(實測不為0也是可以的,但系統會忽略所有RGB值)。因此,使用JPG的圖片是不行的,最好的代替方案就是一張背景透明的PNG圖片。


Android 7.X機型,通知欄小圖示顯示成灰色

問題詳情

這個問題跟第二個有點類似,在7.0系統及以上,有部分應用的小圖示是灰色的,大圖可以正常顯示。碰巧的是,顯示異常的小圖示,顏色都是灰色的。

2018082809400925217427-3cb7-4f0f-9b36-eb437841c8d1.png




解決方案

小圖示顯示異常解決方案類似,將小圖示替換為透明背景的PNG圖片。


RemoteViews顯示異常

問題詳情

由於系統提供的通知欄訊息型別有時候不能滿足要求,部分通知欄訊息採用自定義RemoteViews來實現。採用RemoteViews,特別是手動生成Bitmap然後直接傳給一個自定義Layout,再通過setContentView方式設定通知欄訊息時,會存在各種各樣的坑。


Android通知欄的背景色有幾種情況,白色、暗色、暗色透明和黑色。如果生成的Bitmap帶背景色,這個背景色就很難選擇。如果選擇黑色背景,那麼在白色通知欄的機型上就很難看。因此不能完全在各個系統上面完美展示出來。如果不帶背景色,那麼字型顏色也面臨同樣的困惑。試想,如果在白色的背景上顯示白色的文字,使用者看到白茫茫一片,是什麼感受?

20180828094025084a632c-d400-4c8f-b56e-183e00755d3d.png

另一方面,大部分廠商對原生的Android系統都會有各種各樣的改造,通知欄的樣式也不例外。如果按照原生的樣式來設計,那麼在大部分國內廠商的機子上顯示都和正常的普通通知欄訊息不一樣。例如華為6.0系統的機子,原生系統的時間線在右上角,華為的在左邊,這樣會給使用者帶來錯覺。

20180828094043583e7a78-4376-4d2b-b9d6-1c0a5c17b0ec.jpg



解決方案

詳見RemoteViews適配一節。


大尺寸小圖示在部分機型上顯示不正確

問題詳情

這個問題主要在部分機型的4.X系統上遇見,小圖示大小沒有按照24dp裁剪,而是採用了桌面圖示一樣的大小96dp。具體適配不正常的機型有HTC Desire 820、Lenovo A320T。

201808280940570adb8bf3-6533-487a-bee3-5ed8e8163fb5.jpg


解決方案

按照標準來,小圖示大小為24dp,大圖示為桌面icon圖示大小96dp。具體可參考這裡^14


部分機型不支援Style

具體機型見下圖以及後面統計的表格。順便提下,小米是其中之一,不知道他們為什麼不支援額外的這些Style。

20180828094115c03c3aac-d17c-4627-8da4-7e9a02797627.jpg

點選檢視大圖


通知欄更新頻率

問題詳情

每個應用基本都有自更新的邏輯,App開機的時候提示使用者升級,點選升級按鈕後在Notification出現一個下載帶進度條的通知。應用一般是在開啟一個工作執行緒在後臺下載,然後在下載的過程中通過回撥更新通知欄中的進度條。我們知道,下載進度的快慢是不可控的,如果每次下載中的回撥都去更新通知欄,那麼可能幾百毫秒、幾十毫秒、甚至幾毫秒就更新一次通知欄,應用可能就會ANR,甚至崩潰。


解決方案

控制通知欄更新頻率,一般控制在0.5s或者1s就可以了。在某一個更新時間間隔內下載的進度回撥直接丟棄,需要注意的是下載完成的回撥,需要實時回撥通知欄訊息顯示下載完成。


噁心的後臺通知和“守護”通知

問題詳情

這個坑我不願多介紹,只說結果。但凡存在後臺通知或者“守護”通知的應用,在7.0系統以後都會原形畢露。還沒有適配7.0的應用,可長點心兒吧~

201808280941339bf64522-36bb-408d-abf2-0a3028eeb686.png201808280941332ea5f8a6-8da2-4ca5-b332-1b6bcd827f29.png


解決方案

請棄坑。


小米推送SDK接入問題

問題詳情

為了提升推送到達,考拉接入了小米推送的SDK。小米推送分為通知欄訊息和透傳訊息,通知欄訊息屬於系統級推送,在MIUI的機子上可以在程序被殺死的情況下也能收到應用推送。然而有個問題,小米認為應用在前臺時,不會回撥任何方法;小米認為應用在後臺的時候,收到通知欄訊息的同時,會回撥onNotificationMessageArrived方法。這時候就要小心翼翼地處理這條訊息了。因為如果你的應用前後臺判斷邏輯和小米的不一樣,那麼就有可能小米幫你發了一條通知欄訊息,你自己又發了一遍,造成通知欄訊息的重複傳送(這個坑考拉踩過T_T)。另一方面,在7.0系統的機子上,主標題和小圖示的顏色是可以改變的,目前小米推送SDK沒有開放這個介面供呼叫方定製。


解決方案

目前只能解決第一個問題——前後臺判斷的問題。應用是否在後臺可以根據以下程式碼進行判斷。在Android 5.0以上,可以通過ActivityManager.RunningAppProcessInfo判斷,Android 5.0及以下版本通過ActivityManager.RunningTaskInfo判斷。經測試,這個方案在Android 4.4以上結果是可以完全匹配的。


public static boolean isAppInBackgroundInternal(Context context) {
    ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
        List<ActivityManager.RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();
        if (!ListUtils.isEmpty(runningProcesses)) {
            for (ActivityManager.RunningAppProcessInfo runningProcess : runningProcesses) {
                if (runningProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                    return false;
                }
            }
        }
    } else {
        List<ActivityManager.RunningTaskInfo> task = manager.getRunningTasks(1);
        if (!ListUtils.isEmpty(task)) {
            ComponentName info = task.get(0).topActivity;
            if (null != info) {
                return !isKaolaProcess(info.getPackageName());
            }
        }
    }
    return true;
}

Android通知欄適配

RemoteViews適配

由於系統自帶的通知欄訊息樣式不能完全滿足產品們腦洞大開的需求,有時候我們需要自定義佈局樣式展示通知欄訊息。Android系統可以將自定義佈局通過setContent(7.X系統推薦使用setCustomContentView)設定到Notification.Builder中,來實現樣式的更變。setContent方法需要傳入一個RemoteViews物件,它是一個普通的資料型別,不是View,作用是供其他程序展示檢視。RemoteViews只支援4種基本的佈局^9:

  • FrameLayout

  • LinearLayout

  • RelativeLayout

  • GridLayout


這些佈局下面只支援幾種檢視控制元件:

  • AnalogClock

  • Button

  • Chronometer

  • ImageButton

  • ImageView

  • ProgressBar

  • TextView

  • ViewFlipper

  • ListView

  • GridView

  • StackView

  • AdapterViewFlipper


只能通過上述組合生成一個RemoteViews。


自定義佈局與檢視

除了上面提到的佈局與控制元件,有沒有辦法自定義佈局與檢視呢?我們知道,任何一個View,都可以生成一個Bitmap物件,支援的檢視控制元件裡有ImageView,可以通過ImageView.setBitmapResource()將自定義檢視設定到一個ImageView中,然後再隨便放到一個佈局上,就可以實現通知欄訊息的任意佈局。理想是美好的,但現實是殘酷的。使用這種方式自定義的佈局,會存在與原生的通知欄訊息樣式不一致的可能,包括小圖示/大圖示的大小,字型的大小與顏色,時間的顯示方式(不同版本的時間顯示位置和樣式都不一樣)。下面解決一個最關鍵,也最致命的問題——字型顏色。如果字型顏色和背景顏色一樣,那這條通知欄訊息就沒法看了,如RemoteViews顯示異常一節介紹的一樣。


解決字型顏色和背景顏色一樣的問題有三種解決方案,分別是:

  • 背景色固定不透明,字型顏色與背景色形成反差。(360和京東的做法)

  • 背景色透明,字型顏色採用系統原生的notification_style。

  • 背景色透明,通過特殊方式拿到通知欄字型顏色和字型大小。201808280941554524c5d4-e0cd-4658-942c-c3529898a301.png




其中,第一種方案簡單,能夠相容所有廠商機型。例如京東固定背景色為黑色,字型為紅色。這種方式的唯一缺陷是樣式上不能與普通通知欄訊息重合,在白色背景的通知欄上極為顯眼。第二種方式,通過閱讀原始碼可知,系統的通知欄標題和內容採用的顏色分別是@android:color/primary_text_dark和@android:color/secondary_text_dark,但踩過坑之後發現並非所有的機型預設都是這兩個顏色,有可能獲取不到值。因此這種方案只能作為參考,不能用於實際環境中。最後詳細介紹一下第三種方式。


原文:Android通知欄介紹與適配總結(上篇)

相關閱讀:Android通知欄介紹與適配總結(下篇)


免費領取驗證碼、內容安全、簡訊傳送、直播點播體驗包及雲伺服器等套餐

更多網易技術、產品、運營經驗分享請點選



相關文章:
【推薦】 分散式儲存系統可靠性系列五:副本放置演算法 & CopySet Replication
【推薦】 在Docker中安裝和部署MongoDB叢集
【推薦】 《深入理解計算機系統》之淺析程式效能優化