天天看點

推送 從入門到放棄

推送簡直就是一種輕量級的騷擾方式

自從有了推送,各個公司基本上都在使用推送,這确實是一個比較好的提醒方式,Android較iOS強的一個部分,也就是在于Android的Notification。Google教育我們利用好Android的通知子產品,做更多友好的互動,可這句話,翻譯成中文,不知不覺,就變成了在Notification中推送各種廣告,而且僅僅就是一些廣告,Notification各種牛逼的功能,完全不需要,這也違背了Google設計Notification的初衷。

更關鍵的是,現在随便找一款App,沒有推送的真是鳳毛麟角,更可惡的是,做外賣的App給我推送奧運新聞,一條新聞十幾個App推送,以至于現在很多使用者都非常反感各種推送廣告,就我本人而言,基本上會禁用所有廣告類的App的推送。

本人非常反感推送,借用王思聰的一句話,XXX App天天給我推送各種廣告,還TM是自己做的推送,真是絕了。

輪詢是最簡單的與伺服器保持通信的方式,即循環向伺服器通信。這個方案的特點就是通信由用戶端主動發起,你需要自己實作輪詢消息隊列、頻率等等參數,在功耗和效果間做權衡,類似于TCP的短連接配接。

這個其實就是借助短信來實作資訊的展示,隻不過把短信内容展示到了Notification中,這個方案,到達率确實高,畢竟短信是比較可靠、穩定的,但劣勢也很明顯,就是成本很高,而且在Android平台上,短信的權限比較開放,容易被劫持。

長連接配接和前面提到的短連接配接,都是基于Socket連接配接的方式,他們的差別在與,短連接配接是每次資料傳輸完畢後就斷開連接配接,而長連接配接不會。是以,基于輪詢的方式,每次都要進行鍊路的連接配接,性能消耗更大,基于長連接配接的方式,就是對這點的改進。應用一旦與伺服器連接配接成功,并不會主動斷開連接配接,後面的通信都基于這個通道。目前大部分的推送服務都是基于長連接配接的推送,在背景維護一個Service,維持應用與服務端之間的TCP長連接配接。

iOS這邊使用系統統一的APNs,所有推送消息都由蘋果的伺服器進行下發,同時,也由系統進行統一展示和處理。

與iOS一樣,Android同樣有一套内置的推送方案,但很可惜的是,Google的服務在中國大陸無法使用,草了個蛋。

專業的第三方推送

極光

個推

友盟推送

手機ROM廠商推送

華為推送

小米推送

BAT級别的全家桶

阿裡推送

信鴿推送

百度推送

關于第三方推送服務在各個App中的使用率,大家可以參考賈吉鑫的這篇文章:

<a href="https://mp.weixin.qq.com/s?__biz=MzA5OTMxMjQzMw==&amp;mid=2648112527&amp;idx=1&amp;sn=b23c1b5f3e32e343ad96d705bd4d63ff">https://mp.weixin.qq.com/s?__biz=MzA5OTMxMjQzMw==&amp;mid=2648112527&amp;idx=1&amp;sn=b23c1b5f3e32e343ad96d705bd4d63ff</a>

這些推送服務大同小異,基本上一家使用了一個新功能,另外幾家,也會很快推出這個功能,就例如之前比較火的,『共享推送通道進行App喚醒』這個技術,友盟、個推推出後,很快其它推送服務商就支援了,是以開發者并不需要擔心哪一家推送功能比較強。

這裡還需要說下現在的『推送喚醒』這樣一個功能,簡單的說,就是所有安裝了A推送的App,隻要有一個還活着,就可以把其它安裝了A推送的App拉起來,進而提高推送的到達率。有些阿裡系、百度系的App,被大家稱作『全家桶』,實際上就是因為這個原因,這個方式,确實能在一定程度上提高推送到達率,但另一方面,也破壞了Android生态,增加了功耗,打亂了系統的清理政策。

另外,小米推送、華為推送,大家接入的原因可能很簡單,就是他們的手機市場占有率比較高,接入他們自家的推送,可以在一定程度上提高到達率,但需要注意的是,推送分為透傳和非透傳兩種方式,透傳即我們自己App處理推送消息,而非透傳,則是交給相應的PushSDK處理,對于小米推送、華為推送來說,隻有采用非透傳消息,到達率采用保證,而透傳消息,與其它推送并沒有什麼差別,換句話說,小米手機、華為手機,隻對非透傳的推送消息做了可靠性保證,但非透傳消息的展示格式非常固定、簡單,且不能自定義,這是一個很大的問題,這點應該是很多開發者的誤區。

最後,很多推送服務都需要在Application中進行初始化,同時,各種被喚醒政策,又會拉起Application,導緻推送程序的重複,是以,這裡經常需要對程序名進行過濾,非主程序,不進行初始化。

基本都是基于AndroidPN、MQTT、XMPP、長連接配接這些方式去實作的,自己搭建Push平台服務,一個最大的問題就是服務端的架構設計,不僅成本高,而且效果不一定好,建議中小企業不要輕易嘗試。

一般來說,類似這類ID都是用于唯一辨別應用\使用者的,每個App在每台手機上都會生成一個唯一ID。

Android平台上因為國記憶體在大量山寨裝置,是以很多裝置的IMEI、Mac位址、AndroidID 都有可能為空或者錯誤,是以不能單獨作為唯一辨別,需要将這些進行組合起來使用。

對于應用解除安裝後RegistrationID的問題,很多PushSDK的政策是,生成一個DeviceID儲存到本地存儲,應用被解除安裝後如果被重新安裝,如果檢測到存儲裡的DeviceID還在的話,就判定是同一個裝置,不重新生成RegistrationID。

這些Key基本都是用于驗證App的,每個包名對應一個加密的Key。

非透傳消息是指推送消息被PushSDK擷取并處理,透傳消息是指推送消息被PushSDK交給宿主應用處理,非透傳消息通常隻能設定一些固定的樣式,比較簡單,而透傳消息,可以由App自定義處理,比較靈活。

通過應用使用的appid統計使用者注冊總量。

通過應用使用的appid統計當天的線上使用者數。

通過應用使用的appid統計當天在推送平台激活過的使用者總數。

線上消息下發數/總下發數。

消息回執數(去重)/消息線上下發數。

到達數/實際下發數。

是指最近三個月内有登入過(裝置與推送服務端建立長連結)的裝置總數,即有效可下發的使用者數。一般的推送服務端認為,裝置在100天内沒有登入請求,認為該裝置已經失效,是以無需再次發送。

實際可推送裝置數(在消息有效期内,有聯網并推送程序正常的裝置,即消息有效期内的線上下發數。消息有效期就是設定的離線時間)。

用戶端SDK接收到消息的裝置數(通過統計用戶端SDK接收到消息後的回執獲得)。

用自定義非透傳消息在使用者手機展示過的裝置數。

點選通知欄消息的裝置數。

那麼關于推送,大家實際上最關系的,就是『到達率』。那麼這個到達率究竟怎麼計算呢?

首先我們舉個例子來說明上面的這些資料背後的實際意義,例如,我們有一款App,有100w的下載下傳量,每個App啟動後,都将上報給伺服器一個唯一ID,是以,累計注冊量就是100w,也稱發送總量。

那麼在服務端準備發送推送的時候,目前手機端推送程序還活着的,也就是說推送的長連接配接還健在的,就是線上裝置,如果按天算,那麼就叫日線上裝置數,我們假設這個數字是60w。

OK,推送發出去後,用戶端收到推送消息,并産生回執,代表完成了一次推送,假設這些完成推送的裝置是55w,這個就是送達裝置數。一般來說,隻要裝置線上,基本都能送達,是以這個數字和線上裝置數非常接近,不接近的話,這個推送基本就有問題了,其中可能送不達的原因就在于網絡切換等導緻長連接配接斷掉這類因素。

那麼到這裡,一般的推送服務商會使用送達裝置數/線上裝置數的方式來計算到達率,當然,前面我們也說了,這個比例一定是很高的,如果保持長連接配接的裝置都不能收到推送,那一定是有問題了。

而一般的到達率,應該是送達裝置數/可送達裝置數,也就是百日内活躍的裝置數,這樣一除,這個比例一下子就小了很多,因為誰也不知道,這一百天内曾經活躍的使用者,第二天是不是就已經把你解除安裝了。是以說,Android下統計推送的到達率一般都很低,而推送服務商宣傳的到達率都很高,這其實就是一個偷換概念的問題,我們說的是一般的到達率,而服務商宣傳的是線上到達率。

而且,這個到達率與iOS完全沒有可比性,因為iOS統一通過APNs來進行推送,且無法擷取到達回執,是以,iOS基本不存在到達率這一說法,如果有,幾乎也是100%,完全沒有意義,是以,如果哪一天有産品或者營運跟你說,為什麼Android的到達率比iOS的到達率差這麼多,請毫不客氣的砸它一斤蘋果。

Tag,或者叫标簽,是使用者的一種屬性,在給某些使用者設定某類标簽後就可以針對推送。比如給喜歡『程式設計』的人打上『程式設計』的标簽,就可以隻給他們精準推送。

通常情況下,一個裝置(在一個App裡)可以設定多個标簽。标簽與别名類似,其對應關系也是儲存在推送伺服器側的。

Alias,或者叫别名,是對已經安裝某應用的使用者取個别名進行辨別,在對該使用者消息推送時,就可以用此别名來進行推送。設定了别名後,推送時伺服器端指定别名即可。推送伺服器端來把别名轉化到裝置ID來找到裝置。

