現(xiàn)在看來(lái)習(xí)慣性的查看我們的設(shè)備上的信息幾乎成了我們的另一種本能。幾乎每小時(shí)我們都會(huì)拿出我們的手機(jī),看看狀態(tài)欄有沒新的消息,然后放回我們的口袋。尤其對(duì) Android 用戶來(lái)說(shuō)更是如此,因?yàn)檫@是他們與設(shè)備之間的主要交互方式之一。 解鎖屏幕,讀封郵件,接受好友請(qǐng)求,為你的好友的簽到點(diǎn)個(gè)贊,隨便訪問(wèn)幾個(gè)不同的應(yīng)用,所有這些操作都可以通過(guò)通知欄完成。
對(duì)一些人來(lái)說(shuō)則是另一個(gè)完全不同的世界。尤其是相對(duì)于 iOS 在歷史上曾一度無(wú)法獲取通知,而現(xiàn)在 iOS 開發(fā)者也無(wú)法像 Android 一樣細(xì)粒度的定制他們的應(yīng)用通知。甚至在之前都無(wú)法接收靜默通知,雖然這些在 iOS 7 上得到了改善,但一經(jīng)細(xì)嚼仍然相當(dāng)糟糕,很多 Android 開發(fā)者玩轉(zhuǎn)多年的關(guān)鍵特性在 iOS 系統(tǒng)中仍然是空白。
Android 從最開始就可以接收通知這一點(diǎn)已經(jīng)被吹捧了很長(zhǎng)一段時(shí)間。所有的通知都集中在系統(tǒng)欄電量和信號(hào)圖標(biāo)的旁邊,但是要想了解 Android 通知系統(tǒng)為何可以做到這些,究其根源,我們需要了解 Android 系統(tǒng)的演變。
因?yàn)?Android 允許開發(fā)者們自由控制他們的后臺(tái)進(jìn)程,他們可以在任何時(shí)候以任何理由創(chuàng)建并顯示通知。它從來(lái)沒有傳遞通知給應(yīng)用程序或狀態(tài)欄的概念。它被送到任何你想要它去的地方。
你可以隨時(shí)隨地訪問(wèn)通知。由于大多數(shù)應(yīng)用沒有強(qiáng)迫去實(shí)現(xiàn)一個(gè)全屏的設(shè)計(jì),用戶在他們需要的時(shí)候可以下拉通知‘抽屜’。對(duì)多數(shù)人來(lái)說(shuō),Android 是他們的第一個(gè)智能手機(jī),它改變了人們過(guò)往查看通知的慣例,過(guò)去你需要打開一個(gè)個(gè)單獨(dú)的應(yīng)用去查看你是否錯(cuò)過(guò)了電話,短信或者郵件。
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ái)開始,通知系統(tǒng)走過(guò)了漫長(zhǎng)的道路。
這是對(duì)大多數(shù)人來(lái)說(shuō)的 Android 的開始(包括我)。我們有一些可以定制的功能,比如應(yīng)用圖標(biāo),標(biāo)題,描述以及時(shí)間。如果你需要加入自定義的控件,比如,一個(gè)音樂(lè)播放器當(dāng)然也可以。系統(tǒng)會(huì)維護(hù)所需的寬高限制,而你則可以加入你想要的視圖。在通知中使用自定義的布局是當(dāng)時(shí)大多數(shù)音樂(lè)播放器實(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 等待直到他準(zhǔn)備好。
PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);
// 設(shè)置最后的事件信息
notification.setLatestEventInfo(this, "Content title", "Content subtext", pi);
// 獲取通知 manager 的實(shí)例
NotificationManager noteManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
// 發(fā)布到系統(tǒng)欄
noteManager.notify(1, notification);
}
代碼:這段是如何在1.5-2.3中實(shí)現(xiàn)通知功能。
Android 1.6 中的運(yùn)行的結(jié)果:
http://wiki.jikexueyuan.com/project/objc/images/11-17.png" alt="" />
Android 2.3 中的運(yùn)行結(jié)果:
http://wiki.jikexueyuan.com/project/objc/images/11-18.png" alt="" />
通知系統(tǒng)在 Android 3.0 上實(shí)際有一絲退步, Android 平板,一個(gè)用來(lái)對(duì)抗 iPad 的版本,是 Android 在大屏幕運(yùn)行的一次嘗鮮。相對(duì)于單一的抽屜顯示,Android 嘗試用額外的控件帶來(lái)新的通知體驗(yàn),你依舊有一個(gè)抽屜類型的通知,同時(shí)你也可以接收像 growl
那樣的通知。幸運(yùn)的是,與此同時(shí) Android 提供了一個(gè)叫做 NotificationBuilder
的全新 API,允許我們利用建造者模式 去構(gòu)建我們的通知。盡管略微復(fù)雜,但構(gòu)造器會(huì)根據(jù)每個(gè)新版操作系統(tǒng)的不同來(lái)構(gòu)建復(fù)雜的通知對(duì)象:
// 創(chuàng)建 Intent 實(shí)例
Intent intent = new Intent(this, Main.class);
// 在準(zhǔn)備好使用之前,持有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 的實(shí)例
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="" />
當(dāng)你在導(dǎo)航欄點(diǎn)擊它時(shí)候的樣式:
http://wiki.jikexueyuan.com/project/objc/images/11-20.png" alt="" />
當(dāng)你點(diǎn)擊時(shí)鐘看到的通知的樣式:
http://wiki.jikexueyuan.com/project/objc/images/11-21.png" alt="" />
各種冗余的通知讓用戶感到困惑不知道它們代表什么,這就對(duì)開發(fā)人員提出設(shè)計(jì)挑戰(zhàn),如何在恰當(dāng)?shù)臅r(shí)間返回恰當(dāng)?shù)男畔⒔o用戶。
與其他系統(tǒng)相比,Android 從 4.0 之后真正充實(shí)和統(tǒng)一了通知體驗(yàn)。雖然在 4.0 沒有帶來(lái)任何激動(dòng)人心的設(shè)計(jì),但是 4.1 帶來(lái)一種聚合的通知(一種全新的可視化,讓一個(gè) cell 中可以顯示多個(gè)通知),可擴(kuò)展的通知(比如,顯示電子郵件的第一段),圖片通知,以及可操作的通知。不用說(shuō),這提供了一種全新的方式可以帶給用戶 out-of-app 的體驗(yàn)。如果有人在 Facebook 加我為好友,我可以簡(jiǎn)單的在通知欄上點(diǎn)擊“接受”,再也不用打開 Facebook 應(yīng)用。如果我收到了一封垃圾郵件,我可以直接歸檔而不用再次查看。
這里有一些 Tumblr 應(yīng)用利用了新的 4.0+ API 的例子,使用它們構(gòu)建通知出人意料的簡(jiǎn)單;只需要你加入一些額外的通知風(fēng)格到 NotificationBuilder
中就可以了。
如果文字足夠短,還有什么理由讓我打開應(yīng)用來(lái)閱讀?大文本樣式提供了更大的閱讀空間來(lái)解決這個(gè)問(wèn)題。再也不需要浪費(fèi)時(shí)間打開一個(gè)應(yīng)用
Notification noti = new Notification.Builder()
... // 和之前一樣的通知屬性設(shè)置
.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īng)用就能夠享受到內(nèi)容優(yōu)先的美妙體驗(yàn)。不僅可以提供大量的內(nèi)容,也是一種優(yōu)雅的交互方式。
Notification noti = new Notification.Builder()
... // 同之前一樣的屬性設(shè)置方法
.setStyle(new Notification.BigPictureStyle().bigPicture(mBitmap))
.build();
http://wiki.jikexueyuan.com/project/objc/images/11-24.png" alt="" />
聚合通知是將多個(gè)通知放在一起,聚合有一點(diǎn)欺騙性因?yàn)樗鼘?shí)際上并不堆?,F(xiàn)有的通知,你依然可以自己創(chuàng)造他們,所以這真的是一種很好展示通知的方式:
Notification noti = new Notification.Builder()
... // The same notification properties as the others 和之前一樣的屬性設(shè)置
.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)默認(rèn)的樣式,使用戶總是感覺在使用通知抽屜:
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="" />
這類交互是一種對(duì)用戶負(fù)責(zé)的設(shè)計(jì),并且操作簡(jiǎn)單快速,受限于 Android 的緩慢性能,這種快捷操作的方式非常受歡迎,因?yàn)槟銓?shí)際上不需要打開應(yīng)用就可以使用它。
現(xiàn)在的科技圈對(duì)于任何一個(gè)人來(lái)說(shuō)都已經(jīng)不再像之前那么神秘,正因如此 Android 可穿戴設(shè)備也成為了科技設(shè)備中的一份子。它是否能夠成功的成為一類消費(fèi)品這件事情似乎仍然有待商榷,但是對(duì)于那些想要支持 Android 穿戴設(shè)備的開發(fā)者來(lái)說(shuō),仍然有很多不容忽視存在著的障礙。沒有辜負(fù) Android 系統(tǒng)傳承下來(lái)的一些優(yōu)勢(shì),其穿戴設(shè)備在與你的設(shè)備進(jìn)行同步的時(shí)候似乎總可以接受正確的通知。但事實(shí)上,你的手機(jī)與 Android 穿戴設(shè)備連接后,它將會(huì)在沒有代碼修改的情況下對(duì)設(shè)備推送構(gòu)造器創(chuàng)建的通知。能夠簡(jiǎn)單使用建造者模式則意味著無(wú)論出現(xiàn)什么設(shè)備,只要它們能夠支持 Android 系統(tǒng)和 Android 可穿戴設(shè)備,立即會(huì)有大量熟練使用 API 來(lái)收發(fā)數(shù)據(jù)的應(yīng)用開發(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 穿戴設(shè)備支持,不用寫任何額外的代碼!
雖然 Android 的 NotificationBuilder
支持高自由度定制,但有的時(shí)候依然無(wú)法滿足人們的需求,這就是為何要引入自定義通知布局。很難想象當(dāng)你擁有全部的通知系統(tǒng)的控制權(quán)限的時(shí)候。你將如何改變它,讓它與眾不同? 在諸多約束的情況下不斷創(chuàng)新著實(shí)很難,但是許多 Android 開發(fā)者已經(jīng)開始迎難而上。
自定義音樂(lè)播放器通知:
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="" />
自定義通知僅限于視圖組件所支持遠(yuǎn)程視圖的一個(gè)子集,這些視圖組件本身不能高度延伸或者被覆蓋。雖然只能輕度定制,但是你依然可以利用基本組件構(gòu)造復(fù)雜的通知。
然而創(chuàng)建這些自定義視圖可能需要更多的工作。使用 Android 的 XML 創(chuàng)建自定義通知視圖布局系統(tǒng),你要確保在不同 Android 版本看起來(lái)依舊良好。這非常痛苦,但是當(dāng)你看看這些美麗的通知,你會(huì)覺得一切又那么有價(jià)值:
<?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>
這是一個(gè)非?;镜淖远x通知布局,包含一張圖片以及一段文字。
現(xiàn)在我們已經(jīng)了解了大量歷史,接下來(lái)讓我們看一些關(guān)于通知是如何工作的有趣行為。也許這部分內(nèi)容我們已經(jīng)提及,開發(fā)者可以 完全控制通知系統(tǒng)。就是說(shuō)通知可以因?yàn)槿魏卧蛟谌魏螘r(shí)候顯示或者消失。甚至不需要從 Google 推送服務(wù)接收一個(gè)通知。實(shí)際上,就算我們收到一個(gè)推送通知,它默認(rèn)也不會(huì)顯示在狀態(tài)欄,你需要自己去捕捉推送通知并且決定如何去處理它。
舉例來(lái)說(shuō),一個(gè)常規(guī)的通知交互應(yīng)該是這個(gè)樣子:
比較有趣的是第二步和第三步,后臺(tái)服務(wù)沒有時(shí)間限制。如果推送通知告訴你要下載 1GB 的文件,好吧無(wú)所謂!在大多數(shù)情況下,如果一個(gè)后臺(tái)進(jìn)程用時(shí)非常短的話,系統(tǒng)并沒有要求你必須顯示一個(gè)通知。但長(zhǎng)時(shí)間后臺(tái)服務(wù)(比如音樂(lè)播放器)依舊需要在狀態(tài)欄顯示一個(gè)圖標(biāo)。對(duì)于開發(fā)者來(lái)說(shuō)這是一種值得深思的做法,確保用戶知道是何種服務(wù)在后臺(tái)長(zhǎng)時(shí)間運(yùn)行。
雖然只有 4 個(gè)步驟,但是有一多半的開發(fā)者不愿意處理。如果我可以直接發(fā)送整個(gè) payload 是不是會(huì)更簡(jiǎn)潔?GCM (Google Cloud Messaging) 允許 payloads 控制在 4KB 之內(nèi),平均來(lái)說(shuō),它在 1,024 到 4,096 UTF-8 字符之間(取決于字符)。除非你要推送一張圖片,不然你可以塞入任何你想要的內(nèi)容。聽起來(lái)多棒!
那么開發(fā)人員是如何控制用戶與通知之間的互動(dòng)?當(dāng)然,我們已經(jīng)知道可以添加自定義控件和按鈕,而且我們已經(jīng)看到了如何實(shí)現(xiàn)一般的點(diǎn)擊。但還有其他的么?實(shí)際上,當(dāng)然有!有一個(gè)“刪除”功能,當(dāng)用戶設(shè)置 setDeleteIntent
,用戶從抽屜中刪除通知的時(shí)候?qū)⒈贿壿媱h除。加入刪除是一個(gè)偉大的進(jìn)步,以確保我們不再次顯示老舊信息:
// 在 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();
讓我們更深入地談一談通知的默認(rèn)點(diǎn)擊。現(xiàn)在你當(dāng)然可以在點(diǎn)擊通知后執(zhí)行一些默認(rèn)的行為。你可以僅僅打開那你的應(yīng)用。用戶可以自己找到他們想要去的頁(yè)面。但是如果我們可以直接顯示相關(guān)的頁(yè)面那么體驗(yàn)會(huì)更加友好。如果我們收到一個(gè)郵件通知,我們直接跳轉(zhuǎn)到郵件內(nèi)容,如果我們的一個(gè)朋友在 Foursquare 上簽到,我們直接可以打開應(yīng)用顯示這個(gè)他或者她所在的餐館。允許通知指向到一個(gè)包含內(nèi)容的深度鏈接是一個(gè)很棒的功能。但是更多時(shí)候,深度鏈接已經(jīng)是你應(yīng)用的一部分,你會(huì)遇到導(dǎo)航層次混亂的問(wèn)題。你無(wú)法使用導(dǎo)航’返回‘。 Android 可以幫助你在開始之前創(chuàng)建視圖堆棧來(lái)解決這個(gè)問(wèn)題。這是通過(guò) TaskStackBuilder 類來(lái)完成的。使用這個(gè)技術(shù)需要一些魔法并且需要一些應(yīng)用架構(gòu)方面的知識(shí),但是有空你可以看看 Google 開發(fā)者網(wǎng)站,這里有一個(gè)簡(jiǎn)要實(shí)現(xiàn).
用 Gmail 舉例,我們告訴應(yīng)用 "打開郵件應(yīng)用,接著打開一份指定的郵件。" 來(lái)代替簡(jiǎn)單的打開郵件應(yīng)用。用戶將不會(huì)看到所有的視圖創(chuàng)建,而只會(huì)看到最終的結(jié)果。這是多么的不可思議,因?yàn)楝F(xiàn)在我們點(diǎn)擊返回按鈕的時(shí)候,用戶將不會(huì)退出應(yīng)用,而只會(huì)退到應(yīng)用的首頁(yè)。
我已經(jīng)詳細(xì)羅列了許多 Android 系統(tǒng)中通知的功能,同時(shí)也展示了它們有多么的強(qiáng)大。但是沒有哪個(gè)系統(tǒng)是完美的,Android 的通知系統(tǒng)也有瑕疵。
Android 用戶面臨的首要問(wèn)題之一則是沒有控制通知系統(tǒng)的功能。這就意味著如果一個(gè)應(yīng)用有通知提示的時(shí)候,用戶沒法關(guān)閉它,唯有卸載這個(gè)應(yīng)用。從 Android 4.1 開始,用戶可以在設(shè)置中選擇關(guān)掉指定程序的通知。這阻止了應(yīng)用在狀態(tài)欄的所有通知,這看起來(lái)是十分有用的功能,但是在用戶實(shí)際使用中卻受限很大,因?yàn)槠鋵?shí)很少有人想要將所有的應(yīng)用通知都關(guān)閉,他們很多時(shí)候只想關(guān)掉一些令人惱火的通知,比如 LED 提示燈閃爍或者是不停發(fā)出的提示音這類通知。
從 Android 4.1 開始,用戶可以通過(guò)設(shè)置來(lái)關(guān)閉接收通知,但是這里沒有一種方式來(lái)關(guān)閉 LEDs 或者聲音,除非開發(fā)者顯式地提供了關(guān)閉的方法。
你也許認(rèn)為我們已經(jīng)將通知功能基本掌握了,但其實(shí)我們?nèi)匀挥泻艽蟮倪M(jìn)步空間。盡管現(xiàn)有的系統(tǒng)已經(jīng)具備了一定的個(gè)性化定制功能,但我仍然希望看到它能夠更上一層樓。正如早些時(shí)候我們看到的,NotificationBuilder
使你的通知形成一個(gè)特定的樣子,也就是所有的系統(tǒng)通知都是一樣的。如果你使用自定義布局并且建立自己的通知模式,也只有一小部分支持的組件來(lái)供你使用。如果有很復(fù)雜的組件需要定制,那出于安全考慮你的想法很有可能無(wú)法實(shí)現(xiàn)。如果你想做一些更高級(jí)的功能,比如幀動(dòng)畫,或者一個(gè)視頻,請(qǐng)還是算了吧。
Android 提供了給用戶和開發(fā)者不少通知方面的功能。從一開始,Android 有意識(shí)的朝著更大膽的方向努力,即使從今天看來(lái)它依舊無(wú)以倫比??纯辞斑M(jìn)中的 Android 以及 Android 穿戴設(shè)備,很容易發(fā)現(xiàn),通知管理 API 重點(diǎn)強(qiáng)調(diào)方便使用。雖然在細(xì)粒度的通知管理缺乏完整的 UI 控件方面有一些缺點(diǎn),但是謹(jǐn)慎的說(shuō),如果你正在尋找一個(gè)通知優(yōu)先的生態(tài)系統(tǒng),Android 或許值得一試。