Android App 链式唤醒分析
Gracker | 作者
承香墨影 | 校對
https://androidperformance.com/2020/05/07/Android-App-Chain-Wakeup/?| 原文
MIUI 12 的發布,將之前一直是應用開發者和 Rom 開發者斗爭最激烈的部分展示給了普通消費者,讓普通消費者也知道了這場斗爭的細節,正所謂 "魔高一尺道高一丈",Rom 開發者由于有更高的代碼修改權限,始終占據著上風。
App 開發者當然也不甘示弱, 各種保活拉起黑科技層出不窮,甚至 Google 都參與到了這部分斗爭中,居中調和,制定各種規則來規范雙方。當然斗爭對雙方來說都算是好事, 畢竟任何一方完全的勝利都會導致 “狡兔死走狗烹, 飛鳥盡良弓藏”。
不過雙方斗爭的受害者無疑還是使用手機的消費者,App 如果斗爭成功,那么手機上各種后臺進程亂跑,殺不掉,占用 CPU 和內存,這不是消費者想看到的。如果 Rom 開發者斗爭成功,App 的體驗必定會大打折扣,各位 App 開發者應該深有體會。
從文章最后一段可以看到,其實各個手機廠商對付這一套都有自己的策略,基本上都可以搞定自啟動和關聯啟動。至于隱私 ,李彥宏曾經說過 “中國人對隱私問題的態度更加開放,也相對來說沒那么敏感。如果他們可以用隱私換取便利、安全或者效率。在很多情況下,他們就愿意這么做?”。大家想想在微信里面復制一段話打開到淘寶就可以自動跳轉到這個物品,方不方便?好不好用?還想不想用?剪貼板再借我看一看?
希望大家在隱私問題上不要打哈哈,技術是把雙刃劍,如果隱私落到別有用心的人手上,后果是很嚴重的,就算不是為了自己,為了下一代。歐盟為什么要搞《通用數據保護條例》(General Data Protection Regulation,簡稱 GDPR),就是為了隱私。舉個例子 ,國內很多廠商的產品現在要區分是否在歐盟買,如果是在歐盟賣的話,就得把里面那些收集用戶數據的功能都關掉 ,否則抓住了就能罰你罰到吐血 。至于中國和印度,隨便收集。
本篇文章不涉及到隱私部分,我是對隱私保護無條件支持的 。這里只從技術的角度 ,來講一下 MIUI 12 爆出來的應用自啟動和關聯喚醒的問題。
PS: 大家在自己的手機上可能看不到我列的一些例子,是因為我是用的 Android 10 的 AOSP 代碼,大部分的國產 Rom 都已經阻斷了應用的這些行為。
技術名稱解釋
首先解釋幾個技術名詞,方便大家對號入座
進程啟動
在 Android 中 ,一個 App 包含六部分,進程 (必選) + Activity (可選) + BroadcastReceiver (可選)+Service (可選)+ContentProvider (可選) + 子進程 (可選)
一個必選項加五個可選項,組成了一個 App ,其中 Activity(可選) + BroadcastReceiver(可選) + Service(可選) + ContentProvider(可選) 這四個又稱為 Android 的四大組件,之所以這四兄弟這么特殊,是因為這四個組件都可以單獨啟動。
但是這四兄弟啟動之前,系統都會檢查對應的進程是否存在,如果進程不存在 ,那么就需要先啟動進程,再啟動這個組件。我們在桌面上點擊一個應用圖標,其實啟動的就是他的 Activity ,系統會先創建進程,然后再啟動 Activity ,我們才可以看到對應的界面。
一般自啟動和關聯啟動,一般不會直接啟動 Activity ,因為 Activity 是用戶可感知的 ,你在后臺莫名其妙起了一個界面到前臺,用戶分分鐘卸了你 。所以一般自啟動和關聯啟動都是在 BroadcastReceiver (可選) + Service (可選) + ContentProvider (可選) 三個上面做文章。
自啟動
自啟動指的是不借助其他的應用,通過監聽系統的一些事件,或者文件變化,通過系統的機制,把自己的進程拉起來處理事情。
關聯啟動
關聯啟動,指的是借助其他應用來啟動自己,比如大家列出來的起點讀書啟動作家助手 \ 電信營業廳 \ 百詞斬這種。
啟動阻斷
啟動阻斷也叫切斷喚醒,Rom 開發人員在四大組件啟動的地方加入邏輯判斷,符合條件的組件才能拉起自己的進程 ,不符合條件的組件直接返回 ,這樣就達到了啟動阻斷的目的。
當然這里面還有很多工作要做,比如工作狀態判斷,拉起合理性判斷 ,一旦錯誤的阻斷必然會引起用戶的使用邏輯的斷裂,比如用戶在一個 App 里面要拉起支付寶進行支付 ,結果啟動支付寶的支付組件的時候被你給阻斷了,可以想象用戶的憤怒。
有了上面幾個簡單的概念,下面我們就簡單說一下自啟動和關聯啟動的技術分析 。
分析手段
Monkey
要分析應用啟動,首先需要安裝大量應用,然后執行 Monkey,讓大部分進程都跑起來。我使用的 Monkey 命令如下,跑完就自己去睡覺了。
adb?shell?monkey?--kill-process-after-error?--ignore-security-exceptions?--ignore-crashes?--pct-appswitch?90?--pct-touch?10?--throttle?10000?--ignore-timeouts?--ignore-native-crashes?100000000EventLog
首先可以用 EventLog 來查看進程的啟動信息,EventLog 會如實記錄每個進程的啟動、死亡信息。我使用下面的命令來進行進程啟動和死亡的過濾。
adb?logcat?-b?events?|?egrep?"am_proc_died|am_proc_start"Dumpsys
這里主要是使用了 Dumpsys activity ,主要是用來分析進程的各個組件的信息
adb?shell?dumpsys?activity自啟動指的是不借助其他的應用,通過監聽系統的一些事件,或者文件變化,通過系統的機制,把自己的進程拉起來處理事情。這些系統的事件就包括開機廣播 / 網絡變化 / 媒體庫掃描等 (這里只列了一部分) 。
自啟動的技術分析
開機廣播
用戶重啟手機后,系統會向注冊了開機廣播的應用發廣播,收到廣播的應用就可以把自己拉起來,開始處理對應的邏輯 (拉起更多的進程) ,對應的廣播如下:
android.intent.action.BOOT_COMPLETED應用可以監聽這個廣播,在用戶重啟手機后,將自己喚醒,處理自己的邏輯 ,比如說繼續圖片備份 / 繼續同步聯系人 / 檢查是否有固件更新 / 推送最新的新聞等操作
當然監聽這個廣播也是應用自啟動的一個手段
案例: 騰訊新聞監聽開機廣播拉起后臺進程
典型的廣播接受處理記錄 : com.tencent.news 的 com.tencent.news.system.BootBroadcastReceiver 組件接收了 android.intent.action.BOOT_COMPLETED 廣播 ,處理了 7s ,至于怎么處理,當然是先把 com.tencent.news 這個進程拉起來,然后執行 BootBroadcastReceiver 的 onReceive 方法。這是一個典型的自啟動的例子。
網絡變化
網絡變化包括網絡連接 / 斷開 / wifi 移動網絡切換等操作,一旦發生這些事件,系統會向對應注冊了這個事件的應用發送廣播。對應的廣播如下:
android.net.conn.CONNECTIVITY_CHANGE應用就可以監聽這個廣播來執行對應的邏輯,比如你在看直播,突然 wifi 斷了切換成了 4G 網絡,應用就可以提醒用戶是否使用移動網絡繼續觀看,畢竟網絡直播還是很耗流量的。
當然監聽這個廣播也是應用自啟動的一個手段
案例:
下圖可以看到五個監聽了網絡變大的廣播接收器 (只顯示了五個 ,其實有 200 多個) ,監聽到網絡變化后拉起自身
圖中 packageName 就是對于的應用的包名,name 是啟動的組件。
android.net.wifi.STATE_CHANGE媒體庫掃描
系統監聽到文件變化或者存儲盤變化也會發通知給各個應用,比如說增加了一個圖片或者文檔,其對于的廣播如下。
android.intent.action.MEDIA_SCANNER_STARTED android.intent.action.MEDIA_SCANNER_FINISHED android.intent.action.MEDIA_EJECT當然監聽這個廣播也是應用自啟動的一個手段
案例: jd 監聽 MEDIA_SCANNER_STARTED 廣播自啟
下面是一個典型的監聽媒體庫掃描廣播進行自啟動的案例:
com.jd.jrapp 的廣播接收器 com.jd.jrapp.library.longconnection.receiver.BootReceiver 監聽到 android.intent.action.MEDIA_SCANNER_STARTED 廣播后,啟動自己進程開始處理。
三方 SDK - 個推
個推是各個應用接入的一個三方 SDK,用于消息推送,但其實個推也集成了上面說的哪幾種自啟動的方式,包括 BOOT_COMPLETED、CONNECTIVITY_CHANGE、USER_PRESENT 這些。
關于個推,由于可定制型比較強,比如在項目源碼中添加一個繼承自 com.igexin.sdk.PushService 的自定義 Service 就可以 ,所以從 EventLog 和 Dumpsys 沒法直接看出來哪個用了個推來保活或者相互喚醒,不過其對于的子進程得設置為 :pushservice ,可以根據這個做判斷 (有可能不準)
所以我們直接看個推的配置文檔。
<serviceandroid:android:permission="android.permission.BIND_JOB_SERVICE"android:exported="false"android:label="NotificationCenter"android:process=":pushservice"/> <receiver?android:?><intent-filter><action?android:?/><action?android:?/><action?android:?/><!--?以下三項為可選的?action?聲明,有助于提高?service?存活率?--><action?android:?/><action?android:?/><action?android:?/></intent-filter> </receiver> <activityandroid:android:excludeFromRecents="true"android:exported="false"android:process=":pushservice"android:taskAffinity="com.igexin.sdk.PushActivityTask"android:theme="@android:style/Theme.Translucent.NoTitleBar"?> </activity> <activityandroid:android:excludeFromRecents="true"android:exported="true"android:process=":pushservice"android:taskAffinity="com.igexin.sdk.PushActivityTask"android:theme="@android:style/Theme.Translucent.NoTitleBar"/>像最前面同提到的 BootComplete ,com.ss.android.ugc.aweme:pushservice 可能就是接入了個推
關聯啟動的技術分析
關聯啟動指的是借助其他應用來啟動自己 ,比如說很多 App 接入了同一個 SDK ,那么一旦你啟動了接入這個 SDK 的應用 ,那么這個 SDK 就可以啟動同樣接入了這個 SDK 的其他應用,達到關聯喚醒的目的
這個 SDK 可以是 BAT 集團內部自研的通用 SDK ,也可以是三方提供的 SDK ,根據我自己的調試來看 ,大家提到的 xxx 啟動了 xxx,大部分都是通過三方 SDK 來實現的 ,大部分是用了極光推送。
下面就以一個案例來看極光推送是怎么利用一個已經啟動的 App 來啟動另外一個沒有啟動的 App 的。
首先我們看 EventLog 可以看到進程的啟動信息 ,包括進程名,進程 pid ,啟動的組件,啟動的組件類型。
[0,19428,10195,com.qq.reader,service,{com.qq.reader/cn.jpush.android.service.DaemonService}]上面這條 Log 解釋一下就是:
啟動進程:com.qq.reader(QQ 閱讀)
啟動 pid:19428
啟動組件:cn.jpush.android.service.DaemonService
組件類型:service
案例:起點 App 通過極光推送拉起 QQ 閱讀
執行 adb shell am force-stop com.qq.reader ,強制殺掉 QQ 閱讀 ,觀察 EventLog,從下面的可以看到 ,QQ 閱讀的進程被拉起,拉起的是 Service 這個組件,其具體的內容是 com.qq.reader/cn.jpush.android.service.DaemonService
當然從 Event Log 里面我們看不出來是誰拉起了這個 Service ,這時候就需要 dumpsys activity 的幫助了,由于是 Service 組件被拉起,那么我們可以看 com.qq.reader 的 ServiceRecord ,其內容如下,可以看到其 Connections 一欄,是被 com.qidian.QDReader:pushcore 這個進程拉起的
那么對應的 ,在小米的 MIUI 12 關聯啟動界面就會顯示 : 起點讀書 在 8:48 分拉起了 QQ 閱讀 (由于沒有小米手機,所以沒法截圖,大家自己看高票答案 https://www.zhihu.com/question/391494145 自己腦補一下就可以了)
極光推送的官方文檔其實也說的很清楚,提供了被拉起和拉起別人的功能,看你自己怎么用。
激光官方文檔喚醒配置
激光關聯啟動文檔
手機廠商應對
最前面的有說到,進程管理是應用開發者和 Rom 開發者斗爭最激烈的部分 ,MIUI 選擇了將斗爭的過程展示給了普通消費者,讓普通消費者也知道了這場斗爭的細節。其他的廠商也做了相同的事情,否則整個系統基本上是沒法用的,就像我手上現在這臺測試用的 pixel,不斷有進程因為整機內存太小被 LMK 殺掉,然后馬上被各種手段重新啟動,耗電極快,卡的連娘都不認識了。
我們從極光和個推的官方文檔就可以看到各個手機廠商的應對方法和開關的界面,這里列出來是方便大家進去看一下,因為各個手機廠商的白名單配置不一樣,或者有時候用戶自己改過但是忘記了 ,都可以進去重新設置一下 ,對于那些你退出了就不想讓他繼續活動的應用 ,果斷去掉白名單。
極光推送白名單配置
個推白名單配置
EMUI OS(華為)
自啟動管理:需要把應用加到【自啟動管理】列表,否則殺進程或重新開機后進程不會開啟,只能手動開啟應用
后臺應用保護:需要手動把應用加到此列表,否則設備進入睡眠后會自動殺掉應用進程,只有手動開啟應用才能恢復運行
通知管理:應用狀態有三種:提示、允許、禁止。禁止應用則通知欄不會有任何提醒
Flyme OS(魅族)
自啟動管理:需要把應用加到【自啟動管理】列表,否則殺進程或重新開機后進程無法開啟
通知欄推送:關閉應用通知則收到消息不會有任何展示
Funtouch OS(VIVO)
自啟動管理:需要將應用加入 “i 管家” 中的【自啟動管理】列表,否則重啟手機后進程不會自啟。但強制手動殺進程,即使加了這個列表中,后續進程也無法自啟動。
Color OS(OPPO)
凍結應用管理:需要將應用加入純凈后臺,否則鎖屏狀態下無法及時收到消息
自啟動管理:將應用加入【自啟動管理】列表的同時,還需要到設置 - 應用程序 - 正在運行里鎖定應用進程,否則殺進程或者開機后進程不會開啟,只能手動開啟應用
MIUI OS (小米)
自啟動管理:需要把應用加到【自啟動管理】列表,否則殺進程或重新開機后進程無法開啟
省電策略:需要禁用應用省電策略,否則后臺幾分鐘后會被系統限制聯網
MIUI 7 神隱模式:允許用戶設置后臺聯網應用,開啟后應用即可在后臺保持聯網,否則應用進入后臺時,應用無法正常接收消息。【設置】->【電量和性能】->【神隱模式】
本文對你有幫助嗎?留言、轉發、點好看是最大的支持,謝謝!
熱文推薦
ViewGroup 默認順序繪制子 View,如何修改?什么場景需要修改繪制順序?
OkHttp 支持 WebSocket,鑒權/長連接保活
TCP 握手/回收,出現意外,如何處理?
公眾號后臺回復成長『成長』,將會得到我準備的學習資料。
總結
以上是生活随笔為你收集整理的Android App 链式唤醒分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于单片机的空气检测系统的设计
- 下一篇: ch340串口驱动_如何使用串口来给ST