1. 程式人生 > >再談Android客戶端程序保活

再談Android客戶端程序保活

在很多移動應用中,特別是即時通訊類專案中,保活是一個永遠無法避免的一個話題。保活,按照我的理解,主要包含兩部分:
網路連線保活:如何保證訊息接收實時性。
程序保活:儘量保證應用的程序不被Android系統回收。
在很早以前,談Android的保活都會涉及到程序常駐記憶體,如何進行效能優化等話題,今天就這些話題,做一個簡單的總結。

Android程序

在討論這個問題之前,我們首先來看一些現象級APP的程序。
這裡寫圖片描述
搞Android的同學都知道,每一個Android應用啟動後至少對應一個程序,有的則有多個程序,大多數主流APP都會包含多個程序,因為除了主要的程序之外,還有諸如長連線、推送等程序。

檢視程序

對於任何一個程序,我們都可以通過adb shell ps|grep 的方式來檢視。具體方式如下:
這裡寫圖片描述
上圖的具體含義如下:

解釋
u0_a16 USER 程序當前使用者
3881 程序ID
873024 程序的虛擬記憶體大小
37108 實際駐留”在記憶體中”的記憶體大小

程序劃分

Android系統按重要性從高到低把程序的劃為瞭如下幾種(嚴格來說是6種)。

1,前臺程序

此種程序指使用者正在使用的程式,一般系統是不會殺死前臺程序的,除非使用者強制停止應用或者系統記憶體不足等極端情況會殺死。

主要場景

  • 某個程序持有一個正在與使用者互動的Activity,並且該Activity正處於resume的狀態。
  • 某個程序持有一個Service,並且該Service與使用者正在互動的Activity繫結。
  • 某個程序持有一個Service,並且該Service呼叫startForeground()方法使之位於前臺執行。
  • 某個程序持有一個Service,並且該Service正在執行它的某個生命週期回撥方法,比如onCreate()、onStart()或onDestroy()。
  • 某個程序持有一個BroadcastReceiver,並且BroadcastReceiver正在執行其onReceive()方法。

2,可見程序

使用者正在使用,看得到,但是摸不著,沒有覆蓋到整個螢幕,只有螢幕的一部分可見程序不包含任何前臺元件,一般系統也是不會殺死可見程序的,除非要在資源吃緊的情況下,要保持某個或多個前臺程序存活。

主要場景:

  • 擁有不在前臺、但仍對使用者可見的 Activity(已呼叫onPause())。
  • 擁有繫結到可見(或前臺)Activity 的 Service。

3,服務程序

在記憶體不足以維持所有前臺程序和可見程序同時執行的情況下,服務程序會被殺死。
主要場景:

  • 某個程序中執行著一個Service且該Service是通過startService()啟動的,與使用者看見的介面沒有直接關聯。

4,後臺程序

後臺程序,系統可能隨時終止它們,用以回收記憶體。
主要場景:

  • 在使用者按了”back”或者”home”後,程式本身看不到了,但是其實還在執行的程式,比如Activity呼叫了onPause方法。

空程序

某個程序不包含任何活躍的元件時該程序就會被置為空程序,完全沒用,殺了它只有好處沒壞處,第一個幹它。

記憶體閾值

上面主要講的是程序,那麼程序是怎麼被殺的呢?這不得不提主要的一個原因:記憶體。在移動裝置中記憶體往往是有限的,開啟的應用越多,後臺快取的程序也越多。在系統記憶體不足的情況下,系統開始依據自身的一套程序回收機制來判斷要kill掉哪些程序。在Android的記憶體回收機制中有一個重要的概念:Low Memory Killer。
我們可以使用cat /sys/module/lowmemorykiller/parameters/minfree來檢視某個手機的記憶體閾值。
這裡寫圖片描述
注意這些數字的單位是page(1 page = 4 kb)。上面的六個數字對應的就是(MB): 72,90,108,126,144,180,這些數字也就是對應的記憶體閥值,記憶體閾值在不同的手機上不一樣,一旦低於該值,Android便開始按順序關閉程序. 因此Android開始結束優先順序最低的空程序,即當可用記憶體小於180MB(46080*4/1024)。

