如何實現不影響用戶體驗的後臺保活linux
對於移動APP來講,IM功能正變得愈來愈重要,它可以建立起人與人之間的鏈接。社交類產品中,用戶與用戶之間的溝通能夠產生出更好的用戶粘性。android
在複雜的 Android 生態環境下,多種因素都會形成消息推送不能及時達到客戶端。另外,不穩定的移動網絡也給數據傳輸的速率和可靠性增長了障礙。緩存
本文詳解了網易雲信IM SDK在應對弱網環境、移動端硬件限制以及Android複雜的生態現狀時的探索與心得.如何實現不影響用戶體驗的後臺保活,改善的長鏈接加推送組合方案,以及在弱網環境大數據傳輸的優化實踐。
帶着思考閱讀:安全
1.什麼是IM網絡
2. IM SDK如何實現不影響用戶體驗的後臺保活
3.如何作長鏈接加推送組合方案
4.如何在弱網環境下優化大數據傳輸
架構
IM的定義app
IM由兩個字組成:Instant,Messaging。
即時性要求有新消息時可以當即收到,若是程序在後臺,則要能當即收到推送通知。
通訊則要求穩定可靠,系統不宕機,程序不崩潰,安全,傳遞消息時不會被攔截監聽,消息不丟,順序不亂,不重複,若是包含音視頻聊天,則要求延遲低,流暢不卡頓。大數據
要真正作出一套穩定可靠的商用級IM系統,挑戰很是大。優化
第一個問題是消息推送。iOS有 APNS作推送,至關穩定。Android自己也有GCM能夠用,可是在國內有「牆」,直接就把GCM等等google的服務所有擋在外面。爲了實現即時穩定的消息推送,從易信時代開始,網易就開始研究,隨着時間的推移,困難和方法也在不停的變化。ui
對於IM,當APP退到後臺,是必須還可以收到新消息提醒的,沒有GCM,怎麼辦?在最初,惟一能作的,就是後臺運行了。這幾乎是接收推送的惟一途徑,就算是到如今,也是最主要的途徑。Android從設計上,就是支持真後臺運行的,後臺運行的特性也是Android如今能如此成功的緣由之一,但另外一面,Android長久以來一直襬脫不了的卡頓,耗電等壞名聲,後臺運行也拖不了干係。所以,系統對於後臺運行也不會聽任自流。
APP在後臺運行所面對的四大障礙
第一個障礙是Android的Low Memory Killer機制。手機的內存有限,當後臺運行的進程愈來愈多,內存剩餘量也就隨之減小。當有一個新的APP想要啓動,若是內存不夠,LMK機制就會啓動,從正在運行的進程中挑選一個清理掉,釋放出空間,而後新的APP就能夠運行了。
LMK有兩個尺度去評判。一個是進程優先級,優先級越低,被清理的可能性越大,另外一個是內存佔用,佔的內存越多,被清理的權重天然也越大。
由於LMK機制的存在,雖然APP容許在後臺運行,但一樣也面臨隨時被清理的風險。所以,網易須要在被清理後及時的從新啓動。
第二個障礙是alarm,鬧鐘,有循環鬧鐘和一次性鬧鐘兩種,在鬧鐘觸發後啓動對應的組件。
第三個障礙是在Manifest文件中靜態註冊的Receiver,經過監聽各類系統事件,好比開機,網絡變化,mount/unmounts等,在這些事件發生時啓動組件,由於這種方式會形成在這些事件發生時系統容易卡頓,在7.0裏面,Android增長了限制。
第四個障礙是JobScheduler,這是在5.0裏面新增的,容許APP在特定事件發生時作一些動做,好比充電,切換到wifi等。
雖然說不管怎麼作,APP終究免不了一死,但經過對照LMK的評判準則,仍是能夠下降APP被清理的機率的。第一個就是下降進程的內存佔用。若是採用單進程的模式,因爲進程中包含了UI,Webview,各類圖片緩存等內容,內存必然會居高不下,降不下來。IM軟件通常都會採用雙進程甚至多進程的策略,將push進程獨立出來,在push進程裏只處理網絡鏈接和push業務,不參與任何其餘業務邏輯,更不包含任何UI。
如下是網易雲信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,或者是集成了同一個SDK的APP,若是有,就把這些APP都拉起來。這也就是如今比較出名的「全家桶」方案。雖然說這種方法確實可以帶來較高的後臺存活率,特別是那些大廠和應用普遍的sdk,可是這種方式對於用戶的傷害也很是大,若是有後臺推送的必要性,且不會對用戶體驗形成太大傷害時,此方式還可使用,但若是隻是爲了推廣告,則會對用戶形成傷害,反過來,也可能會致使用戶直接卸載APP。
如今各類手機管理軟件都會對這種全家桶喚醒方式作限制,特別是在root過的機器上,能夠作到徹底切斷這些喚醒路徑。同時,不少ROM也會自帶管理軟件,限制後臺運行和後臺喚醒,以便給設備換取更長的續航。在目前國內的Android生態環境中,不管採用什麼方式,想要一直在後臺運行時愈來愈難了,須要從新想另外的辦法來保障消息推送。另外一方面,做爲開發者,也有義務爲用戶提供更好體驗的軟件,而不是無休止的在後臺浪費用戶的資源。
相關文章:
【推薦】 2017年網易有數用戶定位和產品能力