前一篇文章 —— Android程式保活(一):利用 Activity 提升許可權講到了利用前臺的 Activity 保證系統管家不會殺死我們的應用。這只是一種討巧的技術手段,並沒有利用系統的漏洞。今天要說的方法適用性非常廣,應用間互不干擾,不存在像是我開啟一畫素 Activity 把你蓋住,然後你我爭奪焦點的問題。
在實踐之前還是先看看別人家的應用,比如騰訊新聞。adb shell 下輸入
dumpsys activity services com.tencent.news
複製程式碼
看一下控制檯的輸出,顧名思義 PushService 應該是推送服務,ServiceRecord 指明瞭它是前臺服務。我們看一下它的 oom_adj 的值:
ps | grep com.tencent.news
cat /proc/25890/oom_adj
複製程式碼
沒錯,PuseService 的優先順序是 2,屬於可感知程式,一般不會被殺死。下面有個關於 oom_adj 的說明。
其中紅色部分代表比較容易被殺死的 Android 程式(OOM_ADJ >= 4),綠色部分表示不容易被殺死的 Android 程式,其他表示非 Android 程式(純 Linux 程式)。在 Lowmemorykiller 回收記憶體時會根據程式的級別優先殺死 OOM_ADJ 比較大的程式,對於優先順序相同的程式,則進一步受到程式所佔記憶體和程式存活時間的影響。
下面就來親身實踐一下。利用系統的漏洞開啟前臺服務,提升程式的優先順序。這裡有兩個思路:
- 思路一:API < 18,啟動前臺 Service 時直接傳入new Notification();
- 思路二:API >= 18,同時啟動兩個id相同的前臺 Service,然後再將後啟動的 Service 做 stop 處理;
// 要開啟前臺的 service
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT < 18) {
startForeground(SERVICE_ID, new Notification());
} else {
startForeground(SERVICE_ID, new Notification());
Intent sendIntend = new Intent(this, ChannelService.class);
startService(sendIntend);
}
return super.onStartCommand(intent, flags, startId);
}
// 為開啟前臺 service 服務的傀儡 service
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(DaemonService.SERVICE_ID, new Notification());
stopForeground(true);
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
複製程式碼
這樣就完成了,我們再次用 shell 命令驗證一下。
沒錯,它已經是前臺服務了,使用者看不見摸不著的前臺服務,系統是不會輕易殺死該服務的,可以放心大膽地搞事情啦~~
最後
在現在這個金三銀四的面試季,我自己在網上也蒐集了很多資料做成了文件和架構視訊資料免費分享給大家【包括高階UI、效能優化、架構師課程、NDK、Kotlin、混合式開發(ReactNative+Weex)、Flutter等架構技術資料】,希望能幫助到您面試前的複習且找到一個好的工作,也節省大家在網上搜尋資料的時間來學習。