一文讀懂Android進程及TCP動態心跳保活


一直以來,APP進程保活都是 各軟件提供商個人開發者 頭疼的問題。畢竟一切的商業模式都建立在用戶對APP的使用上,因此保證APP進程的喚醒提升用戶的使用時間,便是軟件提供商和個人開發者的永恆追求。

面對國內GCM(Google Cloud Messaging)推送服務不可用,也未出現一個統一市場PUSH平台的現狀。早期的第三方軟件一般通過維持一個終端遠端服務器之間的TCP長連接,達到PUSH拉活消息及時送達的目的。
而為了維持這個TCP長連接不斷開,前提條件就是保證自己APP的后台服務進程,不會被殺死(因為只有活着的終端進程才能定期與遠端服務器通信,保證長連接不斷連)。
因此在Android發布的早期,各種技術論壇和GitHub出現了五花八門、各顯神通App進程保活方案;如今隨着Android系統的逐漸完善,各種進程保活方案不斷受到限制,想要做到Android進程保活已經不太容易。

一般來說,Android進程保活主要有以下兩方面工作:

  • 進程保活:保持進程不被系統殺死進程被殺后可以重新拉起
    早期Android,對於后台運行的Service服務進程限制較小,第三方軟件保證自己APP后台服務進程長時間運行相對容易,但仍然是有被系統殺死的可能。
    這個階段隨着APP的不斷增多,許多第三方APP開始利用系統漏洞(早期的Android系統尚不完善,可利用的漏洞較多),在APP進入后台系統准備休眠時,通過各種所謂的黑科技(實際為流氓手段)保證自己的APP進程不被殺死,甚至阻止系統休眠
    伴隨隨着這類軟件的不斷增多,最終造成的結果是,用戶側手機卡頓待機時間短耗電量增加
  • TCP保活:保證終端遠端服務器之間的TCP長連接不斷連。
    App進程保活的基礎上,一般通過使用Android系統RCT時鍾 Alarm每5~10分鍾喚醒一次系統,並發送一條只有幾個字節的TCP保活消息,來維持終端遠端服務器之間的TCP長連接不斷開。

一、Android早期進程保活

這里簡單回顧一下,Android早期App進程保活的各種方案。

  • 通過Service的onStartCommand方法中返回 START_STICKY
    當Service的onStartCommand方法返回 START_STICKY 時,若當前Service因內存不足被系統清理掉,待內存再次空閑時,系統將會嘗試重新創建此Service,一旦創建成功后將回調onStartCommand方法,但其中的Intent將是NULL。
  • 1像素透明Activity進程保活:
    監測到Android系統息屏時,啟動一個1像素的前台透明Activity,欺騙系統,使其認為該應用為一個前台應用而不會被清理(據說淘寶早期就使用過這種方案)。
  • 開啟前台服務:
    利用系統漏洞,創建一個 不包含系統通知欄透明系統通知欄前台服務,提升App進程的系統優先級,使其不被系統清理。
  • Fork Native守護進程:
    Android 5.0 以前,App內部 fork 出來的 native進程 不受系統管控。系統在殺死 App進程時,只會殺死對應的Java進程
    因此誕生了一大批流氓軟件,通過fork native進程,在 App 的 Java 進程被殺死的時候,通過 ActivityManager 重新拉起被殺死的進程,從而達到“進程永生”的目的(這個方案據說最初由360提出,后來大家紛紛效仿)。
  • 通過其他活躍App拉起:
    進程保活的后期,又出現一種進程保活的方案。
    多個App組成一個聯盟,只要有一個App被用戶使用,其他所有聯盟內的App進程都會被拉起,以此來保證消息的及時到達。
    例如:集成個推、極光推送的App,可以通過前台活躍的App拉起不活躍的App進程,提升所有集成個推、極光推送的第三方App推送消息的到達率。

注:
當前隨着Android系統的不斷完善,以上方案大多不再有效。

二、Android各版本后台進程限制