Tag和Alias他們的共同點在于,提供對使用者的精确推送。

推送 從入門到放棄

目前大部分的第三方推送服務,都是基于長連接配接的推送方案,下面将對這個方式進行詳細講解。

首先,我們需要了解下一個網絡基本知識——NAT,即網絡位址轉換(Network Address Translation,NAT),這是因為IP位址是有限的,手機無論是通過路由器還是資料網絡,都有一個内網IP位址,同時,路由器上會維護一個外網IP位址,進而形成一個NAT路由表,即内網IP位址:端口,以及對應的外網IP位址:端口。這樣通過一層層封裝與解封裝,就達到了内網與外網交換通信的方式。

由于NAT路由表的大小有效,是以一般路由都有NAT有效期,WIFI下,這個NAT有效期可能會比較長,而在資料流量下,營運商一般都會盡快更新NAT路由表,淘汰無效的裝置,是以,在使用資料流量時,長連接配接經常容易斷。

那麼除了NAT路由表主動淘汰過期的裝置之外,切換網絡環境和DHCP伺服器租期到期,這些情況都有可能導緻NAT路由表改變,進而造成長連接配接中斷。

前面我們說了,現在的推送服務一般采用的是長連接配接的通信方式,而長連接配接會因為NAT路由表的更新而中斷,是以,用戶端會定時向服務端發送一個心跳包,來定期告知NAT路由表,我還活着,别殺我!這就是心跳包的作用——防止NAT路由表逾時,同時檢測連接配接是否被斷開。

既然心跳包的作用是防止NAT逾時,那麼就需要将心跳包的發送頻率設定為小餘NAT逾時的檢測頻率,而WIFI和資料流量下,對于NAT路由表的逾時時間又是不一樣的,而且不同的網絡營運商的逾時時間,甚至都不一樣,是以,一個比較好的方法就是根據裝置目前網絡環境,來動态的設定心跳時間。

注意,心跳包與輪詢是不一樣的,心跳包建立在長連接配接上,隻要發送資料即可,而輪詢每次都是一個完整的TCP連接配接。

既然需要定時任務,那麼就需要使用AlarmManager來作定時喚醒了,原因我之前的文章有講過,是關于處理器喚醒的原因,這裡就不贅述了,大家可以參考我之前的文章:

<a href="http://mp.weixin.qq.com/s?__biz=MzAxNzMxNzk5OQ==&amp;mid=2649484680&amp;idx=1&amp;sn=bd9086a95b769af8d8644cf681ce66ec#rd">http://mp.weixin.qq.com/s?__biz=MzAxNzMxNzk5OQ==&amp;mid=2649484680&amp;idx=1&amp;sn=bd9086a95b769af8d8644cf681ce66ec#rd</a>

所謂程序保活,是指App希望盡可能的保證自己的App的推送程序能夠存活在背景,以保證可以收到服務端的推送消息,是以,才出現了一大批關于程序保活的方式,例如NDK層的檔案鎖,fork子程序、前台服務、程序優先級等等方式,然而,這些東西,實際上,都不能完全保證手機的程序管理政策放過你,特别是Android 5.0以後的系統,Android對程序的管理更加嚴格,還有國内的這些ROM層的修改,ROM想要殺你這個程序,你怎麼做也沒有辦法,哦,除了白名單。是以,不要再花心思去找什麼程序保活的黑科技了,好好做好應用,提供使用者的使用黏性,才是最佳的保活,而對于一些産品、營運所謂的『為什麼微信、QQ都可以保活』這樣的問題,我建議你回答它:『如果你能把産品做到微信、QQ那樣的數量級,我也能讓你活!』

介于各種第三方推送與ROM推送的特點,我們目前采用的推送方案,名為『UniversalPushSDK』,即整合了多個不同的推送管道,通過模闆設計模式來進行整合,并向外暴露統一的接口,這種方式的好處在于UniversalPushSDK利用的各個不同推送特點,提高推送到達率,但是壞處在于,包的體積會大一些。例如,我們現在整合了『小米推送、極光推送、華為推送』,在系統啟動的時候,判斷目前系統,如果是小米系統,則啟用『小米推送』,如果是華為手機,則啟用『華為推送』,其它的Android裝置,則啟用『極光推送』,通過這種方式來設計我們自己的推送SDK,可以在一定程度上,利用好各個推送平台的特性。

那麼如果利用這種方式來設計SDK給到不同的App接入,就需要能夠将應用的推送Key做到動态配置,這也是我們遇到的最大的一個問題,解決方法大家可以參考我之前寫的一篇文章:

<a href="http://blog.csdn.net/eclipsexys/article/details/51283232">http://blog.csdn.net/eclipsexys/article/details/51283232</a>

雖然我極力反對這種方案,我堅持認為,做好App,提升使用者使用黏性,才是提升推送到達率的關鍵