微信團隊原創分享:Android版微信後臺保活實戰分享(程式保活篇)

guojin08發表於2018-03-20

http://www.52im.net/thread-210-1-1.html

哪些部分需要“保活”?


按照我們的理解包含兩部分:


程式保活概述


在Android系統裡,程式被殺的原因通常為以下幾個方面:

  • a. 應用Crash
  • b. 系統回收記憶體
  • c. 使用者觸發
  • d. 第三方root許可權app.

原因a可以單獨作為一個課題研究。原因c、d目前在微信上沒有特殊處理。這裡討論的就是如何應對Android Low Memory Killer。

程式保活實施:程式拆分


微信團隊原創分享:Android版微信後臺保活實戰分享(程式保活篇)_0 (3).png

上圖表述的是微信主要的幾個程式:

  • a. push主要用於網路互動,沒有UI
  • b. worker就是使用者看到的主要UI
  • c. tools主要包含gallery和webview

拆分網路程式,確實就是為了減少程式回收帶來的網路斷開。

微信團隊原創分享:Android版微信後臺保活實戰分享(程式保活篇)_0 (4).png 

可以看到push的記憶體要遠遠小於worker。而且push的工作性質穩定,記憶體增長會非常少。這樣就可以保證,儘量的減少push 被殺的可能。這裡有個思路,但限制比較多,也拋磚引玉。啟動一個純C/C++ 的程式,沒有Java run time ,記憶體使用極低。

這種做法限制很明顯,如:沒有Java run time ,所以無法使用Android系統介面。缺乏許可權,也無法使用各種shell命令操作(如am)。但可以考慮一下用途:高強度運算,網路連線,心跳維持等。比如Shadowsocks-android就如此,通過純c命令列程式,維護著socks5代理 (Android M執行正常)。

tools程式的拆分也同樣是記憶體的原因:

  • a. 老版本的webview 是有記憶體洩漏的
  • b. Gallery大量縮圖導致記憶體使用大

微信在進入後臺後,會主動把tools程式kill掉。


程式保活實施:及時拉起


系統回收不可避免,及時重新拉起的手段主要依賴系統特性。從上圖看到, push有AlarmReceiver, ConnectReceiver,BootReceiver。這些receiver 都可以在push被殺後,重新拉起。特別AlarmReceiver ,結合心跳邏輯,微信被殺後,重新拉起最多一個心跳週期。

而對於worker,除了使用者UI操作啟動。在接收訊息,或者網路切換等事件, push也會通過LocalBroadcast,重新拉起worker。這種拉起的worker ,大部分初始化已經完成,也能大大提高使用者點選微信的啟動速度。

歷史原因,我們在push和worker通訊使用Broadcast和AIDL。實際上,我一直不喜歡這裡的實現,AIDL程式碼冗餘多, broadcast效率低。歡迎大家分享更好的思路或者方法。

程式保活實施:程式優先順序


Low Memory Killer 決定是否殺程式除了記憶體大小,還有程式優先順序:
微信團隊原創分享:Android版微信後臺保活實戰分享(程式保活篇)_0 (5).png 

上表的數字可能在不同系統會有一定的出入,但明確的是,數值越小,優先順序越高。對於優先順序相同的程式,總是會把記憶體佔用多的先kill。提高程式優先順序是保活的最好手段。

正常情況下微信的oom_adj:
微信團隊原創分享:Android版微信後臺保活實戰分享(程式保活篇)_0 (6).png 

而被提高優先順序後:
微信團隊原創分享:Android版微信後臺保活實戰分享(程式保活篇)_0 (7).png 

從統計上報看,提高後的效果極佳。原理:Android 的前臺service機制。但該機制的缺陷是通知欄保留了圖示。

對於 API level < 18 :呼叫startForeground(ID, new Notification()),傳送空的Notification ,圖示則不會顯示。對於 API level >= 18:在需要提優先順序的service A啟動一個InnerService,兩個服務同時startForeground,且繫結同樣的 ID。Stop 掉InnerService ,這樣通知欄圖示即被移除。

這方案實際利用了Android前臺service的漏洞。微信在評估了國內不少app已經使用後,才進行了部署。其實目標是讓大家站同一起跑線上,哪天google 把漏洞堵了,效果也是一樣的。

QA環節


Q:之前看微信的架構分享,貌似是通過單一Activity,用多個Fragment切換來實現的多視窗。如果分程式的話,看起來Gallery和
WebView是單獨的一個Activity,我的理解是否正確呢?以及進入後臺之後,為何只kill tools而不一起釋放work呢?

A:Fragment的改造只是用在有限的幾個UI上,大部分的UI,對於切換時間要求不高,還是保留成activity,Gallery和WebView都是單獨的
activity,所以才可能另外一個程式的。對於我們來說worker的保活僅次於網路的push,worker如果頻繁被殺,使用者每次啟動微信都需要
等待,這個就不好了。所以,我們在後臺,只會kill tools,不會主動kill worker。

Q:除了提高程式的優先順序,微信在記憶體方面有什麼處理或優化的技術嗎?
A:不可否認,其實微信是記憶體大戶了,現階段我們主要關注記憶體洩漏,沒有專門去減少記憶體的使用,畢竟記憶體意味著cache,意味著使用者體驗更快,後續對於記憶體優化我們有一些規劃,比如說,在cache這塊照顧一些低端機。

Q:我記得很久以前聽說過微信使用一個畫素的浮動視窗來保活,不知道現在還有沒有呢?
A:我們有想過,也聽說過有其他app是這樣做的,但從來沒實現過這個方案。

Q:你們push程式與worker程式採用過socket通訊方案麼?採用的話效果怎麼樣?
A:有考慮過用socket,後續也可能會有這種嘗試,但因為push和worker依賴程式碼太多,傷筋動骨了,但估計也要比AIDL好,AIDL對於應用出問題後能做的事情太少了。

相關文章