Android系統源碼的開放,加之早期的系統尚不完善,眾多應用軟件提供商通過對AOSP各版本源碼的深入解讀,提出了各種所謂黑科技的保活方案,保證自己的應用程序進程不被系統清理。這段時期可謂是魑魅橫行、群魔亂舞;Android手機用戶則是苦不堪言,Android手機卡頓、待機時間短等問題也為人所詬病。

隨着Android系統的不斷完善,Google和國內終端手機廠商也對Android系統做了很多的改進,如今已經基本封死了第三方應用各種所謂黑科技流氓保活方案。

  • Android后台進程限制;
  • 國內手機廠商后台進程限制;

2.1 Android后台進程限制

如今Google每年發布的AOSP(Android Open Source Project)新版本中,每個版本均不斷增加后台服務進程限制的相關條款,一方面是為了限制第三方App后台服務進程的運行,節省用戶手機資源與電量消耗;另一方面,增加后台服務進程限制也是在保護用戶的隱私。

Android 5.0 開始,Android系統開始以uid為標識,查殺APP進程組,通過殺死整個進程組來殺死App進程,因此通過fork native 進程守護App進程這種方式從此不再有效。
Process.killProcessGroup(info.uid, pid)

Android 6.0開始,引入了Doze模式用戶拔下設備的電源插頭,並在屏幕關閉后的一段時間后,設備會進入Doze模式
Android 6.0 Doze模式

  • 在Doze模式下,系統會嘗試通過限制應用訪問網絡和 占用CPU資源的措施來節省電量,阻止應用訪問網絡,並延遲作業與Alarm鬧鍾。
  • 系統會定期退出Doze模式一小段時間,讓應用完成其延遲的活動。在此維護期內,系統會運行所有待處理的同步操作、Alarm鬧鍾,並允許應用訪問網絡。
  • 隨着時間的推移,系統進入維護期的次數越來越少,這有助於在設備未連接至充電器的情況下長期處於Doze模式狀態降低耗電量。

Android 7.0開始加強了Doze模式,進入Doze模式不再要求設備靜止狀態。
只要屏幕關閉了一段時間,且設備未插入電源,設備就會進入Doze模。
Android 7.0 Doze模式

Android 8.0開始,加強了應用后台執行限制:

  • 不能再通過startService創建后台服務,否則將拋出異常;但可通過 Context.startForegroundService() 方法啟動一個帶通知欄提醒的前台服務;
  • 應用處於后台時,會對后台應用檢索用戶當前位置信息的頻率進行限制。應用每小時僅接收幾次位置信息更新。
  • 廣播限制:第三方應用將無法通過在AndroidManifest注冊靜態廣播來接收大部分的系統隱式廣播,以減少App對手機的喚醒,從而節省手機的電量;動態注冊不受影響。

Android 9.0開始,進一步加強了應用后台執行限制:

  • 后台應用不再可以訪問麥克風和攝像頭,傳感器(加速度傳感器、陀螺儀);
  • 創建前台服務需要申請普通權限:FOREGROUND_SERVICE

Android 10開始,再進一步加強了應用后台執行限制:

  • 應用在后台運行時,訪問手機位置需要動態申請ACCESS_BACKGROUND_LOCATION權限,用戶則可以選擇拒絕;
  • 應用在后台運行時,對后台應用啟動Activity進行限制(運行前台服務的應用仍然會被應用看做“后台”應用)。

Android 11開始,再進一步完善了應用后台訪問位置限制:

  • 在Android11設備上,對於targetSdkVersion=30(Android 11)的應用,需先申請前台位置權限,后申請后台位置權限。
  • 在Android11設備上,對於targetSdkVersion=30(Android 11)的應用,同時申請前台、后台位置權限時,系統會忽略該請求,無任何響應(需首先獲取前台位置權限,再次申請后台位置權限)。
  • 在Android11設備上,對於targetSdkVersion<=29(Android 10)的應用,同時申請前台、后台位置權限時,對話框不再提示始終允許字樣,而是提供了位置權限的設置入口,需要用戶在設置頁面選擇始終允許才能獲得后台位置權限。