讀到這裡,你或許有一個疑問,假設現在記憶體不足,空程序都被殺光了,現在要殺後臺程序,但是手機中後臺程序很多,難道要一次性全部都清理掉?當然不是的,程序是有它的優先順序的,這個優先順序通過程序的adj值來反映,它是linux核心分配給每個系統程序的一個值,代表程序的優先順序,程序回收機制就是根據這個優先順序來決定是否進行回收,adj值定義在com.android.server.am.ProcessList類中,這個類路徑是${android-sdk-path}\sources\android-23\com\android\server\am\ProcessList.java。oom_adj的值越小,程序的優先順序越高,普通程序oom_adj值是大於等於0的,而系統程序oom_adj的值是小於0的,我們可以通過cat /proc/程序id/oom_adj可以看到當前程序的adj值。

這裡寫圖片描述

看到adj值是0,0就代表這個程序是屬於前臺程序,我們再按下Back鍵,將應用至於後臺,再次檢視。
這裡寫圖片描述
adj值變成了8,8代表這個程序是屬於不活躍的程序。關於oom_adj程序的相關內容可以參考下表:

adj級別 解釋
UNKNOWN_ADJ 16 預留的最低級別,一般對於快取的程序才有可能設定成這個級別
CACHED_APP_MAX_ADJ 15 快取程序,空程序,在記憶體不足的情況下就會優先被kill
CACHED_APP_MIN_ADJ 9 快取程序,也就是空程序
SERVICE_B_ADJ 8 不活躍的程序
PREVIOUS_APP_ADJ 7 切換程序
HOME_APP_ADJ 6 與Home互動的程序
SERVICE_ADJ 5 有Service的程序
HEAVY_WEIGHT_APP_ADJ 4 高權重程序
BACKUP_APP_ADJ 3 正在備份的程序
PERCEPTIBLE_APP_ADJ 2 可感知的程序,比如那種播放音樂
VISIBLE_APP_ADJ 1 可見程序,如當前的Activity
FOREGROUND_APP_ADJ 0 前臺程序
PERSISTENT_SERVICE_ADJ -11 重要程序
PERSISTENT_PROC_ADJ -12 核心程序
SYSTEM_ADJ -16 系統程序
NATIVE_ADJ -17 系統起的Native程序

說明:上表的數字可能在不同系統會有一定的出入。

下面按照網路保活和程序保活來給大家介紹保活的一些策略。

網路連線保活

網路保活,業界主要手段有:
a. GCM;
b. 公共的第三方push通道(信鴿等);
c. 自身跟伺服器通過輪詢,或者長連線;

GCM即Google Cloud Messaging,主要用於訊息推送的,即使在應用沒有起來的情況下,客戶端也能通過GCM收到來自伺服器的訊息。GCM支援Android、IOS和Chrome。由於GCM需要google service支援,在國內基本不能用,經常會斷線。

push很多也是基於長連線實現的,早年的微信,直接通過Java socket 實現。所以後面我們直接談長連線。
長連線實現包括幾個要素:
a. 網路切換或者初始化時 server ip 的獲取。
b. 連線前的 ip篩選,出錯後ip 的拋棄。
c. 維護長連線的心跳。
d. 伺服器通過長連notify。
e. 選擇使用長連通道的業務。
f. 斷開後重連的策略。

今天,我們討論重點即時聊天中的心跳和 notify 機制。

1,心跳機制

通過定期的資料包,對抗NAT超時(一般會設定為5-10秒)。以下是部分地區網路NAT 超時統計。
這裡寫圖片描述

心跳的實現過程如下:
這裡寫圖片描述

說明:
a. 連線後主動到伺服器Sync拉取一次資料,確保連線過程的新訊息。
b. 心跳週期的Alarm 喚醒後,一般有幾秒的cpu 時間,無需wakelock。
c. 心跳後的Alarm防止傳送超時,如伺服器正常回包,該Alarm 取消。
d. 如果伺服器回包,系統通過網路喚醒,無需wakelock。

