大家好,又到了新一期的项目需求讨论,很多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