2.2 手機廠商后台進程限制

Google新發布的各版本主要還是限制第三方App后台進程服務的運行,而國內各終端廠商則在AOSP的基礎上增加了Alarm的限制。
國內手機廠商(華米OV等)在手機系統休眠后,第三方App注冊的Alarm定時喚醒鬧鍾幾乎全部無效!!!

“Google的后台進程限制” 與 “國內手機廠商Alarm限制”相疊加的雙重影響是:
徹底封死了 “第三方手機軟件” 利用 “Alarm鬧鍾” 定時喚醒手機系統,維持 “終端” 與 “遠端服務器” 之間的TCP長連接。達到 遠端服務器 可隨時拉起 終端App,提升APP用戶端使用時長(提升APP的DAU)這一方案。

  • 國內手機廠商 Alarm 限制;
  • 國內手機廠商 后台進程 限制;

2.2.1 手機廠商 Alarm 限制

國內終端手機廠商,不同廠商對應的Alarm限制方案也不相同,但目前已知的大概有以下兩個方向:

  • Alarm 對齊機制:
    當手機系統黑屏休眠時,部分國內的手機品牌,會忽略 “第三方APP” 設置的 “Alarm喚醒周期”,強制將所有注冊Alarm鬧鍾的APP,做Alarm喚醒周期對齊
    例如:假設有三款App,其設定的Alarm喚醒周期分別為1分鍾、3分鍾、5分鍾。手機會直接忽略以上三款App的Alarm喚醒周期設定,強制將Alarm喚醒對齊為每5分鍾喚醒一次。
    系統休眠時,將所有App的喚醒時間做對齊,來減少手機的喚醒次數,節省用戶的待機電量消耗
  • Alarm 無效:
    當手機系統黑屏休眠時,部分國內的手機品牌,忽略 “第三方APP” 注冊的全部Alarm,導致第三方應用注冊的Alarm無效。

2.2.2 手機廠商 后台進程 限制

除了以上Alarm限制外,對於后台運行進程,不同終端廠商也進行了不同的限制。例如,部分終端廠商增加了后台進程耗電監測機制。

  • 后台進程耗電監測機制:
    當手機系統黑屏休眠時,部分國內的手機品牌,會啟動一個耗電監測進程,若發現某個后台進程持續耗電,將直接殺死耗電進程。

三、手機廠商進程白名單

前邊說道第三方App進程若在后台運行,會受到國內廠商Alarm限制、后台耗電監測等限制。但也有例外情況,比如:微信

這里可以將其歸結為以下兩個白名單:

  • Alarm 對齊白名單
    微信 這個體量的App 會被直接添加到,國內手機廠商的Alarm 對齊白名單中。白名單中的進程,系統休眠時的Alarm喚醒將不再受到限制。
  • 系統守護進程白名單
    若能達到微信的體量,國內手機廠商甚至可能會將其添加到 系統守護進程白名單。該白名單中的進程,若某些原因后台進程被殺,系統守護進程會在一定時間內迅速拉起該進程,從而保證進程的活躍。

四、Push推送 建議方案

前邊回顧了這幾年在進程保活這個問題上各軟件提供商Android系統研發制造商之間互相博弈過程。

如今在“Google的后台進程限制” 與 “國內手機廠商Alarm限制”雙重限制下,第三方軟件希望做到App進程常駐后台已經不太可能。

但每一個第三方App,基本都存在消息Push的需求。對於消息PUSH需求,第三方APP可使用的方案是什么?

  • 接入終端手機廠商 Push通道;
  • 進程添加到廠商進程保活白名單;

4.1 接入廠商 Push通道

在當前環境下的建議實現方案:接入終端手機廠商Push通道。

