最近公司有進(jìn)程?;罘矫娴臉I(yè)務(wù)需求,所以就趁著閑暇時間研究了相關(guān)的技術(shù)方案,并且親身驗(yàn)證它們的可行性,接下來我會用幾篇文章詳細(xì)介紹。
之前就有人爆出手機(jī) QQ 長久存活的秘訣,那就是 監(jiān)聽用戶的解鎖屏操作,在鎖屏的時候啟動一個像素的透明窗口的 Activity,在解鎖的時候把 Activity 銷毀。 不得不佩服鵝廠的程序猿,竟然能想出這么棒的方案!管你 Android 怎么升級,該方案真的是屢試不爽!用戶無感知,目的達(dá)到了,兩全其美的事情。
首先驗(yàn)證一下:在鎖屏狀態(tài)下 cmd 輸入
adb shell dumpsys activity activities
我們來看一下 dump 的輸出:最頂層的 Task 的信息,包名:com.tencent.reading,我看了一下應(yīng)用列表,它是「天天快報(bào)」,果然是騰訊家的。

我們看到 OffActicity 就是頂層的 Activity,懷著好奇心找到了源碼所在的目錄,參考相關(guān)代碼,自己寫了一個 demo。
具體實(shí)現(xiàn)分兩步:
- 創(chuàng)建一個透明的 Activity
- 監(jiān)聽用戶解鎖屏操作
第一步:創(chuàng)建一個透明的 Activity
1. 在 onCreate 方法中設(shè)置 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 中設(shè)置一些屬性,包括排除在最近任務(wù)列表外、透明主題、啟動模式等
<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. 處理觸摸和銷毀事件
因?yàn)?Activity 是在鎖屏的時候啟動的,所以在用戶點(diǎn)亮屏幕后,它是絕對不能存在的。我們要在 Activity 的生命周期里做些處理。為了穩(wěn)妥起見,對 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();
}
}
第二步:監(jiān)聽用戶解鎖屏操作
實(shí)現(xiàn)該功能要注冊三個廣播:
<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 可以靜態(tài)注冊,其余兩個只能通過動態(tài)注冊才能收到廣播。我們索性把這三個廣播都動態(tài)和靜態(tài)注冊一次,反正不會有什么壞處。然后接收到開關(guān)屏廣播事件,對 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, "開屏關(guān)閉一像素");
OnePxActivity onePxActivity = OnePxActivity.instance != null ? OnePxActivity.instance.get() : null;
if (onePxActivity != null) {
onePxActivity.finishSelf();
}
mHandler.removeCallbacks(mCheckTopTask);
}
這里有一個很雞賊的地方,既然鎖屏?xí)r已經(jīng)啟動了透明 Activity,為什么還要再三秒后還要執(zhí)行一個任務(wù)?因?yàn)閾?dān)心其他應(yīng)用也采用同樣的方案,把它的 Activity 蓋在我們的上面。這個任務(wù)就是在三秒后檢測當(dāng)前 Activity 是否在前臺,如果不在就再次啟動,獲得前臺的焦點(diǎn)。我看騰訊就是這么搞的,大寫的「服」!
最后實(shí)現(xiàn)的功能是 Activity 為我們占據(jù)前臺,保證進(jìn)程不被殺死,后臺的 Service 在辛勤工作,目的達(dá)到了,so happy ~~
項(xiàng)目地址:AndroidKeepPractive