Android程序保活(一):利用 Activity 提升許可權
前言
最近公司有程序保活方面的業務需求,所以就趁著閒暇時間研究了相關的技術方案,並且親身驗證它們的可行性,接下來我會用幾篇文章詳細介紹。
之前就有人爆出手機 QQ 長久存活的祕訣,那就是 監聽使用者的解鎖屏操作,在鎖屏的時候啟動一個畫素的透明視窗的 Activity,在解鎖的時候把 Activity 銷燬。 不得不佩服鵝廠的程式猿,竟然能想出這麼棒的方案!管你 Android 怎麼升級,該方案真的是屢試不爽!使用者無感知,目的達到了,兩全其美的事情。
首先驗證一下:在鎖屏狀態下 cmd 輸入
adb shell dumpsys activity activities
我們來看一下 dump 的輸出:最頂層的 Task 的資訊,包名:com.tencent.reading,我看了一下應用列表,它是「天天快報」,果然是騰訊家的。

dump 輸出
我們看到 OffActicity 就是頂層的 Activity,懷著好奇心找到了原始碼所在的目錄,參考相關程式碼,自己寫了一個 demo。
具體實現分兩步:
- 建立一個透明的 Activity
- 監聽使用者解鎖屏操作
第一步:建立一個透明的 Activity
1.在 onCreate 方法中設定 window 的屬性
Window window = getWindow(); window.setGravity(Gravity.TOP | Gravity.LEFT); LayoutParams attributes = window.getAttributes(); attributes.x = 0; attributes.y = 0; attributes.height = 1; attributes.width = 1; window.setAttributes(attributes);
2.在 Manifest 中設定一些屬性,包括排除在最近任務列表外、透明主題、啟動模式等
<activity android:name="com.silence.keeplive.onepx.OnePxActivity" android:excludeFromRecents="true" android:exported="false" android:finishOnTaskLaunch="false" android:launchMode="singleInstance" android:process=":main" android:theme="@android:style/Theme.Translucent" android:configChanges="keyboardHidden|orientation|screenSize" />
3.處理觸控和銷燬事件
因為 Activity 是在鎖屏的時候啟動的,所以在使用者點亮屏幕後,它是絕對不能存在的。我們要在 Activity 的生命週期裡做些處理。為了穩妥起見,對 Activity 的觸控事件我們也要處理,直接銷燬 Activity 就可以了。
@Override protected void onResume() { super.onResume(); if (isScreenOn()) { finishSelf(); } } @Override protected void onDestroy() { super.onDestroy(); if (instance != null && instance.get() == this) { instance = null; } } public void finishSelf() { if (!isFinishing()) { finish(); } } private boolean isScreenOn() { PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { return powerManager.isInteractive(); } else { return powerManager.isScreenOn(); } }
第二步:監聽使用者解鎖屏操作
實現該功能要註冊三個廣播:
<action android:name="android.intent.action.USER_PRESENT"/> <action android:name="android.intent.action.SCREEN_ON"/> <action android:name="android.intent.action.SCREEN_OFF"/>
但是這裡有一個問題,USER_PRESENT 可以靜態註冊,其餘兩個只能通過動態註冊才能收到廣播。我們索性把這三個廣播都動態和靜態註冊一次,反正不會有什麼壞處。然後接收到開關屏廣播事件,對 Activity 做處理。
if ("android.intent.action.SCREEN_OFF".equals(action)) { Log.i(TAG, "鎖屏開啟一畫素"); CheckTopTask.setForeground(context); mHandler.postDelayed(mCheckTopTask, 3000); } else if ("android.intent.action.USER_PRESENT".equals(action) || "android.intent.action.SCREEN_ON".equals(action)) { Log.i(TAG, "開屏關閉一畫素"); OnePxActivity onePxActivity = OnePxActivity.instance != null ? OnePxActivity.instance.get() : null; if (onePxActivity != null) { onePxActivity.finishSelf(); } mHandler.removeCallbacks(mCheckTopTask); }
這裡有一個很雞賊的地方,既然鎖屏時已經啟動了透明 Activity,為什麼還要再三秒後還要執行一個任務?因為擔心其他應用也採用同樣的方案,把它的 Activity 蓋在我們的上面。這個任務就是在三秒後檢測當前 Activity 是否在前臺,如果不在就再次啟動,獲得前臺的焦點。我看騰訊就是這麼搞的,大寫的「服」!
最後實現的功能是 Activity 為我們佔據前臺,保證程序不被殺死,後臺的 Service 在辛勤工作,目的達到了,so happy
【附錄】

資料圖
需要資料的朋友可以加入Android架構交流QQ群聊:513088520
點選連結加入群聊【Android移動架構總群】: 加入群聊
獲取免費學習視訊,學習大綱另外還有像高階UI、效能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。