接入各終端廠商的Push通道,是最簡單的實現方案。
廠商的Push通道常駐內存,所有第三方App接入廠商Push后 即能保證消息及時准確的到達,又可以減少終端用戶手機的常駐進程,延長用戶手機的待機時長,可以說是對雙方都有利。

  • 接入廠商PUSH 終端側實現方案;
  • 接入廠商Push的 到達率上的注意點。

4.1.1 接入廠商PUSH 終端方案

接入廠商PUSH 終端方案,終端側可通過創建與維護一個單獨的推送 Module 模塊,其中集成 華為、小米、VIVO、OPPO、中興 5家廠商的Push SDK;其他終端廠商的Push到達,可通過接入 個推極光推送來實現。

  • 華米OV 等頭部手機廠商:
    通過接入華米OV等頭部廠商PUSH通道,保證市場上大多數手機用戶的PUSH到達率;
  • 其他非頭部手機廠商:
    通過極光個推等第三方市場占有率較高的PUSH實現方案,來覆蓋除 華米OV 等頭部手機廠商外的其他終端手機,保證市場上少部分手機用戶的PUSH到達率;

采用這個方案,研發人員需要開發和維護6個Push SDK組成的Module模塊。因此,接入廠商PUSH,優點和缺點可歸結如下。

  • 優點可以很好的保證 PUSH到達率;
  • 缺點開發與維護成本增加。
    國內頭部手機終端廠商較多,研發的同學在一個App中需要同時維護 華為、小米、VIVO、OPPO、中興、魅族 等一系列廠商的PUSH SDK,研發維護成本急劇增加。

4.1.2 接入廠商Push的注意點

  • 接入廠商Push的注意點;
  • 將來終端手機廠商 PUSH渠道 可能收費。

接入廠商Push的注意點
接入廠商PUSH,在PUSH到達上仍有不同的限制條件,這一點也需要相關研發人員仔細研究各終端廠商PUSH的接入文檔
例如OPPO,存在一個PUSH配額問題:
OPPO PUSH配額是 OPPO推送消息的數量限制規則。每天的Push量 超過這個PUSH配額,OPPO將不再下發Push。
OPPO PUSH配額官方描述:
https://open.oppomobile.com/wiki/doc#id=10200
OPPO PUSH配額官方描述如下:
OPPO PUSH配額

將來終端手機廠商 PUSH渠道 可能收費。
目前終端手機廠商的PUSH渠道均是免費為開發者使用的,但隨着全市場的PUSH通道均為各終端廠商把控后,第三方APP的消息PUSH也許存在收費的可能。個人認為廠商PUSH渠道 收費 在不久的將來可能性還是很大的。

4.2 添加到廠商進程保活白名單

終端手機廠商追求的還是利益,因此只要給錢或有錢賺,沒什么不可談的

與國內各終端廠商談合作 添加到對應的進程白名單中。

這個方案,需要與各終端廠商合作 達成利益上的同盟或找到利益契合點,從而使終端廠商為您的App開放進程保活白名單。

您的公司與各終端廠商達成了利益同盟,恭喜您可以考慮采用維持一個終端與遠端服務器的TCP長連接,實現消息及時到達終端,提升用戶端App使用時長的PUSH方案了。

五、TCP 動態心跳方案

前邊提到,若APP用戶體量足夠大與各終端廠商達成了利益同盟,可以考慮維持一個終端與遠端服務器的TCP長連接,實現消息及時到達終端,提升用戶端App使用時長。

  • TCP心跳 相關標准;
  • 維持 TCP心跳 不斷連;
  • TCP動態心跳 方案。

5.1 TCP心跳 相關標准

在Android下,通過自建 TCP長連接 來進行Push消息推送,TCP長連接若存活,消息Push才能及時送達。
而說到TCP心跳,那什么是TCP心跳,TCP消息的結構又是什么?
rfc5626 4.4.1 Keep-Alive with CRLF標准中,關於SIP消息的TCP心跳給出了標准。

  • Keep-Alive with CRLF;
  • CRLF 詳細說明;
  • ping pong 心跳消息。