流程基於兩個系統特性:

a. Alarm喚醒後,足夠cpu時間發包。
b. 網路回包可喚醒機器。

特別是b項,假如Android封堵該特性,那就只能用GCM了。API level >= 23的doze就關閉所有的網路, alarm等。Google也最終在6.0版本加入REQUEST_IGNORE_BATTERY_OPTIMIZATIONS許可權。

2,動態心跳

4.5min心跳週期是穩定可靠的,但無法確定是最大值。通過終端的嘗試,可以獲取到特定使用者網路下,心跳的最大值。引入該特性的背景:
a. 運營商的信令風暴
b. 運營商網路換代,NAT超時趨於增大
c. Alarm耗電,心跳耗流量。

動態心跳引入下列狀態:
a. 前臺活躍態:亮屏,微信在前臺, 週期minHeart (4.5min) ,保證體驗。
b. 後臺活躍態:微信在後臺10分鐘內,週期minHeart ,保證體驗。
c. 自適應計算態:步增心跳,嘗試獲取最大心跳週期(sucHeart)。
d. 後臺穩定態:通過最大週期,保持穩定心跳。

下面是自適應計算態流程:
這裡寫圖片描述
在自適應態:
a. curHeart初始值為minHeart , 步增(heartStep)為1分鐘。
b. curHeart 失敗5次, 意味著整個自適應態最多隻有5分鐘無法接收訊息。
c. 結束後,如果sucHeart > minHeart,會減去10s(避開臨界),為該網路下的穩定週期。
d. 進入穩定態時,要求連線連續三次成功minHeart心跳週期,再使用sucHeart。

3,notify機制

網路保活的意義在於訊息實時。通過長連線,即時通訊類產品有下列機制保證訊息的實時。

Sync

通過Sync CGI直接請求後臺資料。Sync 通過後臺和終端的seq值對比,判斷該下發哪些訊息。終端正常處理訊息後,seq更新為最新值。
Sync 的主要場景:
a. 長連無法建立時,通過Sync 定期輪詢;
b. 微信切到前臺時,觸發Sync(保命機制);
c. 長連建立完成,立即觸發Sync,防止連線過程漏訊息;
d. 接收到Notify 或者 gcm 後,終端觸發Sync 接收訊息。

Notify:

類似於GCM。通過長連線,後臺發出僅帶seq的小包,終端根據seq決定是否觸發Sync拉取訊息。

NotifyData:

在長連穩定, Notify機制正常的情況下(保證seq的同步)。後臺直接推送訊息內容,節省1個RTT (Sync) 訊息接收時間。終端收到內容後,帶上seq迴應NotifyAck,確認成功。這裡會出現Notify和NotifyData狀態互相切換的情況:

如NotifyData 後,伺服器在沒收到NotifyAck,而有新訊息的情況下,會切換回到Notify,Sync可能需要冗餘之前NotifyData的訊息。終端要保證序列處理NotifyData和Sync ,否則seq可能回退。

GCM:

只要機器上有GMS ,啟動時就嘗試註冊GCM,並通知後臺。伺服器會根據終端是否保持長連,決定是否由GCM通知。GCM主要針對國外比較複雜的網路環境。

程序保活

在Android系統裡,程序被殺的原因通常為以下幾個方面:
a. 應用Crash;
b. 系統回收記憶體;
c. 使用者觸發;
d. 第三方root許可權app。

下面分享幾個微信和qq關於程序保活的幾個方法:

1,程序拆分

俗話說,雞蛋不能放一個籃子裡面,那麼為了保活,我們也可以將程序拆分為幾個。
這裡寫圖片描述

