Android 進(jìn)程保活系列:(一)利用 Activity 提升權(quán)限

最近公司有進(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)」,果然是騰訊家的。

dump 輸出

我們看到 OffActicity 就是頂層的 Activity,懷著好奇心找到了源碼所在的目錄,參考相關(guān)代碼,自己寫了一個 demo。

具體實(shí)現(xiàn)分兩步:

  1. 創(chuàng)建一個透明的 Activity
  2. 監(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

參考鏈接:

【騰訊Bugly干貨分享】Android 進(jìn)程?;钫惺酱笕?/a>

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容