5.1.1 Keep-Alive with CRLF

rfc5626 4.4.1 Keep-Alive with CRLF中,SIP消息TCP心跳標准可以如下描述:

  • 終端側需每隔一段時間(心跳間隔時間)需發送一個“ping”(double CRLF)消息,到遠端服務器側;
  • 終端發送“ping”消息后,若在10s之內未收到遠端服務端的“pong”(CRLF)消息,則終端認為與服務端的連接失敗。

rfc5626 TCP ping pong保活

5.1.2 CRLF 詳細說明

根據rfc5626 4.4.1 Keep-Alive with CRLF標准:

  • 終端上行的ping消息為 CRLFCRLF;
  • 遠端服務器下行的pong消息為 CRLF;

pong消息為CRLF ,含義是 回車+換行 符;
ping消息為double CRLF,也就是CRLFCRLF 含義是 回車換行回車換行 符。

CRLF 在ASCII表中與 16進制數據 的對應關系,如下圖所示:

CRLF為回車換行符

ASCII的對應表中查看:

  • ping心跳消息CRLFCRLF,對應的16進制數據為0d0a0d0a
  • pong心跳消息CRLF,對應的16進制數據為0d0a
心跳 含義 縮寫 十六進制
ping 終端上行心跳數據 CRLFCRLF 0d0a0d0a
pong 遠端服務器下行心跳數據 CRLF 0d0a

5.1.3 ping pong 心跳消息

這一節關於 ping pong心跳消息,從其消息發送接收流程、WireShark現網數據抓包、消息結構舉例 三方面進行介紹。

  • ping pong 心跳流程;
  • ping pong 心跳WireShark抓包;
  • ping pong 心跳消息舉例。

ping pong 心跳流程:

ping pong 心跳消息的發送/接收流程,如下圖所示:
ping pong心跳發送接收流程

  • 終端側需每隔一段時間(心跳間隔時間)需發送一個“ping”(double CRLF 0xd0xa0xd0xa)消息,到遠端服務器側;
  • 終端發送“ping”消息后,若在10s之內未收到遠端服務端的“pong”(CRLF 0xd0xa)消息,則終端認為與服務端的連接失敗。

ping pong 心跳WireShark抓包:

ping pong 心跳WireShark抓包如下圖所示:

ping pong

ping pong 心跳消息舉例:

ping pong 心跳消息的舉例如下所示:

// 終端側發送: TCP Ping:0d 0a 0d 0a