例如,上圖是微信應用的幾個程序:
a. push主要用於網路互動,沒有UI
b. worker就是使用者看到的主要UI
c. tools主要包含gallery和webview
這樣,程序通過拆分之後,單個程序被回收了並不影響其他的程序。拆分網路程序,確實就是為了減少程序回收帶來的網路斷開。
這裡寫圖片描述
可以看到push的記憶體要遠遠小於worker。而且push的工作性質穩定,記憶體增長會非常少。這樣就可以保證,儘量的減少push 被殺的可能。為了提高執行緒存活的概率,這裡啟動一個純C/C++ 的程序,而不是Java run time。

2,及時拉起

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

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

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

3,程序優先順序

前面說過Low Memory Killer機制,Low Memory Killer 機制決定是否殺程序除了記憶體大小,還有程序優先順序。這個前面也說過。從這個原理來說,我們可以通過提高程序的優先順序來保活。

值得注意的是,Android 的前臺service機制。但該機制的缺陷是通知欄保留了圖示。

對於 API level < 18 :呼叫startForeground(ID, new Notification()),傳送空的Notification ,圖示則不會顯示。

對於 API level >= 18:在需要提優先順序的service A啟動一個InnerService,兩個服務同時startForeground,且繫結同樣的 ID。Stop 掉InnerService ,這樣通知欄圖示即被移除。

相關推薦

Android客戶程序

在很多移動應用中,特別是即時通訊類專案中,保活是一個永遠無法避免的一個話題。保活,按照我的理解,主要包含兩部分: 網路連線保活:如何保證訊息接收實時性。 程序保活:儘量保證應用的程序不被Android系統回收。 在很早以前,談Android的保活都會涉及到

