標籤:
基本功
iOS在誕生之初為了最大程度的保證使用者體驗,做了一些高瞻遠矚且影響深遠的設計。APNs(Apple Push Notification service)就是其中一項。
早期iOS裝置的記憶體和CPU資源都很有限,為了讓前台活躍的app擁有儘可能多的系統資源,以及節約裝置電量,iOS一開始就“不允許”普通app的進程常駐後台。這個決定很大程度上保障了使用者體驗和延長了手機的待機時間,但app的開發商需要和他們的使用者保持聯絡。開發商需要有一個穩定的網路通道能每隔一段時間推送新的內容到使用者裝置。Apple決定自己來搭建維護這個通道,也就是我們今天所說的APNs。記得剛開始接觸iOS開發的時候,看到有不少開發人員吐槽push機制,覺得不可控且增加了開發成本。其實稍微思考下Apple今天的平台規模和訊息量,以及所帶來的成本消耗,就能明白Apple設計這個機制所需要的智慧和魄力。一切都是為了使用者。
APNs雖然允許開發商推送訊息到使用者裝置,但考慮到訊息的量級和成本,這個由Apple維護的長連結通道就不可能是無限制使用的。APNs有著諸多的限制:
可靠性。一般情況下,Apple會保證這個通道的Qaulity of Service,也就是推送的訊息能及時穩定到達裝置。不過一旦使用者的裝置處於offline狀態,Apple只會儲存發送給使用者的最新一條push,之前發送的push會被直接丟掉。而且這最後一條離線push也是有到期時間的。一些使用者應該有過這種經曆,在使用的時候,明明對方發送了多條訊息,卻只收到了一條push。
Payload Size。每一條push訊息的包體大小是有最大限制的。Apple在文檔裡清楚的說明,push只應該用來通知使用者有新的內容,而不應該用來承載內容本身。理論上payload size越小,push到達裝置的機率就越高。在iOS8之前max payload size是256位元組,到iOS8發布這個最大值被調整到了2048位元組,再到最近的iOS9發布,引入了HTTP2.0,payload size又被設為4KB了。老版本的256位元組實在有點捉襟見肘,連塞一個連結進去都要考慮再三。到2KB的時候就寬裕多了,已經有不少開發商開始嘗試往裡面放少量的業務資料了,如果能減少開啟app之後的一次網路請求何樂而不為呢。當然4KB的想象空間會更大。Apple一直在調整這個數值,為的是給開發商更多的空間去提升使用者體驗。push慢慢變的不僅僅是一條“alert”那麼簡單了。
成功率並不高。Apple雖然保證了push通道一定程度的可靠性,但push由於各種各樣的原因並不能保證較高水平的到達率。push需要向使用者申請許可權,即使當時賦予了許可權,後面也可能由於push過於頻繁被使用者又關掉。在夜間模式下push雖然能到達通知欄,可使用者沒有任何感知,更不用說點擊push啟動app了。還有server端token失效,這點可以通過feedback service來清理失效的token。Apple的APNs server據說每天會發送超過百億條push,在某個時間段出現峰值的時候,開發商server和Apple server串連的成功率也會降低。還有用戶端裝置所處網路環境並不穩定等等因素,使得通過push成功啟動app的成功率並不怎麼高。
理解了上面這些限制,就能按照Apple的規範向使用者推送內容了。但push裡面的門道遠不止這麼簡單,Apple也從沒有停止過對APNs體驗的最佳化,類似payload size調整,interactive notification等等,每一個新的feature增加,哪怕是細微的改動,都能被聰明的開發人員加以利用,以四兩撥千斤提升產品的體驗。下面就介紹一些筆者所瞭解到的“隱蔽門道”。
不僅僅是Local Push
很多個人開發人員不具備搭建server的條件,一般會設定一個定時的local push來提醒使用者喚醒自己的app。Local push看起來似乎是個廉價的折中方案,事實上它可以更強大。APNs(一般也叫做remote push)因為有上面的各種限制,並不能很好的契合業務需要。而Local Push則不同,擁有完整的app業務上下文,還可以對push進行定製化。如果可以用Local Push替代Remote Push對體驗的提升是不言而喻的。Loca push的限制在於app必須處於運行狀態才能發起,很多聰明的開發商會開啟background task,在使用者按了home鍵之後再爭取到幾分鐘的已耗用時間,在這期間所有的remote push都被替換成了local push。不要小看了這幾分鐘的時間,對於很多活躍度高的app來說,按home鍵之後馬上又產生新的使用者內容的機率並不小。,WhatsApp都採用了這種機制來提升體驗。
叫醒你的App
開啟background task之後雖然能夠再多運行一會,但時間一到,app還是會被掛起或者kill。大部分多時候你的app是處於非活躍狀態。很多app都需要預先擷取內容,或者後台下載檔案等來減少使用者的等待時間。iOS7引入的Silent Notification和Background Fetch機制可以一定程度上滿足這種需要。silent push實現比較簡單,開啟相關後台許可權之後發送如下特定格式的json就能啟用。
喚醒app之後能處理的業務就多了,這對不少app來說是個非常實用的拓展,預先載入內容也好,產生local push也好,都能提升體驗。但這種喚醒機制並不總是可靠,有時候會“叫不醒”。app如果被手動kill叫不醒,如果background fetch被使用者關閉也叫不醒,但這兩種情況在手機充電的時候又可以被叫醒。Apple有一套自己的“智能”策略。
前台訊息通道
大部分時候APNs都被用來通知使用者某個處於background的app有新內容。但其實說白了APNs不過就是一條基於長連結的資料通道,在app處於foreground的時候也是能收到push訊息的,不過不會有任何UI展示提醒而已。處理回調的位置也是在 也就是說APNs其實還是個免費的前台訊息通道。而且有時候走APNs通道會比自己的server通道更快,如果用戶端做好資料去重,多一個輔助的資料通道當然能提升體驗。
新神通PushKit
APNs設計的初衷是避免app常駐後台,只在使用者點收到push的時候主動去啟動app。前面提到的silent push可以在有限的情境下,無需使用者感知啟動app。但到iOS8引入PushKit framework之後,app就可以通過push隨時喚醒了,不過這個新的神通暫時還只限於voip類應用。
之前在社區看到有人提問,說電話本可以在使用者掛掉電話的時候,把呼叫中的push改成未接電話,好奇是怎麼辦到的。因為大家都知道remote push是無法通過server動態修改push內容的,所以答案只有一個可能,app被後台喚醒了。使用者看到的push其實是local push,而local push是可以在用戶端隨意調整的。喚醒到方式就是利用PushKit。
當然好處不僅僅是修改push內容這麼簡單。WhatsApp的使用者在iOS8之後應該會有明顯的感覺,好像很少看到啟動頁面了。看起來似乎是WhatsApp開啟了voip後台常駐運行模式,但這種模式會比較費電,一些使用者會有顧慮。真相也並非如此,WhatsApp並沒有常駐後台,只不過是開啟了PushKit的push喚醒機制。每次使用者有新的離線訊息,普通文本或者是voip call,app都會先被後台喚醒,再從server拉取離線訊息,最後產生local push。等使用者點擊local push啟動app的時候,沒有啟動頁面,沒有connecting和loading,所有的資料已經準備就緒,就好像WhatsApp一直在後台運行一樣。也就是說,WhatsApp其實已經把所有的push都換成了local push。驗證方法也很簡單:
- 手動kill WhatsApp。
- 手機進入飛航模式。
- 收一條離線訊息。
- 使用tcpdump開始監聽iphone網路包,關閉飛航模式。
- 這時候,app被push喚醒,能看到如一條WhatsApp相關的網域名稱解析,說明app被啟動了。而且能看到很多後續的伺服器互動(拉取離線訊息之類)。 2 不知道是出於什麼考慮,既沒有開啟voip後台常駐模式,也沒有利用PushKit喚醒機制。每次收到訊息之後開啟app,都是先看到地球,串連中,收取中,到真正看到最新訊息經常需要3s以上。PushKit已經沒有電量方面的額外損耗了,對voip類應用的體驗提升非常之大。
具體怎麼實現PushKit可以參照文章末尾的連結地址。
總結
關於push這條長連結通道,Apple幾乎在每次的iOS新版本裡都會增加一些feature。為了控制新feature帶來的影響,每次改動都不多,但怎麼利用這些feature就看開發人員各自都功力了。對使用者體驗帶來的改變遠不止官方文檔上介紹的那麼簡單,只有多思考,時刻關注行業最新動向,才能發掘更多的隱藏“門道”。
參考文章
- Apple APNs官方文檔:https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html
- Silent Push實現:http://hayageek.com/ios-silent-push-notifications
- PushKit實現:https://zeropush.com/guide/guide-to-pushkit-and-voip
來自: http://music4kid.github.io/ios/2016/01/06/push/
感謝:http://www.open-open.com/lib/view/open1452487743355.html
IOS的APNS和PushKit門道詳述