最近公司有進程鄙涓唬活方面的業(yè)務(wù)需求膝迎,所以就趁著閑暇時間研究了相關(guān)的技術(shù)方案限次,并且親身驗證它們的可行性柴灯,接下來我會用幾篇文章詳細介紹卖漫。
之前就有人爆出手機 QQ 長久存活的秘訣赠群,那就是 監(jiān)聽用戶的解鎖屏操作,在鎖屏的時候啟動一個像素的透明窗口的 Activity店枣,在解鎖的時候把 Activity 銷毀叹誉。 不得不佩服鵝廠的程序猿,竟然能想出這么棒的方案长豁!管你 Android 怎么升級,該方案真的是屢試不爽钝侠!用戶無感知酸舍,目的達到了,兩全其美的事情啃勉。
首先驗證一下:在鎖屏狀態(tài)下 cmd 輸入
adb shell dumpsys activity activities
我們來看一下 dump 的輸出:最頂層的 Task 的信息,包名:com.tencent.reading叮阅,我看了一下應(yīng)用列表,它是「天天快報」浩姥,果然是騰訊家的。
我們看到 OffActicity 就是頂層的 Activity兜挨,懷著好奇心找到了源碼所在的目錄缴饭,參考相關(guān)代碼,自己寫了一個 demo颗搂。
具體實現(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. 處理觸摸和銷毀事件
因為 Activity 是在鎖屏的時候啟動的,所以在用戶點亮屏幕后比驻,它是絕對不能存在的岛抄。我們要在 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)聽用戶解鎖屏操作
實現(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ù)?因為擔(dān)心其他應(yīng)用也采用同樣的方案烁竭,把它的 Activity 蓋在我們的上面。這個任務(wù)就是在三秒后檢測當前 Activity 是否在前臺婉弹,如果不在就再次啟動终吼,獲得前臺的焦點。我看騰訊就是這么搞的际跪,大寫的「服」!
最后實現(xiàn)的功能是 Activity 為我們占據(jù)前臺良姆,保證進程不被殺死穴肘,后臺的 Service 在辛勤工作歇盼,目的達到了评抚,so happy ~~
項目地址:AndroidKeepPractive