IM 推送保障及網絡優化詳解(一):如何實現不影響用戶體驗的後臺保活

對於移動 App 來講,IM 功能正變得愈來愈重要,它可以建立起人與人之間的鏈接。社交類產品中,用戶與用戶之間的溝通能夠產生出更好的用戶粘性。
在複雜的 Android 生態環境下,多種因素都會形成消息推送不能及時達到客戶端。另外,不穩定的移動網絡也給數據傳輸的速率和可靠性增長了障礙。
本文詳解了網易雲信 IM SDK 在應對弱網環境、移動端硬件限制以及 Android 複雜的生態現狀時的探索與心得。如何實現不影響用戶體驗的後臺保活,改善的長鏈接加推送組合方案,以及在弱網環境大數據傳輸的優化實踐。linux

帶着思考閱讀:

1.什麼是 IM
2. IM SDK 如何實現不影響用戶體驗的後臺保活
3.如何作長鏈接加推送組合方案
4.如何在弱網環境下優化大數據傳輸

IM 的定義

IM 由兩個字組成:Instant, Messaging
即時性要求有新消息時可以當即收到,若是程序在後臺,則要能當即收到推送通知。
通訊則要求穩定可靠,系統不宕機,程序不崩潰,安全,傳遞消息時不會被攔截監聽,消息不丟,順序不亂,不重複,若是包含音視頻聊天,則要求延遲低,流暢不卡頓。

圖片描述

要真正作出一套穩定可靠的商用級 IM 系統,挑戰很是大。
第一個問題是消息推送。iOS 有 APNS 作推送,至關穩定。Android 自己也有GCM 能夠用,可是在國內有「牆」,直接就把 GCM 等等 Google 的服務所有擋在外面。爲了實現即時穩定的消息推送,從易信時代開始,網易就開始研究,隨着時間的推移,困難和方法也在不停的變化。
對於 IM,當 App 退到後臺,是必須還可以收到新消息提醒的,沒有 GCM,怎麼辦?在最初,惟一能作的,就是後臺運行了。這幾乎是接收推送的惟一途徑,就算是到如今,也是最主要的途徑。Android 從設計上,就是支持真後臺運行的,後臺運行的特性也是 Android 如今能如此成功的緣由之一,但另外一面,Android 長久以來一直襬脫不了的卡頓,耗電等壞名聲,後臺運行也拖不了干係。所以,系統對於後臺運行也不會聽任自流。android

App 在後臺運行所面對的四大障礙

第一個障礙是 Android 的 Low Memory Killer 機制。手機的內存有限,當後臺運行的進程愈來愈多,內存剩餘量也就隨之減小。當有一個新的 APP 想要啓動,若是內存不夠,LMK 機制就會啓動,從正在運行的進程中挑選一個清理掉,釋放出空間,而後新的 APP 就能夠運行了。
LMK 有兩個尺度去評判。一個是進程優先級,優先級越低,被清理的可能性越大,另外一個是內存佔用,佔的內存越多,被清理的權重天然也越大。
由於 LMK 機制的存在,雖然 APP 容許在後臺運行,但一樣也面臨隨時被清理的風險。所以,網易須要在被清理後及時的從新啓動。常規的,有4種方式可以作到。git

  • 第一個是 sticky service
就是在 Service 的 onStartCommand 中返回sticky flag,這樣當 service 被 kill 掉後,系統會將它加入重啓的 pending 列表,在後面合適的時機再把 service 重啓。
  • 第二個障礙是 alarm
鬧鐘,有循環鬧鐘和一次性鬧鐘兩種,在鬧鐘觸發後啓動對應的組件。
  • 第三個障礙是在 Manifest 文件中靜態註冊的 Receiver
經過監聽各類系統事件,好比開機,網絡變化,mount/unmounts 等,在這些事件發生時啓動組件,由於這種方式會形成在這些事件發生時系統容易卡頓,在 7.0 裏面,Android 增長了限制。
  • 第四個障礙是 JobScheduler
這是在 5.0 裏面新增的,容許 App 在特定事件發生時作一些動做,好比充電,切換到 wifi 等。

雖然說不管怎麼作,App 終究免不了一死,但經過對照 LMK 的評判準則,仍是能夠下降APP被清理的機率的。第一個就是下降進程的內存佔用。若是採用單進程的模式,因爲進程中包含了 UI,Webview,各類圖片緩存等內容,內存必然會居高不下,降不下來。IM 軟件通常都會採用雙進程甚至多進程的策略,將 push 進程獨立出來,在 push 進程裏只處理網絡鏈接和 push 業務,不參與任何其餘業務邏輯,更不包含任何 UI。github

如下是網易雲信 Android SDK 的架構,按照分層的結構模式設計。最底下青色的一層是 push 層,他就是做爲一個獨立進程運行的。他只負責處理網絡長鏈接的相關工做,好比安全加密,心跳,鑑權,封包解包等工做,全部業務邏輯都交給UI進程的服務模塊去作。來看一下雲信 Demo 的進程內存佔用狀況。上面一個是主進程,看第四列 PSS 的數據,內存佔用是 50M 左右,下面一個是 push 進程,內存佔用只有 10M 左右。當處於後臺時,push 進程被清理機率比 UI 主進程低不少。算法

圖片描述

