鍍金池/ 教程/ iOS/ Android 通知中心
與四軸無人機的通訊
在沙盒中編寫腳本
結構體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學
NSString 與 Unicode
代碼簽名探析
測試
架構
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應用數(shù)據(jù)腳本化
一個完整的 Core Data 應用
插件
字符串
為 iOS 建立 Travis CI
先進的自動布局工具箱
動畫
為 iOS 7 重新設計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡應用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應式 Android 應用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調試
項目介紹
Swift 的強大之處
測試并發(fā)程序
Android 通知中心
調試:案例學習
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學習的一代人
視頻
Playground 快速原型制作
Omni 內部
同步數(shù)據(jù)
設計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉場
照片框架
響應式視圖
Square Register 中的擴張
DTrace
基礎集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設計的藝術
導航應用
線程安全類的設計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗證
數(shù)據(jù)同步
自定義 ViewController 容器轉場
游戲
調試核對清單
View Controller 容器
學無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術:Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴展
理解 Scroll Views
使用 VIPER 構建 iOS 應用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機捕捉
語言標簽
同步案例學習
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉字符串
相機工作原理
Build 過程

Android 通知中心

現(xiàn)在看來習慣性的查看我們的設備上的信息幾乎成了我們的另一種本能。幾乎每小時我們都會拿出我們的手機,看看狀態(tài)欄有沒新的消息,然后放回我們的口袋。尤其對 Android 用戶來說更是如此,因為這是他們與設備之間的主要交互方式之一。 解鎖屏幕,讀封郵件,接受好友請求,為你的好友的簽到點個贊,隨便訪問幾個不同的應用,所有這些操作都可以通過通知欄完成。

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

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

因為 Android 允許開發(fā)者們自由控制他們的后臺進程,他們可以在任何時候以任何理由創(chuàng)建并顯示通知。它從來沒有傳遞通知給應用程序或狀態(tài)欄的概念。它被送到任何你想要它去的地方。

你可以隨時隨地訪問通知。由于大多數(shù)應用沒有強迫去實現(xiàn)一個全屏的設計,用戶在他們需要的時候可以下拉通知‘抽屜’。對多數(shù)人來說,Android 是他們的第一個智能手機,它改變了人們過往查看通知的慣例,過去你需要打開一個個單獨的應用去查看你是否錯過了電話,短信或者郵件。

Android 1.6 中的通知 (Donut):

http://wiki.jikexueyuan.com/project/objc/images/11-15.jpg" alt="" />

Android 4.4 的通知 (KitKat):

http://wiki.jikexueyuan.com/project/objc/images/11-16.png" alt="" />

簡史

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

Android 1.5 - 2.3

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

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

  // 創(chuàng)建 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);

  // 發(fā)布到系統(tǒng)欄
  noteManager.notify(1, notification);
}

代碼:這段是如何在1.5-2.3中實現(xiàn)通知功能。

Android 1.6 中的運行的結果:

http://wiki.jikexueyuan.com/project/objc/images/11-17.png" alt="" />

Android 2.3 中的運行結果:

http://wiki.jikexueyuan.com/project/objc/images/11-18.png" alt="" />

Android 3.0 - 3.2

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

// 創(chuàng)建 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);

// 發(fā)布到系統(tǒng)欄
noteManager.notify(1, notification);

通知在 Android 3.2(蜂巢)接后的初始狀態(tài):

http://wiki.jikexueyuan.com/project/objc/images/11-19.png" alt="" />

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

http://wiki.jikexueyuan.com/project/objc/images/11-20.png" alt="" />

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

http://wiki.jikexueyuan.com/project/objc/images/11-21.png" alt="" />

各種冗余的通知讓用戶感到困惑不知道它們代表什么,這就對開發(fā)人員提出設計挑戰(zhàn),如何在恰當?shù)臅r間返回恰當?shù)男畔⒔o用戶。

最終, 4.0-4.4

與其他系統(tǒng)相比,Android 從 4.0 之后真正充實和統(tǒng)一了通知體驗。雖然在 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();

大文本通知折疊:

http://wiki.jikexueyuan.com/project/objc/images/11-22.png" alt="" />

大文本通知展開:

http://wiki.jikexueyuan.com/project/objc/images/11-23.png" alt="" />

大圖片通知

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

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

http://wiki.jikexueyuan.com/project/objc/images/11-24.png" alt="" />

聚合 (Roll-up) 通知

聚合通知是將多個通知放在一起,聚合有一點欺騙性因為它實際上并不堆?,F(xiàn)有的通知,你依然可以自己創(chuàng)造他們,所以這真的是一種很好展示通知的方式:

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();

http://wiki.jikexueyuan.com/project/objc/images/11-25.png" alt="" />

可操作通知

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

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();

http://wiki.jikexueyuan.com/project/objc/images/11-26.png" alt="" />

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

Android 穿戴設備

現(xiàn)在的科技圈對于任何一個人來說都已經(jīng)不再像之前那么神秘,正因如此 Android 可穿戴設備也成為了科技設備中的一份子。它是否能夠成功的成為一類消費品這件事情似乎仍然有待商榷,但是對于那些想要支持 Android 穿戴設備的開發(fā)者來說,仍然有很多不容忽視存在著的障礙。沒有辜負 Android 系統(tǒng)傳承下來的一些優(yōu)勢,其穿戴設備在與你的設備進行同步的時候似乎總可以接受正確的通知。但事實上,你的手機與 Android 穿戴設備連接后,它將會在沒有代碼修改的情況下對設備推送構造器創(chuàng)建的通知。能夠簡單使用建造者模式則意味著無論出現(xiàn)什么設備,只要它們能夠支持 Android 系統(tǒng)和 Android 可穿戴設備,立即會有大量熟練使用 API 來收發(fā)數(shù)據(jù)的應用開發(fā)者出現(xiàn)。

http://wiki.jikexueyuan.com/project/objc/images/11-27.png" alt="" />

http://wiki.jikexueyuan.com/project/objc/images/11-28.png" alt="" />

NotificationBuilder 提供了 out-of-the-box 的 android 穿戴設備支持,不用寫任何額外的代碼!

自定義通知

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

自定義音樂播放器通知:

http://wiki.jikexueyuan.com/project/objc/images/11-29.png" alt="" />

自定義天氣通知:

http://wiki.jikexueyuan.com/project/objc/images/11-30.png" alt="" />

自定義電量通知:

http://wiki.jikexueyuan.com/project/objc/images/11-31.png" alt="" />

自定義通知僅限于視圖組件所支持遠程視圖的一個子集,這些視圖組件本身不能高度延伸或者被覆蓋。雖然只能輕度定制,但是你依然可以利用基本組件構造復雜的通知。

然而創(chuàng)建這些自定義視圖可能需要更多的工作。使用 Android 的 XML 創(chuàng)建自定義通知視圖布局系統(tǒng),你要確保在不同 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>

這是一個非?;镜淖远x通知布局,包含一張圖片以及一段文字。

通知的行為

推送通知

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

舉例來說,一個常規(guī)的通知交互應該是這個樣子:

  1. 接收遠程推送通知
  2. 檢查 payload,根據(jù) payload 建立一個后臺服務去獲取數(shù)據(jù)。
  3. 獲取并解析返回的數(shù)據(jù)
  4. 構建并顯示通知

比較有趣的是第二步和第三步,后臺服務沒有時間限制。如果推送通知告訴你要下載 1GB 的文件,好吧無所謂!在大多數(shù)情況下,如果一個后臺進程用時非常短的話,系統(tǒng)并沒有要求你必須顯示一個通知。但長時間后臺服務(比如音樂播放器)依舊需要在狀態(tài)欄顯示一個圖標。對于開發(fā)者來說這是一種值得深思的做法,確保用戶知道是何種服務在后臺長時間運行。

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

通知回調

那么開發(fā)人員是如何控制用戶與通知之間的互動?當然,我們已經(jīng)知道可以添加自定義控件和按鈕,而且我們已經(jīng)看到了如何實現(xiàn)一般的點擊。但還有其他的么?實際上,當然有!有一個“刪除”功能,當用戶設置 setDeleteIntent,用戶從抽屜中刪除通知的時候將被邏輯刪除。加入刪除是一個偉大的進步,以確保我們不再次顯示老舊信息:

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

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

重建導航層次結構

讓我們更深入地談一談通知的默認點擊?,F(xiàn)在你當然可以在點擊通知后執(zhí)行一些默認的行為。你可以僅僅打開那你的應用。用戶可以自己找到他們想要去的頁面。但是如果我們可以直接顯示相關的頁面那么體驗會更加友好。如果我們收到一個郵件通知,我們直接跳轉到郵件內容,如果我們的一個朋友在 Foursquare 上簽到,我們直接可以打開應用顯示這個他或者她所在的餐館。允許通知指向到一個包含內容的深度鏈接是一個很棒的功能。但是更多時候,深度鏈接已經(jīng)是你應用的一部分,你會遇到導航層次混亂的問題。你無法使用導航’返回‘。 Android 可以幫助你在開始之前創(chuàng)建視圖堆棧來解決這個問題。這是通過 TaskStackBuilder 類來完成的。使用這個技術需要一些魔法并且需要一些應用架構方面的知識,但是有空你可以看看 Google 開發(fā)者網(wǎng)站,這里有一個簡要實現(xiàn).

用 Gmail 舉例,我們告訴應用 "打開郵件應用,接著打開一份指定的郵件。" 來代替簡單的打開郵件應用。用戶將不會看到所有的視圖創(chuàng)建,而只會看到最終的結果。這是多么的不可思議,因為現(xiàn)在我們點擊返回按鈕的時候,用戶將不會退出應用,而只會退到應用的首頁。

少了些了什么

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

標準

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

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

顯示什么

你也許認為我們已經(jīng)將通知功能基本掌握了,但其實我們仍然有很大的進步空間。盡管現(xiàn)有的系統(tǒng)已經(jīng)具備了一定的個性化定制功能,但我仍然希望看到它能夠更上一層樓。正如早些時候我們看到的,NotificationBuilder 使你的通知形成一個特定的樣子,也就是所有的系統(tǒng)通知都是一樣的。如果你使用自定義布局并且建立自己的通知模式,也只有一小部分支持的組件來供你使用。如果有很復雜的組件需要定制,那出于安全考慮你的想法很有可能無法實現(xiàn)。如果你想做一些更高級的功能,比如幀動畫,或者一個視頻,請還是算了吧。

結論

Android 提供了給用戶和開發(fā)者不少通知方面的功能。從一開始,Android 有意識的朝著更大膽的方向努力,即使從今天看來它依舊無以倫比??纯辞斑M中的 Android 以及 Android 穿戴設備,很容易發(fā)現(xiàn),通知管理 API 重點強調方便使用。雖然在細粒度的通知管理缺乏完整的 UI 控件方面有一些缺點,但是謹慎的說,如果你正在尋找一個通知優(yōu)先的生態(tài)系統(tǒng),Android 或許值得一試。

參考