Redirect 客戶重定向 和Dispatch 伺服器重定向

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android中的程序(不死程序

Android中的程序保活方式主要分為以下三種: 黑色保活:不同的app程序,用廣播相互喚醒(包括利用系統提供的廣播進行喚醒) 白色保活:啟動前臺Service 灰色保活:利用系統的漏洞啟動前臺Service 黑色保活 利用不同的app程序使用廣播來進

android程序兩年實戰經驗(已經上線使用)

程序保活參考: https://www.jianshu.com/p/53c4d8303e19 https://github.com/08carmelo/android-keeplive 以上地址這個我是通過鴻洋的公眾號看到的,我們公司做的是VPN撥號必須要求app保持後臺執行,上面連

Android程序相關實踐

最近測試APP時接到個需求:1畫素保活 打眼一看,應該跟安卓程序有關係,索性找點詳細的資料來了解下: 系統什麼時候殺掉一個程序?為什麼殺掉這個程序?怎樣最大程度保活一個程序?詳細的講解可以參考這個連結------https://www.cnblogs.com/Doing-what-I-love/p/553

Android 程序資料

現在發現App在後臺執行越來越難了。App在華為手機後臺死的非常快,之前看網上說華為有白名單,網上也通過改包名的方式來驗證了。但是半信半疑的,直到諮詢了華為的客服給了一個郵箱 [email protected],回覆的資料 應用加白名單簡化流程v0.1

【騰訊Bugly乾貨分享】Android程序招式大全

【騰訊Bugly乾貨分享】Android程序保活招式大全 本文來自於騰訊bugly開發者社群,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/57ac4a0ea374c75371c08ce8 作者:騰訊——張興華 目前市面上的應用,貌似除了微信和手Q都會

Android程序招數概覽

Android中的程序保活應該分為兩個方面: 提高程序的優先順序,減少被系統殺死的可能性 在程序已經被殺死的情況下,通過一些手段來重新啟動應用程序 本文針對這兩方面來程序闡述,並給出相應的示例。其實主要也是在前人的基礎上做了一個總結,並進行了一些實踐。 閱讀本

android程序實戰(已經上線使用)

https://github.com/08carmelo/android-keeplive 這個我是通過鴻洋的公眾號看到的,我們公司做的是vpn撥號必須要求app保持後臺執行,上面介紹的很詳細,我用的github中使用的程式碼 這個具體需要你自己看 我使用後其實還是有

Android程序招式大全

作者:騰訊——張興華目前市面上的應用,貌似除了微信和手Q都會比較擔心被使用者或者系統(廠商)殺死問題。本文對 Android 程序拉活進行一個總結。Android 程序拉活包括兩個層面:A. 提供程序優先順序,降低程序被殺死的概率B. 在程序被殺死後,進行拉活本文下面就從這兩

android程序實踐(根據鴻洋大神彙總,本人忘性大備份下)

前言程序保活的關鍵點有兩個,一個是程序優先順序的理解,優先順序越高存活機率越大。二是弄清楚哪些場景會導致程序會kill,然後採取下面的策略對各種場景進行優化:提高程序的優先順序在程序被kill之後能夠喚醒程序優先順序Android一般的程序優先順序劃分:1.前臺程序 (Foreground process)2

Android 程序資料彙總與華為白名單那些事

現在發現App在後臺執行越來越難了。App在華為手機後臺死的非常快,之前看網上說華為有白名單,網上也通過改包名的方式來驗證了。但是半信半疑的,直到諮詢了華為的客服給了一個郵箱 [email protected],回覆的資料 應用加白名單簡化流程v0.1

Android程序(常駐記憶體)

Android將程序分為6個等級,它們按優先順序順序由高到低依次是:  1.前臺程序( FOREGROUND_APP);  2.可視程序(VISIBLE_APP );  3. 次要服務程序(SECONDARY_SERVER );  4.後臺程序 (HIDDEN_APP);  

Android程序精煉詳解

一、前期基礎知識儲備在之前的文章《如何保證Service在後臺不被殺死?》中,筆者分析了為什麼要保活Service、Service的幾種保活方法和Service保活的意義。今天的這篇文章就更進一步,講解程序保活的方法和意義。(1)什麼是程序保活?拿我們的手機應用程式QQ來說,

Android 5.0 以下Native程序嘗試

最近博主嘗試了Android 5.0 以下版本的Native保活機制,感覺收穫頗豐,在此寫下一篇部落格記錄一下。 首先把整個保活流程通過圖片的形式描述下: 首先是AndroidManifest 中註冊的控制元件: <activity androi

如何讓我們的Android應用程序

一、執行緒 程序 應用 預設下,同一個應用的所有元件都執行在同一個程序中。當然也可以在manifest清單檔案中設定元件執行的程序。 元件元素 activity、service、receiver、provider,都有一個process屬性可以指定元

Android之D面試題②程序的一般套路(1畫素Activity/賬號同步/Jobscheduler/系統服務捆綁)

       讀到這裡,你或許有一個疑問,假設現在記憶體不足,空程序都被殺光了,現在要殺後臺程序,但是手機中後臺程序很多,難道要一次性全部都清理掉?當然不是的,程序是有它的優先順序的,這個優先順序通過程序的adj值來反映,它是linux核心分配給每個系統程序的一個值,代表程序的優先順序,程序回收機制就是根據這

Android程序(最新)帶你淺析這幾種可行性的方案

1.概述   據前人驗證,在沒有白名單的情況下,安卓系統要做一個任何情況下都不被殺死的應用是基本不可能的,但是我們可以做到應用基本不被殺死,如果殺死可以立即復活.經過上網查詢,程序常駐的方案眾說紛紜,但是很多的方案都是不靠譜的或不是最好的,結合很多資料,今天總結一下And

android程序

有些時候(國內通常是這樣)我們需要應用在 後臺存活,但是現在的很多手機room是在記憶體不足或者一定時間後銷燬程序的。 總結幾種android常用的保活手段。 系統出於體驗和效能上的考慮,app在退到後臺時系統並不會真正的kill掉這個程序,而是將其快取起來。開啟的應用越多

關於 Android 程序,你所需要知道的一切

早前,我在知乎上回答了這樣一個問題:怎麼讓 Android 程式一直後臺執行,像 QQ 一樣不被殺死?。關於 Android 平臺的程序保活這一塊,想必是所有 Android 開發者矚目的內容之一。你到網上搜 Android 程序保活,可以搜出各種各樣神乎其技的做法,絕大多數都是極其不靠譜。前段時間,Gith