下降被清理機率的第二個手段是提高進程優先級。先看這個例子,這是綠色守護的一個截圖,最上面是「暫不自動休眠」,由於這裏列出的兩個 App 的狀態都是工做中,對應的進程優先級是「可視進程」。但這兩個 App 並無提供桌面小部門在運行,也沒有指示前臺服務的常駐通知欄提醒,事實上,他們就只是在後臺運而已。一般進程退到後臺後,其進程優先級類型就變成了較低的後臺進程,而不是這樣的「可視進程」,他們是經過什麼方法來提高優先級,下降被清理機率呢?緩存

圖片描述

Android 在設計前臺服務上有一個漏洞,經過兩個服務配合就能建立一個隱形的前臺服務。這裏有兩個已經啓動的 Service: A 和 B。先在 A 中調用 startForeground,提供一個 NOTIFY_ID, 而後 A 就變成前臺服務了,同時有了一個 ID 爲 NOTIFY_ID 的常駐通知欄提醒,而後網易在 B 中也調用startForeground,提供相同的 NOTIFY_ID, B 也變成了前臺服務,由於兩個通知ID相同,所以這一次就不會建立新的通知欄提醒了。而後再在 A 中調用stopForeground,A 的前臺屬性被取消,同時,常駐通知欄提醒也會被移除,可是,Service B 並不會受到任何影響,仍是前臺服務,這是再把 A 停掉,進程就只剩下前臺服務 B 了,進程也變成了前臺進程,但用戶不會有任何感知。安全

正常來講,作了上面 3 步以後,進程就可以比較穩定的在後臺運行了。網絡

但在有些狀況下,推送進程卻永遠起不來。跟蹤以後發現,除了系統可以殺掉後臺運行的進程外,用戶也同樣是能夠殺死進程的。用戶殺掉進程的方式有兩種,一種是在最近任務列表中將 App 劃掉,這種方式和系統殺掉進程效果相同。另一種就是經過這裏的 force stop,這種方式比系統清理更加完全。不但 App正在運行的進程會被清理,app 當前在重啓列表中的待重啓服務,註冊的各類鬧鐘,事件監聽組件等都會被移除,除非用戶在主動點擊或者系統重啓等外力,App 無法再本身從新爬起來了。
在有些國內的像 MIUI 一類的 ROM 上,用戶從最近任務列表中將 App 移除,效果居然也是 force stop。正常來講,若是是用戶主動操做,App 自己也不該該再重啓了。但有些時候這個並非用戶本意,何況,對於 IM 軟件來講,消息推送是必定要獲得保障的,不然不明正確的吃瓜羣衆們會以爲是軟件不行,連消息推送都作很差。架構

APP 安卓進程保活的好辦法

第一個是經過兩次 fork 加上 exec 的方式。兩個 fork 後,第一次 fork 的進程退出,第二次 fork 出來的進程就會被 init 進程領養。用戶此時再 force stop,由於這個進程復進程是 init,而不是 Zygote,所以不會被清理。因爲這個進程仍是從 android 進程 fork 出來的,帶有 android 運行時環境以及復進程的資源,因此內存會比較大,這裏能夠再經過 exec 命令,打開一個純 linux 的可執行文件,開啓一個 daemon 進程,其內存佔用大概只有 100K+,對用戶也就徹底無感了。利用這個後臺進程,能夠定時的將 push 進程拉起來。此種方式只在 5.0 如下的系統中有效,在 4.4 及以上系統中,SELinux 特性是強制開啓的,exec 沒有權限執行,同時在 5.0 以後,ActivityManager 在作 force stop 以及移除任務時,只要是具備相同的 uid 的進程,就會所有清理掉,再也不漏掉沒有虛擬機環境的進程。app

最後一個後臺保活的手段是一個大殺器,也是帶有強烈的中國特點。由於前面所列的全部保活手段都不是那麼保險,所以想出來這麼一個互相保活的方式。當一個 App 進程起來後,他就去掃描已安裝的應用列表,看看有沒有本身的兄弟姐妹,好比說同一個長的 App,或者是集成了同一個 SDK 的 APP,若是有,就把這些 App 都拉起來。這也就是如今比較出名的「全家桶」方案。雖然說這種方法確實可以帶來較高的後臺存活率,特別是那些大廠和應用普遍的 SDK,可是這種方式對於用戶的傷害也很是大,若是有後臺推送的必要性,且不會對用戶體驗形成太大傷害時,此方式還可使用,但若是隻是爲了推廣告,則會對用戶形成傷害,反過來,也可能會致使用戶直接卸載 App。

如今各類手機管理軟件都會對這種全家桶喚醒方式作限制,特別是在 root 過的機器上,能夠作到徹底切斷這些喚醒路徑。同時,不少 ROM 也會自帶管理軟件,限制後臺運行和後臺喚醒,以便給設備換取更長的續航。在目前國內的 Android 生態環境中,不管採用什麼方式,想要一直在後臺運行時愈來愈難了,須要從新想另外的辦法來保障消息推送。另外一方面,做爲開發者,也有義務爲用戶提供更好體驗的軟件,而不是無休止的在後臺浪費用戶的資源。


隨着即時通信以及音頻處理和壓縮技術的不斷髮展,效果更好、適用範圍更廣、性能更高的算法和新的技術必將不斷涌現,若是你有好的技術或者分享,歡迎關注網易雲信官方博客和 GitHub:

關注更多技術乾貨內容: 網易雲信博客
歡迎關注 網易雲信 GitHub
歡迎關注 網易雲信官網
相關文章
相關標籤/搜索