Frame 2427: 60 bytes on wire (480 bits), 60 bytes captured (480 bits)
Linux cooked capture v1
Internet Protocol Version 4, Src: 10.xxx.xxx.xxx, Dst: 183.xxx.xxx.xxx
Transmission Control Protocol, Src Port: 46649, Dst Port: 5460, Seq: 21, Ack: 11, Len: 4
    Source Port: 46649
    Destination Port: 5460
    [Stream index: 3]
    [TCP Segment Len: 4]
    Sequence Number: 21    (relative sequence number)
    Sequence Number (raw): 149531750
    [Next Sequence Number: 25    (relative sequence number)]
    Acknowledgment Number: 11    (relative ack number)
    Acknowledgment number (raw): 3481893389
    0101 .... = Header Length: 20 bytes (5)
    Flags: 0x018 (PSH, ACK)
    Window: 65535
    [Calculated window size: 65535]
    [Window size scaling factor: -1 (unknown)]
    Checksum: 0x727d [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    [SEQ/ACK analysis]
    [Timestamps]
    TCP payload (4 bytes)
Data (4 bytes)
    Data: 0d0a0d0a
    [Length: 4]


// 終端側接收:TCP ACK

Frame 2432: 56 bytes on wire (448 bits), 56 bytes captured (448 bits)
Linux cooked capture v1
Internet Protocol Version 4, Src: 183.xxx.xxx.xxx, Dst: 10.xxx.xxx.xxx
Transmission Control Protocol, Src Port: 5460, Dst Port: 46649, Seq: 11, Ack: 25, Len: 0
    Source Port: 5460
    Destination Port: 46649
    [Stream index: 3]
    [TCP Segment Len: 0]
    Sequence Number: 11    (relative sequence number)
    Sequence Number (raw): 3481893389
    [Next Sequence Number: 11    (relative sequence number)]
    Acknowledgment Number: 25    (relative ack number)
    Acknowledgment number (raw): 149531754
    0101 .... = Header Length: 20 bytes (5)
    Flags: 0x010 (ACK)
    Window: 21476
    [Calculated window size: 21476]
    [Window size scaling factor: -1 (unknown)]
    Checksum: 0x7279 [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    [SEQ/ACK analysis]
    [Timestamps]

// 終端側接收:TCP Pong:0d 0a

Frame 2433: 58 bytes on wire (464 bits), 58 bytes captured (464 bits)
Linux cooked capture v1
Internet Protocol Version 4, Src: 183.xxx.xxx.xxx, Dst: 10.xxx.xxx.xxx
Transmission Control Protocol, Src Port: 5460, Dst Port: 46649, Seq: 11, Ack: 25, Len: 2
    Source Port: 5460
    Destination Port: 46649
    [Stream index: 3]
    [TCP Segment Len: 2]
    Sequence Number: 11    (relative sequence number)
    Sequence Number (raw): 3481893389
    [Next Sequence Number: 13    (relative sequence number)]
    Acknowledgment Number: 25    (relative ack number)
    Acknowledgment number (raw): 149531754
    0101 .... = Header Length: 20 bytes (5)
    Flags: 0x018 (PSH, ACK)
    Window: 21476
    [Calculated window size: 21476]
    [Window size scaling factor: -1 (unknown)]
    Checksum: 0x727b [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    [SEQ/ACK analysis]
    [Timestamps]
    TCP payload (2 bytes)
Data (2 bytes)
    Data: 0d0a
    [Length: 2]

// 終端側發送:TCP ACK

Frame 2434: 56 bytes on wire (448 bits), 56 bytes captured (448 bits)
Linux cooked capture v1
Internet Protocol Version 4, Src: 10.xxx.xxx.xxx, Dst: 183.xxx.xxx.xxx
Transmission Control Protocol, Src Port: 46649, Dst Port: 5460, Seq: 25, Ack: 13, Len: 0
    Source Port: 46649
    Destination Port: 5460
    [Stream index: 3]
    [TCP Segment Len: 0]
    Sequence Number: 25    (relative sequence number)
    Sequence Number (raw): 149531754
    [Next Sequence Number: 25    (relative sequence number)]
    Acknowledgment Number: 13    (relative ack number)
    Acknowledgment number (raw): 3481893391
    0101 .... = Header Length: 20 bytes (5)
    Flags: 0x010 (ACK)
    Window: 65535
    [Calculated window size: 65535]
    [Window size scaling factor: -1 (unknown)]
    Checksum: 0x7279 [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    [SEQ/ACK analysis]
    [Timestamps]


5.2 維持 TCP心跳 不斷連

TCP長連接的存活且有效,終端才能維持與遠端服務器的TCP心跳,保證消息及時准確的到達。
因此影響TCP長連接穩定狀態的因素,值得研發人員重點關注:

  • 運營商 NAT超時;
  • 終端 網絡狀態變化。

5.2.1 運營商 NAT超時

NAT(Network Address Translation) 是運營商的一個地址轉換網關。生活中最常見的NAT設備,是我們家中使用的路由器

國內運行商網絡下,因為 IP v4 的 IP 數量有限,運營商分配給手機終端的 IP 是運營商內網的 IP,手機要連接 Internet,需要通過運營商的網關做一個網絡地址轉換(Network Address Translation,NAT)。

NAT 功能由圖中的 GGSN 模塊實現

簡單的說運營商的網關需要維護一個外網 IP、端口到內網 IP、端口的對應關系,以確保內網的手機可以跟 Internet 的服務器通訊。

內網地址 外網地址
192.168.0.3:8888 120.132.92.21:9202
192.168.0.2:5566 120.132.92.21:9200

如上表所示:

  • NAT設備會根據NAT表對 發送 和 接收 的數據做修改, 比如將192.168.0.3:8888發出去的數據封包改成120.132.92.21:9202,外部就認為他們是在和120.132.92.21:9202通信。
  • NAT設備會將120.132.92.21:9202收到的封包數據 IP和端口改成192.168.0.3:8888 再發給內網的主機,這樣內部和外部就能互相通信了。
  • 但如果192.168.0.3:8888 == 120.132.92.21:9202這一映射因為某些原因被NAT設備淘汰了,那么外部設備就無法與192.168.0.3:8888通信了。

為了節省資源,大部分國內移動網絡運營商 在鏈路一段時間沒有數據通訊時,會淘汰 NAT 表中的對應項,造成通信雙方鏈路的中斷。
因此,為了應對運營商NAT超時,終端每隔一段時間遠端服務器 發送一個 TCP保活消息,也就是TCP保活心跳

以上也就是為什么終端與遠端服務器,需要維持TCP心跳的原因。

5.2.2 終端 網絡狀態變化

終端網絡狀態變化,也會使TCP長連接斷連,比如:終端移動網絡與WIFI網絡切換、網絡斷開與重新連 等
因此,終端 APP 需監聽相應網絡狀態變化事件:若發現終端網絡狀態發生變化,需重新建立TCP長連接

5.3 TCP 動態心跳方案

  • 跟隨手機狀態變化 調整心跳狀態;
  • TCP 動態心跳周期的計算;
  • 冗余心跳。

5.3.1 動態調整 心跳狀態

這里我們可以將手機不同狀態划進行划分,比如:
應用處於前台時為活躍態,剛剛進入后台或息屏時為次活躍態,應用進入后台一段時間后為后台狀態,手機斷網或關機后為IDEL狀態。
可根據用戶手機不同狀態變化,動態調整應用的TCP心跳周期。

  • 若應用處於前台活躍態:固定心跳。
    當應用處於前台活躍狀態時,為了保證消息及時准確的達到,使用固定心跳,保證用戶體驗。
  • 應用剛進入后台(或息屏)的次活躍態:使用幾次固定心跳。
    應用進入后台(或息屏)時,先用幾次固定跳維持長鏈接,保證用戶剛剛息屏或應用剛剛進入后台,消息的及時與准確到達,然后進入后台自適應心跳計算。
  • 應用進入后台(或息屏)一段時間后:使用動態心跳。
    應用進入后台(或息屏)一段時間后,采用動態心跳,減少因心跳引起的空中信道資源消耗,以及因心跳引起的終端喚醒與電量消耗。
  • 用戶切換網絡或重新聯網:重新建立TCP長連接。
    用戶斷網后,直接進入IDEL狀態,此時需檢測用戶網絡狀態變化,若用戶聯網后,則重建TCP長連接;
    用戶切換網絡狀態(WIFI、移動網絡互相切換),也需要重新建立TCP連接;
  • 應用重新進入前台活躍態:固定心跳。
    應用重新進入前台活躍狀態,重新采用固定心跳,保證用戶消息及時准確到達。

根據用戶手機不同狀態變化 調整應用的TCP心跳周期

注:應用進入后台(或者息屏)時,先用幾次最小心跳維持長鏈接,然后采用動態心跳。這樣做的目的是 盡量選擇用戶不活躍的時間段,來減少因動態心跳可能產生的 消息送達不及時,從而對用戶體驗產生影響。

5.3.2 動態計算 心跳周期

動態計算心跳前,假設預定義定義幾個數據常量:

  • MinHeart 最小心跳間隔時間;
  • MaxHeart 最大心跳間隔時間;
  • HeartStep 心跳增加時間步長;

預定義幾個數據變量:

  • successHeart 動態探測 穩定后的心跳間隔時間;
  • curHeart 當前成功心跳 初始為MinHeart;

注:
MinHeart、MaxHeart、HeartStep為預先定義的固定數據常量。
successHeart、curHeart為我們要探測的數據變量。

TCP 動態心跳計算

如上圖所示:

  • 應用進入后台(或者息屏)時,先用最小心跳間隔 MinHeart 維持心跳長鏈接;
  • 若連續三次MinHeart心跳均成功,則認為下一次相同時間間隔的心跳大概率也會成功;此時下次心跳,可以嘗試增加一次心跳步進HeartStep;
  • 增加TCP心跳步進后,再次進行三次心跳探測,若連續三次均成功,則繼續增加HeartStep步進,向上探測;
  • 若出現失敗,則同一個curHeart累計出現3次失敗時,則認為這個時間為NAT超時時間;
    同一個 curHeart 需要累計3次失敗,才認定為失敗,是為了排除用戶處於弱網環境的情況下,比如地鐵快速行進中。
  • 同一個 curHeart 累計3次失敗時,則認為找到了NAT超時時間。
    下次心跳使用 successHeart = curHeart- HeartStep 作為心跳時間周期。
  • 使用 successHeart 作為心跳周期,若連續成功,則一直使用 successHeart 作為心跳周期;
  • 使用 successHeart 作為心跳周期,若連續出現3次失敗,則認為探測數據失敗,或者用戶更換了網絡環境,需重新探測。

5.3.2 冗余心跳

在用戶對手機的的一些主動操作時,需增加冗余心跳,確保及時收到消息。

  • 當用戶點亮屏幕(或熄滅屏幕)時,立刻做一次心跳;
  • 當應用切換到前台(或切換到后台)時,立刻做一次心跳;
  • 當用戶切換網絡狀態時,重新建立TCP長連接;

六、參考:

1像素Activity進程保活:
https://blog.csdn.net/zhenufo/article/details/79317068

Optimize for Doze and App Standby:
https://developer.android.google.cn/training/monitoring-device-state/doze-standby

Android 7.0 行為變更:
https://developer.android.google.cn/about/versions/nougat/android-7.0?hl=zh-cn

Android 8.0 行為變更:
https://developer.android.google.cn/about/versions/oreo/android-8.0-changes?hl=zh-cn

Android 9.0 行為變更:
https://developer.android.google.cn/about/versions/pie/android-9.0-changes-28?hl=zh-cn

Android10 行為變更:
https://developer.android.google.cn/about/versions/10/privacy?hl=zh-cn

Android11 行為變更:
https://developer.android.google.cn/about/versions/11/behavior-changes-all?hl=zh-cn

IPV6部署之后 是否還會大量使用NAT?
https://www.zhihu.com/question/27316663

Using Native IPv6 via Comcast in San Francisco:
https://blog.kylemanna.com/ipv6/using-native-ipv6-via-comcast-in-san-francisco/

rfc5626 SIP TCP心跳:
https://datatracker.ietf.org/doc/html/rfc5626#section-4.4.1

TCP Keep-Alive 和 應用層探活:
https://www.jianshu.com/p/00aec37b6be8

WebSocket細節 長連接保活及其原理:
https://baijiahao.baidu.com/s?id=1661934194124740212&wfr=spider&for=pc

2020年Android最新保活實現原理揭秘:
https://cloud.tencent.com/developer/news/585273

Andoird TCP通訊:
https://www.cnblogs.com/duwenqidu/p/12361811.html

關於TCP長連接、NAT超時、心跳包
https://www.cnblogs.com/sjjg/p/5830009.html

Android微信智能心跳方案:
https://mp.weixin.qq.com/s/ghnmC8709DvnhieQhkLJpA

=== THE END ===

歡迎關注我的公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM