1. 程式人生 > >Android 通知中心

Android 通知中心

現在看來習慣性的檢視我們的裝置上的資訊幾乎成了我們的另一種本能。幾乎每小時我們都會拿出我們的手機,看看狀態列有沒新的訊息,然後放回我們的口袋。尤其對 Android 使用者來說更是如此,因為這是他們與裝置之間的主要互動方式之一。 解鎖螢幕,讀封郵件,接受好友請求,為你的好友的簽到點個贊,隨便訪問幾個不同的應用,所有這些操作都可以通過通知欄完成。

對一些人來說則是另一個完全不同的世界。尤其是相對於 iOS 在歷史上曾一度無法獲取通知,而現在 iOS 開發者也無法像 Android 一樣細粒度的定製他們的應用通知。甚至在之前都無法接收靜默通知,雖然這些在 iOS 7 上得到了改善,但一經細嚼仍然相當糟糕,很多 Android 開發者玩轉多年的關鍵特性在 iOS 系統中仍然是空白。

Android 從最開始就可以接收通知這一點已經被吹捧了很長一段時間。所有的通知都集中在系統欄電量和訊號圖示的旁邊,但是要想了解 Android 通知系統為何可以做到這些,究其根源,我們需要了解 Android 系統的演變。

因為 Android 允許開發者們自由控制他們的後臺程序,他們可以在任何時候以任何理由建立並顯示通知。它從來沒有傳遞通知給應用程式或狀態列的概念。它被送到任何你想要它去的地方。

你可以隨時隨地訪問通知。由於大多數應用沒有強迫去實現一個全屏的設計,使用者在他們需要的時候可以下拉通知‘抽屜’。對多數人來說,Android 是他們的第一個智慧手機,它改變了人們過往檢視通知的慣例,過去你需要開啟一個個單獨的應用去檢視你是否錯過了電話,簡訊或者郵件。

Android 1.6 中的通知 (Donut):

Notifications in Android 1.6

Android 4.4 的通知 (KitKat):

Notifications in Android 4.4

簡史

從 Android 在2008年登上舞臺開始,通知系統走過了漫長的道路。

Android 1.5 - 2.3

這是對大多數人來說的 Android 的開始(包括我)。我們有一些可以定製的功能,比如應用圖示,標題,描述以及時間。如果你需要加入自定義的控制元件,比如,一個音樂播放器當然也可以。系統會維護所需的寬高限制,而你則可以加入你想要的檢視。在通知中使用自定義的佈局是當時大多數音樂播放器實現自定義控制元件的方式:

private void showNotification() {
  // 建立基本通知(the R.drawable 參考自 png 圖片)
  Notification notification = new Notification(R.drawable.stat_notify_missed_call,
      "Ticket text", System.currentTimeMillis());

  // 建立 Intent
  Intent intent = new Intent(this, Main.class);

  // 讓 intent 等待直到他準備好。
  PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);

  // 設定最後的事件資訊
  notification.setLatestEventInfo(this, "Content title", "Content subtext", pi);

  // 獲取通知 manager 的例項
  NotificationManager noteManager = (NotificationManager)
      getSystemService(Context.NOTIFICATION_SERVICE);

  // 釋出到系統欄
  noteManager.notify(1, notification);
}

程式碼:這段是如何在1.5-2.3中實現通知功能。

Android 1.6 中的執行的結果:

Notifications in Donut 1.6

Android 2.3 中的執行結果:

Notifications in Gingerbread 2.3

Android 3.0 - 3.2

通知系統在 Android 3.0 上實際有一絲退步, Android 平板,一個用來對抗 iPad 的版本,是 Android 在大螢幕執行的一次嚐鮮。相對於單一的抽屜顯示,Android 嘗試用額外的控制元件帶來新的通知體驗,你依舊有一個抽屜型別的通知,同時你也可以接收像 growl 那樣的通知。幸運的是,與此同時 Android 提供了一個叫做 NotificationBuilder 的全新 API,允許我們利用建造者模式 去構建我們的通知。儘管略微複雜,但構造器會根據每個新版作業系統的不同來構建複雜的通知物件:

// 建立 Intent 例項
Intent intent = new Intent(this, Main.class);

// 在準備好使用之前,持有intent
PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);

Notification noti = new Notification.Builder(getContext())
  .setContentTitle("Honeycomb")
  .setContentText("Notifications in Honeycomb")
  .setTicker("Ticker text")
  .setSmallIcon(R.drawable.stat_notify_missed_call)
  .setContentIntent(pi)
  .build();

// 獲取通知 manager 的例項
NotificationManager noteManager = (NotificationManager)
    getSystemService(Context.NOTIFICATION_SERVICE);

// 釋出到系統欄
noteManager.notify(1, notification);

通知在 Android 3.2(蜂巢)接後的初始狀態:

Honeycomb notifications ticket text

當你在導航欄點選它時候的樣式:

Honeycomb notifications tapping notification

當你點選時鐘看到的通知的樣式:

Honeycomb notifications tapping clock

各種冗餘的通知讓使用者感到困惑不知道它們代表什麼,這就對開發人員提出設計挑戰,如何在恰當的時間返回恰當的資訊給使用者。

最終, 4.0-4.4

與其他系統相比,Android 從 4.0 之後真正充實和統一了通知體驗。雖然在 4.0 沒有帶來任何激動人心的設計,但是 4.1 帶來一種聚合的通知(一種全新的視覺化,讓一個 cell 中可以顯示多個通知),可擴充套件的通知(比如,顯示電子郵件的第一段),圖片通知,以及可操作的通知。不用說,這提供了一種全新的方式可以帶給使用者 out-of-app 的體驗。如果有人在 Facebook 加我為好友,我可以簡單的在通知欄上點選“接受”,再也不用開啟 Facebook 應用。如果我收到了一封垃圾郵件,我可以直接歸檔而不用再次檢視。

這裡有一些 Tumblr 應用利用了新的 4.0+ API 的例子,使用它們構建通知出人意料的簡單;只需要你加入一些額外的通知風格到 NotificationBuilder 中就可以了。

大文字通知

如果文字足夠短,還有什麼理由讓我開啟應用來閱讀?大文字樣式提供了更大的閱讀空間來解決這個問題。再也不需要浪費時間開啟一個應用

Notification noti = new Notification.Builder()
  ... // 和之前一樣的通知屬性設定
  .setStyle(new Notification.BigTextStyle().bigText("theblogofinfinite replied..."))
  .build();

大文字通知摺疊:

Notifications in Cupcake 1.5

大文字通知展開:

Notifications in Cupcake 1.5

大圖片通知

大圖片通知提供了不需要開啟應用就能夠享受到內容優先的美妙體驗。不僅可以提供大量的內容,也是一種優雅的互動方式。

Notification noti = new Notification.Builder()
  ... // 同之前一樣的屬性設定方法
  .setStyle(new Notification.BigPictureStyle().bigPicture(mBitmap))
  .build();

Big picture notification

聚合 (Roll-up) 通知

聚合通知是將多個通知放在一起,聚合有一點欺騙性因為它實際上並不堆疊現有的通知,你依然可以自己創造他們,所以這真的是一種很好展示通知的方式:

Notification noti = new Notification.Builder()
  ... // The same notification properties as the others 和之前一樣的屬性設定
  .setStyle(new Notification.InboxStyle()
     .addLine("Soandso likes your post")
     .addLine("Soandso reblogged your post")
     .setContentTitle("3 new notes")
     .setSummaryText("+3 more"))
  .build();

Rollup notification

可操作通知

在通知中增加操作就和你想象的一樣容易。建造者模式可以確保它能夠使用任何系統預設的樣式,使使用者總是感覺在使用通知抽屜:

Notification noti = new Notification.Builder()
  ... // The same notification properties as the others
  .addAction(R.drawable.ic_person, "Visit blog", mPendingBlogIntent)
  .addAction(R.drawable.ic_follow, "Follow", mPendingFollowIntent)
  .build();

Action notification

這類互動是一種對使用者負責的設計,並且操作簡單快速,受限於 Android 的緩慢效能,這種快捷操作的方式非常受歡迎,因為你實際上不需要開啟應用就可以使用它。

Android 穿戴裝置

現在的科技圈對於任何一個人來說都已經不再像之前那麼神祕,正因如此 Android 可穿戴裝置也成為了科技裝置中的一份子。它是否能夠成功的成為一類消費品這件事情似乎仍然有待商榷,但是對於那些想要支援 Android 穿戴裝置的開發者來說,仍然有很多不容忽視存在著的障礙。沒有辜負 Android 系統傳承下來的一些優勢,其穿戴裝置在與你的裝置進行同步的時候似乎總可以接受正確的通知。但事實上,你的手機與 Android 穿戴裝置連線後,它將會在沒有程式碼修改的情況下對裝置推送構造器建立的通知。能夠簡單使用建造者模式則意味著無論出現什麼裝置,只要它們能夠支援 Android 系統和 Android 可穿戴裝置,立即會有大量熟練使用 API 來收發資料的應用開發者出現。

Action notificationAction notification

NotificationBuilder 提供了 out-of-the-box 的 android 穿戴裝置支援,不用寫任何額外的程式碼!

自定義通知

雖然 Android 的 NotificationBuilder 支援高自由度定製,但有的時候依然無法滿足人們的需求,這就是為何要引入自定義通知佈局。很難想象當你擁有全部的通知系統的控制權限的時候。你將如何改變它,讓它與眾不同? 在諸多約束的情況下不斷創新著實很難,但是許多 Android 開發者已經開始迎難而上。

自定義音樂播放器通知:

Custom music player notification

自定義天氣通知:

Custom weather notification

自定義電量通知:

Custom battery notification

自定義通知僅限於檢視元件所支援遠端檢視的一個子集,這些檢視元件本身不能高度延伸或者被覆蓋。雖然只能輕度定製,但是你依然可以利用基本元件構造複雜的通知。

然而建立這些自定義檢視可能需要更多的工作。使用 Android 的 XML 建立自定義通知檢視佈局系統,你要確保在不同 Android 版本看起來依舊良好。這非常痛苦,但是當你看看這些美麗的通知,你會覺得一切又那麼有價值:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:layout_gravity="center_vertical" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:text="You received a notification" />

</LinearLayout>

這是一個非常基本的自定義通知佈局,包含一張圖片以及一段文字。

通知的行為

推送通知

現在我們已經瞭解了大量歷史,接下來讓我們看一些關於通知是如何工作的有趣行為。也許這部分內容我們已經提及,開發者可以 完全控制通知系統。就是說通知可以因為任何原因在任何時候顯示或者消失。甚至不需要從 Google 推送服務接收一個通知。實際上,就算我們收到一個推送通知,它預設也不會顯示在狀態列,你需要自己去捕捉推送通知並且決定如何去處理它。

舉例來說,一個常規的通知互動應該是這個樣子:

  1. 接收遠端推送通知
  2. 檢查 payload,根據 payload 建立一個後臺服務去獲取資料。
  3. 獲取並解析返回的資料
  4. 構建並顯示通知

比較有趣的是第二步和第三步,後臺服務沒有時間限制。如果推送通知告訴你要下載 1GB 的檔案,好吧無所謂!在大多數情況下,如果一個後臺程序用時非常短的話,系統並沒有要求你必須顯示一個通知。但長時間後臺服務(比如音樂播放器)依舊需要在狀態列顯示一個圖示。對於開發者來說這是一種值得深思的做法,確保使用者知道是何種服務在後臺長時間執行。

雖然只有 4 個步驟,但是有一多半的開發者不願意處理。如果我可以直接傳送整個 payload 是不是會更簡潔?GCM (Google Cloud Messaging) 允許 payloads 控制在 4KB 之內,平均來說,它在 1,024 到 4,096 UTF-8 字元之間(取決於字元)。除非你要推送一張圖片,不然你可以塞入任何你想要的內容。聽起來多棒!

通知回撥

那麼開發人員是如何控制使用者與通知之間的互動?當然,我們已經知道可以新增自定義控制元件和按鈕,而且我們已經看到了如何實現一般的點選。但還有其他的麼?實際上,當然有!有一個“刪除”功能,當用戶設定 setDeleteIntent,使用者從抽屜中刪除通知的時候將被邏輯刪除。加入刪除是一個偉大的進步,以確保我們不再次顯示老舊資訊:

// 在 Android 中,我們可以建立任意名字讓元件決定它們想要處理哪種操作    
Intent clearIntent = new Intent("clear_all_notifications");
PendingIntent clearNotesFromDb = PendingIntent.getBroadcast(aContext, 1, clearIntent, 0)

Notification noti = new Notification.Builder(getContext())
  ...
  .setDeleteIntent(clearNotesFromDb)
  .build();

重建導航層次結構

讓我們更深入地談一談通知的預設點選。現在你當然可以在點選通知後執行一些預設的行為。你可以僅僅開啟那你的應用。使用者可以自己找到他們想要去的頁面。但是如果我們可以直接顯示相關的頁面那麼體驗會更加友好。如果我們收到一個郵件通知,我們直接跳轉到郵件內容,如果我們的一個朋友在 Foursquare 上籤到,我們直接可以開啟應用顯示這個他或者她所在的餐館。允許通知指向到一個包含內容的深度連結是一個很棒的功能。但是更多時候,深度連結已經是你應用的一部分,你會遇到導航層次混亂的問題。你無法使用導航’返回‘。 Android 可以幫助你在開始之前建立檢視堆疊來解決這個問題。這是通過 TaskStackBuilder 類來完成的。使用這個技術需要一些魔法並且需要一些應用架構方面的知識,但是有空你可以看看 Google 開發者網站,這裡有一個簡要實現.

用 Gmail 舉例,我們告訴應用 "開啟郵件應用,接著開啟一份指定的郵件。" 來代替簡單的開啟郵件應用。使用者將不會看到所有的檢視建立,而只會看到最終的結果。這是多麼的不可思議,因為現在我們點選返回按鈕的時候,使用者將不會退出應用,而只會退到應用的首頁。

少了些了什麼

我已經詳細羅列了許多 Android 系統中通知的功能,同時也展示了它們有多麼的強大。但是沒有哪個系統是完美的,Android 的通知系統也有瑕疵。

標準

Android 使用者面臨的首要問題之一則是沒有控制通知系統的功能。這就意味著如果一個應用有通知提示的時候,使用者沒法關閉它,唯有解除安裝這個應用。從 Android 4.1 開始,使用者可以在設定中選擇關掉指定程式的通知。這阻止了應用在狀態列的所有通知,這看起來是十分有用的功能,但是在使用者實際使用中卻受限很大,因為其實很少有人想要將所有的應用通知都關閉,他們很多時候只想關掉一些令人惱火的通知,比如 LED 提示燈閃爍或者是不停發出的提示音這類通知。

從 Android 4.1 開始,使用者可以通過設定來關閉接收通知,但是這裡沒有一種方式來關閉 LEDs 或者聲音,除非開發者顯式地提供了關閉的方法。

顯示什麼

你也許認為我們已經將通知功能基本掌握了,但其實我們仍然有很大的進步空間。儘管現有的系統已經具備了一定的個性化定製功能,但我仍然希望看到它能夠更上一層樓。正如早些時候我們看到的,NotificationBuilder 使你的通知形成一個特定的樣子,也就是所有的系統通知都是一樣的。如果你使用自定義佈局並且建立自己的通知模式,也只有一小部分支援的元件來供你使用。如果有很複雜的元件需要定製,那出於安全考慮你的想法很有可能無法實現。如果你想做一些更高階的功能,比如幀動畫,或者一個視訊,請還是算了吧。

結論

Android 提供了給使用者和開發者不少通知方面的功能。從一開始,Android 有意識的朝著更大膽的方向努力,即使從今天看來它依舊無以倫比。看看前進中的 Android 以及 Android 穿戴裝置,很容易發現,通知管理 API 重點強調方便使用。雖然在細粒度的通知管理缺乏完整的 UI 控制元件方面有一些缺點,但是謹慎的說,如果你正在尋找一個通知優先的生態系統,Android 或許值得一試。

參考