上篇文章中提到,安全测试的一个需求是每次由后台切换到前台的时候都要走一遍登录界面,登录界面就是我的启动页,讲的是只要在配置文件中设置启动页activity的启动模式为singleTask就可以了,之后好像对这个效果不满意(是我解决的太快,感觉太简单了么??),所以给出了另外一种方案。
每次进到后台,从后台切换到前台的时候,只要不是在登录界面,就给弹一个dialog,提示用户输入密码验证身份,验证成功进入,点击取消的话进入到登录界面重新登录。
1、首先要解决的问题就是判断程序在前台还是在后台。
因为要监听所有的activity状态,所以判断应该写在BaseActivity中
/**
* 应用是否在前台运行
*
* @return true:在前台运行;false:已经被切到后台了
*/
private boolean isAppOnForeground() {
ActivityManager mActivityManager = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
List appProcesses = mActivityManager.getRunningAppProcesses();
if (appProcesses !=null) {
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
if (appProcess.processName.equals("cn.bzzy.com.bzzy_app")) {
//以下两句话是保存是否在后台的布尔值,判断是否显示dialog会用到
//editor.putBoolean("comefromback", false);
//editor.apply();
return true;
}
}
}
}
Toast.makeText(getApplicationContext(), "注意,标准作业已进入后台运行", Toast.LENGTH_SHORT).show();
//editor.putBoolean("comefromback", true);
//editor.apply();
return false;
}
值得注意的是,这个方法应该写在哪里,一开始我是写在onPause()方法里面的,发现怎么都不可以,后来换到onStop()方法中就可以了。
想想为什么要在onStop中检测,而不是onPause?这是由于A启动B时,生命周期的执行顺序如下:A.onPause->B.onCreate->B.onStart->B.onResume->A.onStop,也就是说,在A的onPause方法中,B的生命周期还没有执行,进程没有进入前台,当然是检测不到的。
好了第一步已经完成了,接下来第二步。
2、第二步是显示dialog。
private AlertDialog passwordDialog ;
private void showDialog() {
final EditText editText =new EditText(BaseActivity.this);
passwordDialog =
new AlertDialog.Builder(BaseActivity.this).
setTitle("请重新输入密码验证身份").setView(editText)
.setPositiveButton("确定", null)
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent =new Intent(getApplicationContext(), LoginActivity.class);
startActivity(intent);
finish();
}
}).setCancelable(false).create();
// if(passwordDialog!=null && !passwordDialog.isShowing()){
passwordDialog.show();
// }
passwordDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String password =editText.getText().toString().trim();
if (!password.equals("")) {
if (!CipherUtils.sm2DecryptHex(passwordPref.getString("password", "")).equals(password)) {
ToastUtil.showShort(getApplicationContext(), "密码错误");
}else {
ToastUtil.showShort(getApplicationContext(), "验证成功");
passwordDialog.dismiss();
}
}else {
ToastUtil.showShort(getApplicationContext(), "请输入密码");急
}
}
});
}
一开始在实现的时候以为就是简单的show出来一个dialog,后来发现默认的dialog是在点击确定的时候自己就消失了,这可不符合我们的需求啊,想想,就算你验证了密码不符合,给出toast出来说验证不通过,但是你的dialog消失了,用户照样可以使用。
所以要解决这个问题,方法就是,在setPositiveButton的点击事件中先设置为null,dialog.show()出来,然后获取PositiveButton的点击,通过验证之后才让dialog消失。
还有一点就是,只有在验证成功的条件下dialog才会消失,所以要设置点击屏幕空白部分和返回键dialog都不能消失,使用.setCancelable(false)。
另外setCanceledOnTouchOutside(false)的作用是点击屏幕空白部分不消失,点击返回键消失。
3、完成以上两个步骤我以为完成了需求,但之后在测试的时候发现,从后台切换到前台的时候,显示dialog没错,这时候再退回到后台,之后进入前台发现又弹出一个dialog,你会发现你在填写密码验证成功之后还有一个没有填写东西的dialog。
解决思路:肯定要在showDialog()方法执行之前进行判断。
怎么判断,想到showDialog的时候实例化dialog的对象,所以在没执行showDialog方法之前肯定是空的
if (passwordDialog ==null){
showDialog();
}
这样弹出一个dialog之后,对象就不为空,就不会执行showDialog方法,确实也是这样,不过经过测试发现,第一次可以达到效果,之后从后台到前台的时候就不会有dialog弹出,看看代码,想一下,是啊,执行过一次之后对象就不为空了啊,以后也不会执行就不会显示dialog了,要想一个办法让它进入到if里面执行showDialog,后来想到dialog.isShowing()这个方法,如果它没在显示的话就让他执行,进入到if了,没毛病,试试看
if (passwordDialog ==null || !passwordDialog.isShowing()) {
showDialog();
}
可以了,到这里目的就达到了。
PS. 本人写在这里的文章是记录自己解决问题的过程,以便日后查看,可能会有些啰嗦,也没有排版,之前也没有写过。如果有幸被你看到引起不适,勿喷。
以上。