本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。
前言:
一些app设置完手势密码或者数字密码之后,每次打开这个app都会打开解锁页面。
这个解锁页面的弹起分为两种:一种是按下home下次打开即弹起解锁页面 ,一种是锁屏之后。这种方式大同小异,区别只是广播接收者的监听的区别。我们这里以按下home键为例子,展开描述。
一、需求
不管当前我们正在操作我们的app的哪个页面,当按下home键之后,下次打开app,即打开指定检查页,比如密码页。
演示git.gif二、 分析
既然是按下home触发的,那么我们就不能再Activity里面去打开指定检查页,因为我们不知道到底是在哪一个页面被按下home的。
1、按下home触发,联想到广播接收者。
2、这个需求是需要后台一直执行的,需要服务的协助。又因为这个home键的监听比较敏感,所以需要在的服务里面代码注册广播接收者才能生效。
总的来说就是这样,服务加广播。
情景想像:
当我选择了打开检查页,就利用Sp作为将检查页选项作为boolean缓存起来,并且开启服务。
当我们打开启动页,就判断 检查页选择项 ,如果打开,就判断服务是否开启,无开启则开启服务。
服务里面的代码很简单,服务开启就代码注册广播接收者,服务销毁就取消广播接收者。
广播接收者里面做的事情是,当我们按下home键,就由广播接收者开启检查页。
有了上面的准备之后,当我们打开开启检查页,按下home键就会顺利弹检查页了。
但是如果这样会有问题,那就是当用户狂按home键,那么就会打开很多个重复的检查页。
所以我们在广播接收者里面还需要做一个判断,即可当前手机的前台app是否是我们的当前app,如果是,那么才允许跳检查页,如果不是,就不跳了,避免3下home键打开3个检查页。
看起来差不多,但是判断不够严谨,那就是,当我们在当前程序按下home键,我们的程序由前台任务栈转为后台任务栈的这个时间换成毫秒还是挺长的,足够用户快速按好多下,也就是说,当我们在当前程序处于前台任务线的时候,用户猛点了好几下home键,起始还是可以足够广播响应好几次打开好几次检查页的。
解决办法:
1、本来可以判断当前手机前台任务栈的栈顶是哪一个Activity,但是在android5.0以后这个权限被限制了,有其他迂回的实现方式,详情可以百度,我们这里暂时不考虑。
2、我们在广播接收者打开检查页在最外层再做一个boolean判断到Sp。我们在启动页让这个 检查页判断项 先置为true,在接收者的onReceive里面,先做这个最外层的判断,因为启动页置为true,所以第一次是可以通过的,当我们启动一次之后,就把这个值置为false,只有当检查页被销毁执行onDestroy时才重新把这个最外层检查项目置为true。
三、代码
三.1、先看看服务和广播和检查页
看看服务
public class HomeWatchService extends Service {
private HomeWatcherReceiver receiver;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// 服务onCreate里面代码注册广播接收者
System.out.println("=========== 开启服务");
receiver = new HomeWatcherReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.CLOSE_SYSTEM_DIALOGS");
System.out.println("=========== 服务 代码注册广播接收者");
this.registerReceiver(receiver, filter);
super.onCreate();
}
@Override
public void onDestroy() {
// 服务onCreate里面代码取消广播接收者
System.out.println("=========== 服务 代码取消广播接收者");
unregisterReceiver(receiver);
super.onDestroy();
}
}
看看广播:
public class HomeWatcherReceiver extends BroadcastReceiver {
private static final String LOG_TAG = "HomeReceiver";
private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
private static final String SYSTEM_DIALOG_REASON_LOCK = "lock";
private static final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.i(LOG_TAG, "onReceive: action: " + action);
if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
// android.intent.action.CLOSE_SYSTEM_DIALOGS
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
Log.i(LOG_TAG, "reason: " + reason);
// 第一层判断,能否打开新的检查页,在启动页我们只为true,下面成功打开检查页之后就只为false
// 之后检查页指定onDestroy之后才会将这个值置为false
if(CacheUtils.getBoolean(context,"IS_CAN_OPEN")){
// 是否短按home
if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) {
// 短按Home键
Log.i(LOG_TAG, "homekey");
// 当前app是否处有前台进程,只有处于前台进程才允许打开检查页
if(isAppOnForeground(context)){
System.out.println("============= 当前app在属于前台任务栈");
Intent openCheck = new Intent(context,CheckActivity.class);
openCheck.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(openCheck);
}else{
System.out.println("============= 当前app在不属于前台任务栈");
}
}
CacheUtils.setBoolean(context,"IS_CAN_OPEN",false);
}
else if (SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) {
// 长按Home键 或者 activity切换键
Log.i(LOG_TAG, "long press home key or activity switch");
}
else if (SYSTEM_DIALOG_REASON_LOCK.equals(reason)) {
// 锁屏
Log.i(LOG_TAG, "lock");
}
else if (SYSTEM_DIALOG_REASON_ASSIST.equals(reason)) {
// samsung 长按Home键
Log.i(LOG_TAG, "assist");
}
}
}
/**
* 判断当前app是否处于前台进程
* @param context
* @return
*/
public boolean isAppOnForeground(Context context) {
// Returns a list of application processes that are running on the
// device
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
String packageName = context.getPackageName();
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager
.getRunningAppProcesses();
if (appProcesses == null)
return false;
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
// The name of the process that this object is associated with.
if (appProcess.processName.equals(packageName)
&& appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
return false;
}
}
.
.
看看 检查页
Paste_Image.pngpublic class CheckActivity extends Activity{
private TextView mTvPass;
private boolean isLastIsSelf;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_check);
mTvPass = (TextView) findViewById(R.id.mTvPass);
mTvPass.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
CacheUtils.setBoolean(CheckActivity.this,"IS_CAN_OPEN",true);
}
}
三.2 看看3个页面
页面分为三个,MainActivity、SecondActivity、ThirdActivity三个。
其中MainActivity包括对检查页的开关
页面一、MainActivity
Paste_Image.pngMainActivity
public class MainActivity extends AppCompatActivity {
private TextView mTvOpenSecond;
private TextView mTvOpenThird;
private TextView mTvOpenCheck;
private TextView mTvcCloseCheck;
private TextView mTvServiceState;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvOpenSecond = (TextView) findViewById(R.id.mTvOpenSecond);
mTvOpenThird = (TextView) findViewById(R.id.mTvOpenThird);
mTvOpenCheck = (TextView) findViewById(R.id.mTvOpenCheck);
mTvcCloseCheck = (TextView) findViewById(R.id.mTvcCloseCheck);
mTvServiceState = (TextView) findViewById(R.id.mTvServiceState);
mTvOpenSecond.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,SecondActivity.class));
}
});
mTvOpenThird.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,ThirdActivity.class));
}
});
mTvOpenCheck.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"打开检查锁",Toast.LENGTH_SHORT).show();
if(ServiceUtils.isServiceWork(MainActivity.this,"com.am.alwayscheckview.service.HomeWatchService")){
System.out.println("按钮开启服务的判断 服务已经开启");
}else{
System.out.println("按钮开启服务的判断 服务未开启");
startService(new Intent(MainActivity.this, HomeWatchService.class));
}
// 把密码的开启状态缓存起来
CacheUtils.setBoolean(MainActivity.this,"Check",true);
}
});
mTvcCloseCheck.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CacheUtils.setBoolean(MainActivity.this,"Check",false);
Toast.makeText(MainActivity.this,"关闭检查锁",Toast.LENGTH_SHORT).show();
if(ServiceUtils.isServiceWork(MainActivity.this,"com.am.alwayscheckview.service.HomeWatchService")){
System.out.println("按钮关闭服务的判断 服务已经开启,进行关闭");
stopService(new Intent(MainActivity.this, HomeWatchService.class));
}
}
});
mTvServiceState.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"当前开启状态:"+ServiceUtils.isServiceWork(MainActivity.this,"com.am.alwayscheckview.service.HomeWatchService")
,Toast.LENGTH_SHORT).show();
}
});
}
}
.
.
页面二、SecondActivity
Paste_Image.pngpublic class SecondActivity extends AppCompatActivity {
private TextView mTvOpenMain;
private TextView mTvOpenThird;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mTvOpenMain = (TextView) findViewById(R.id.mTvOpenMain);
mTvOpenThird = (TextView) findViewById(R.id.mTvOpenThird);
mTvOpenMain.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SecondActivity.this,MainActivity.class));
}
});
mTvOpenThird.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SecondActivity.this,ThirdActivity.class));
}
});
}
}
.
.
页面三、ThirdActivity
Paste_Image.pngpublic class ThirdActivity extends AppCompatActivity {
private TextView mTvOpenMain;
private TextView mTvOpenSecond;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
mTvOpenMain = (TextView) findViewById(R.id.mTvOpenMain);
mTvOpenSecond = (TextView) findViewById(R.id.mTvOpenSecond);
mTvOpenMain.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(ThirdActivity.this,MainActivity.class));
}
});
mTvOpenSecond.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(ThirdActivity.this,SecondActivity.class));
}
});
}
}
两个工具类
本地缓存工具类
public class CacheUtils
{
private final static String SP_NAME = "AlwaysCheck";
private static SharedPreferences mPreferences; // SharedPreferences的实例
private static SharedPreferences getSp(Context context)
{
if (mPreferences == null)
{
mPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
}
return mPreferences;
}
/**
* 通过SP获得boolean类型的数据,没有默认为false
*
* @param context
* : 上下文
* @param key
* : 存储的key
* @return
*/
public static boolean getBoolean(Context context, String key)
{
SharedPreferences sp = getSp(context);
return sp.getBoolean(key, false);
}
/**
* 通过SP获得boolean类型的数据,没有默认为false
*
* @param context
* : 上下文
* @param key
* : 存储的key
* @param defValue
* : 默认值
* @return
*/
public static boolean getBoolean(Context context, String key, boolean defValue)
{
SharedPreferences sp = getSp(context);
return sp.getBoolean(key, defValue);
}
/**
* 设置int的缓存数据
*
* @param context
* @param key
* :缓存对应的key
* @param value
* :缓存对应的值
*/
public static void setBoolean(Context context, String key, boolean value)
{
SharedPreferences sp = getSp(context);
SharedPreferences.Editor edit = sp.edit();// 获取编辑器
edit.putBoolean(key, value);
edit.commit();
}
public static int getInt(Context context, String key, int defValue)
{
SharedPreferences sp = getSp(context);
return sp.getInt(key, defValue);
}
public static String getString(Context context, String key, String defValue)
{
SharedPreferences sp = getSp(context);
return sp.getString(key, defValue);
}
/**
* 设置int的缓存数据
*
* @param context
* @param key
* :缓存对应的key
* @param value
* :缓存对应的值
*/
public static void setInt(Context context, String key, int value)
{
SharedPreferences sp = getSp(context);
SharedPreferences.Editor edit = sp.edit();// 获取编辑器
edit.putInt(key, value);
edit.commit();
}
public static void setString(Context context, String key, String value)
{
SharedPreferences sp = getSp(context);
SharedPreferences.Editor edit = sp.edit();// 获取编辑器
edit.putString(key, value);
edit.commit();
}
public static void setInt(Context context, String key, String value)
{
SharedPreferences sp = getSp(context);
SharedPreferences.Editor edit = sp.edit();// 获取编辑器
edit.putString(key, value);
edit.commit();
}
}
.
.
判断服务是否存活的工具类
public class ServiceUtils {
/**
* 判断某个服务是否正在运行的方法
*
* @param mContext
* @param serviceName 是包名+服务的类名(例如:aa.bb.com.HomeWatchService)
* @return true代表正在运行,false代表服务没有正在运行
*/
public static boolean isServiceWork(Context mContext, String serviceName) {
boolean isWork = false;
ActivityManager myAM = (ActivityManager) mContext
.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> myList = myAM.getRunningServices(40);
if (myList.size() <= 0) {
return false;
}
for (int i = 0; i < myList.size(); i++) {
String mName = myList.get(i).service.getClassName().toString();
if (mName.equals(serviceName)) {
isWork = true;
break;
}
}
return isWork;
}
}
本篇完。
网友评论