开发中遇到了一个需求,如果应用在非前台的状态超过一定时间,就需要用户重新去登录。如果用户不进行登录,就回到主页。这种类似的需求我在银行类App中见到过。技术点就是如何判断App当前的状态。
但是这里面状态有很多,并不只是前后台。还有App在前台,但是用户锁屏放置的情况,这种情况,也是需要算作用户未使用App的,所以需要判断的状态如下。
应用状态:
- 从后台切换到前台
- 从前台切换到后台
- 用户锁屏
- 用户解锁手机
总结出需要判断的内容之后,可以开始根据不同状态编写代码了。
判断前后台切换
首先使用ActivityLifecycleCallbacks接口来接收每一个Activity生命周期的回调,因为我们不知道用户会在哪个Activity中切换App状态,所以需要这个回调来统一处理。
这里解释两个回调方法:
- onActivityResumed
这里是当App到onResume时统一回调,当应用从后台返回前台时候也会走这个方法。
- onActivityStopped
这里是当App到onStop时统一回调,当应用从前台进入时候也会走这个方法。!!注意:这个方法不一定会执行(比如切换最近使用APP列表时),所以需要配合onTrimMemory一起使用。
onTrimMemory是个很神奇的方法,他实际上是App关于内存优化的一个回调。用于让应用程序在不同的状态下进行内存释放,从而避免被系统杀掉进程。本来与前后台切换并没有什么关系,但是当阅读文档时,发现他在这种状态下是会被回调的:
TRIM_MEMORY_UI_HIDDEN
它表示应用程序的所有UI界面被隐藏了,即用户点击了Home键或者Back键导致应用的UI界面不可见.这时候应该释放一些资源。
那么我们可以通过这个状态来判断用户是否切换到后台,与onStop方法配合使用。
补充:TRIM_MEMORY_BACKGROUND
也是应用进入后台的回调,不同的手机厂商可能会使用这两个不同的Flag。最好都进行一下判断。
需要用于判断的回调都准备好了,下面来准备一下需要的常量和变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public static final int STATE_NORMAL = 0;
public static final int STATE_BACK_TO_FRONT = 1;
public static final int STATE_FRONT_TO_BACK = 2;
public static int sAppState = STATE_NORMAL;
private boolean isBackFlag = false;
private boolean background = false;
private static long frontToBackTime;
|
常量准备好后,现在开始编写判断部分的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityResumed(Activity activity) { if (isBackFlag || background) { isBackFlag = false; background = false; sAppState = STATE_BACK_TO_FRONT; } else { sAppState = STATE_NORMAL; } }
@Override public void onActivityStopped(Activity activity) { if (!DeviceUtils.isCurAppTop(activity)) { sAppState = STATE_FRONT_TO_BACK; frontToBackTime = System.currentTimeMillis(); isBackFlag = true; } } });
@Override public void onTrimMemory(int level) { super.onTrimMemory(level); if (level == Application.TRIM_MEMORY_UI_HIDDEN || level == TRIM_MEMORY_BACKGROUND) { background = true; } else if (level == Application.TRIM_MEMORY_COMPLETE) { background = !DeviceUtils.isCurAppTop(this); } if (background) { frontToBackTime = System.currentTimeMillis(); sAppState = STATE_FRONT_TO_BACK; } else { sAppState = STATE_NORMAL; } }
|
isCurAppTop是用来判断程序是否是前台进程的工具类,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public static boolean isCurAppTop(Context context) { if (context == null) { return false; } String curPackageName = context.getPackageName(); ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> list = am.getRunningTasks(1); if (list != null && list.size() > 0) { ActivityManager.RunningTaskInfo info = list.get(0); String topPackageName = info.topActivity.getPackageName(); String basePackageName = info.baseActivity.getPackageName(); if (topPackageName.equals(curPackageName) && basePackageName.equals(curPackageName)) { return true; } } return false; }
|
判断用户是否进行锁屏解锁
上面的判断方法虽然已经很完整了,但是当用户将App放置前台并锁屏之后,不会触发上面的流程,那么需要对用户锁屏与解锁的状态进行判断了。
这时候我们需要在onCreate注册一个广播接收器,来接收系统锁屏开屏的广播。
这里简单介绍一下相关的三个状态:
- Intent.ACTION_SCREEN_ON
用户点亮屏幕
- Intent.ACTION_SCREEN_OFF
用户锁屏操作
- Intent.ACTION_USER_PRESENT
用户解锁屏幕,这时可以看到App了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| private class ScreenBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_SCREEN_ON.equals(action)) { } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { if (sAppState != STATE_FRONT_TO_BACK){ isBackFlag = true; sAppState = STATE_FRONT_TO_BACK; frontToBackTime = System.currentTimeMillis(); } } else if (Intent.ACTION_USER_PRESENT.equals(action)) { } } }
private void startScreenBroadcastReceiver() { ScreenBroadcastReceiver mScreenReceiver = new ScreenBroadcastReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_USER_PRESENT); this.registerReceiver(mScreenReceiver, filter); }
|
用户如果不进行登录,返回主页
在App类中,创建管理Activity栈的对象。这里面需要一个工具类。ApplicationActivitiesQueue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| import android.app.Activity; import java.util.Stack;
public class ApplicationActivitiesQueue { private ApplicationActivitiesQueue() { }
private static ApplicationActivitiesQueue queue = new ApplicationActivitiesQueue();
public static ApplicationActivitiesQueue ShareActivityQueue() { return queue; }
private Stack<Activity> activityStack = new Stack<Activity>();
public Activity currentActivity() { return activityStack.lastElement(); }
public void addActivity(Activity activity) { activityStack.push(activity); }
public void popCurrentActivity(Activity activity) { activityStack.remove(activity); }
public void finishOneActivity(Activity activity) { if (activity != null) { if (!activity.isFinishing()) { activity.finish(); } } }
public void finishOneActivity(Class<?> cls) { for (Activity activity : activityStack) { if (!activity.getClass().equals(cls)) continue; finishOneActivity(activity); return; } }
public void finishToActiovity(Class<?> cls) { while (!activityStack.lastElement().getClass().equals(cls)) { activityStack.pop().finish(); if (activityStack.size() == 0) return; } }
public void finishExcludeActivityAllActivity(Class<?> cls) { for (Activity activity : activityStack) { if (activity == null) continue; if (activity.getClass().equals(cls)) continue; finishOneActivity(activity); } }
public void finishAllActivity() { for (Activity activity : activityStack) { if (activity == null) continue; finishOneActivity(activity); } }
}
|
使用方法:
在registerActivityLifecycleCallbacks
中的onActivityCreated
和onActivityDestroyed
将当前Activity添加入视图栈中,之后按需调用视图栈的方法即可。
1 2 3 4 5 6 7 8 9 10 11 12 13
| private ApplicationActivitiesQueue activitiesQueue = ApplicationActivitiesQueue.ShareActivityQueue();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { activitiesQueue.addActivity(activity); } @Override public void onActivityDestroyed(Activity activity) { activitiesQueue.popCurrentActivity(activity); } });
|
参考