大家好,又到了新一期的專案需求討論,很多APP都有安全的意識,比如一些銀行的APP,你登入後,看一些東西,然後這時候鎖屏了。或者是按了Home鍵退到了後臺,這時候,再啟動這個App,可能就會又到了這個APP的解鎖的介面。或者重新登入的介面。防止安全。
在前面的文章中我介紹過APP第一次開啟登入進去時候的解鎖功能:
專案需求討論-APP手勢解鎖及指紋解鎖
假設我們我們這裡APP的登入用的是手勢解鎖,那麼我們的APP在使用過程中,退到後臺或者鎖屏後,就應該再次出現這個手勢解鎖的介面。也就是:
還是老話,我寫的方法可能不是最佳的,希望大家輕點噴,哈哈。
我們分情況來看:
1.使用者按了Home鍵或者啟動其他APP等導致當前APP居於後臺:
因為使用者是在操作過程中,把APP退到了後臺,所以我們不可能在特定的某個Activity中去監聽這些使用者退出後臺等操作。所以我們想到了在BaseActivity中做處理。
我們假設使用者在操作我們APP的A介面,A介面呼叫onStop方法有二種情況,一種是你開啟了APP中的其他介面,還有一種就是比如按了Home鍵退到了後臺,所以當onStop呼叫的時候,我們來判斷下,我們的APP當前是不是在還處於前臺,如果還在前臺,就說明只是APP內部開啟了另外一個Activity而已。不然就是處於了後臺。
判斷我們的APP是處於前臺還是後臺有很多介紹,網上也有很多。但是我這裡還是要說一點:
網上很多程式碼都是這樣的:
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
for (String activeProcess : processInfo.pkgList) {
if (activeProcess.equals(context.getPackageName())) {
isInBackground = false;
}
}
}
}複製程式碼
我們可以看到網上很多介紹ActivityManager.getRunningAppProcessed
方法說是返回了系統中正在執行的APP的程式,所有拿到的是List,所以然後遍歷一遍,判斷哪個程式處於前端,然後再判斷這個處於前端的程式的包名是不是我們這個APP的名字。網上清一色的介紹也都是這樣,但是在我實際開發中,我發現runningProcess
的size一直返回為1。直接就返回了我們的APP的程式,還不是像網上所說的那樣。就算我額外開了好幾個其他APP也還是一樣,返回的size為1,後來查了其他的資料發現了原因:
在Android 5.0+的系統上,getRunningTasks方法和getRunningAppProcess方法返回的都是你的application process。我在測試時候也都驗證了(Android 6.0),的確這二個方法都返回的size都為1。這裡大家可以留個心。說不定以後就碰到這方面問題。
最後我們的程式碼如下所示:
public boolean isApplicationBroughtToBackground(final Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
if(runningProcesses != null && !runningProcesses.isEmpty()){
if(runningProcesses.get(0).importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND){
return true;
}
}
}else{
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
if (tasks != null && !tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
return true;
}
}
}
return false;
}複製程式碼
然後我們在onRestart
方法中進行判斷來決定是否啟動我們的手勢鎖介面:
@Override
protected void onRestart() {
super.onRestart();
if (isBackground) {
isBackground = false;
Intent intent = new Intent();
intent.putExtra("isReLock", true);
intent.setClass(aty, PatternLockActivity.class);
startActivity(intent);
}
}複製程式碼
細心的讀者發現,我們這裡intent裡面傳了個引數intent.putExtra("isReLock", true);
,為什麼要傳這個值,是這樣的:
- 如果你是第一次開啟這個APP。啟動這個手勢介面的時候,你不想進去了。你可以按返回鍵,然後退出了這個APP,但是如果是你在操作我們的APP過程中,因為退到了後臺後再次被鎖定,這時候出來的手勢鎖就不能有響應返回鍵的功能了。除非你重新解鎖,不然按了返回鍵,手勢介面被關閉,豈不是裡面的內容又被看到了。再次啟動的手勢介面也毫無保密功能可言。
- 第一次開啟APP的顯示的手勢鎖解鎖成功的時候,是會進行其他登入的程式碼,然後跳到主頁面,而後面再次鎖上的手勢鎖,解鎖成功後,我們只是把它給finish掉而已。所以我們這裡要傳這麼個引數來進行標記。
我們來看下相關的程式碼:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (!isReLock && keyCode == KeyEvent.KEYCODE_BACK) {
finish();
}
return true;
}複製程式碼
2. 使用者對手機進行了鎖屏操作:
我們一般在APP登入成功後,進入到主介面MainActivity,然後通過MainActivity進行相關介面的跳轉及操作,所以一般來說,這個MainActivity是一直存在的。不會說被關閉等,所以我們只需要在主介面處動態註冊廣播,來監聽相關的鎖屏介面即可。
自定義廣播LockStateReceiver.java:
public class LockStateReceiver extends BroadcastReceiver{
private StateListener listener;
public LockStateReceiver(StateListener listener) {
this.listener = listener;
}
@Override
public void onReceive(Context context, Intent intent) {
if(Intent.ACTION_USER_PRESENT.equals(intent.getAction())){
if(!App.getBackgroundApp()){
listener.stateScreenOff();
}
}
}
public interface StateListener{
void stateScreenOff();
}
}複製程式碼
MainActivity.java:
receiver = new LockStateReceiver(this);
if (receiver != null) {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_PRESENT);
registerReceiver(receiver, filter);
}複製程式碼
記得在MainActivity.java銷燬時候取消註冊的廣播:
if (receiver != null) {
unregisterReceiver(receiver);
}複製程式碼
這裡大家可能會問,你不是說好的監聽鎖屏的嗎,怎麼現在監聽的是使用者解鎖的Action了。
是這樣的,我解釋下:
在我的上一篇文章中,我們的使用者可能用的是指紋解鎖的功能,
專案需求討論-APP手勢解鎖及指紋解鎖
如果你在監聽使用者鎖屏動作,然後在接受到鎖屏的廣播時候就去把我們APP的指紋鎖屏介面給調出來,這時候你會發現,你去解鎖手機自帶的鎖屏介面時候,用指紋解鎖無效,因為指紋解鎖的功能已經被我們的APP給挾持過去了。所以反而手機的鎖屏無法用指紋解鎖了。所以我們思路換一下,既然有手機要鎖定,肯定有解鎖的時候,我們只需要監聽手機解鎖動作,然後把我們的APP給鎖定起來即可。
所以我們只需要在接受到Intent.ACTION_USER_PRESENT
的廣播後,判斷下當前是不是處於後臺,如果是處於後臺,我們就不需要做處理,為什麼,因為我們的APP處於後臺後,本身就已經有一套機制去呼叫APP鎖定介面,如果我們的APP處於前端,然後手機解鎖後,我們才會去啟動APP的鎖定介面。
所以程式碼才會反過來:
if(!App.getBackgroundApp()){
listener.stateScreenOff();
}複製程式碼
同樣的,stateScreenOff
方法也就是啟動我們的APP的手勢鎖定介面:
public void stateScreenOff() {
Intent intent = new Intent();
intent.putExtra("isReLock", true);
intent.setClass(aty, PatternLockActivity.class);
startActivity(intent);
}複製程式碼
好了。基本的實現就是這樣。希望大家多提意見。哈哈。。
PS:感謝大家留言,下面也有人說了可以使用ActivityLifecycleCallbacks來執行。。大家也可以參考。o( ̄︶ ̄)o