- 通过延时任务的实现
- 利用:“内部跳转,onPause后一定会触发OnResume,在onPause中启动延时任务,onResume中取消;一旦没有onResume,就触发任务,意味着进入了后台”
- 稳定性可靠,解决了部分机型判断processInf.importance不灵敏的问题
- 弊端:不支持不保留活动(不保留活动开启后,因为activity的destory,主线程的延时任务被取消掉,即便后台了,也不触发监听)
- 通过计数的实现
- 利用onStart和onStop的配对出现原则:“内部跳转,当我们从activity A到Activity B时,执行的生命周期的执行顺序如下:
A.onPause -> B.onCreate -> B.onStart -> B.onResume -> A.onStop” - 注意:第一次启动也会认为是后台转前台
- 弊端:不支持屏幕旋转(当我们旋转设备时activity将会重建,onStart方法将被再次调用,这时将会错误的判断为app第二次被打开。所以若是APP支持水平模式,则不能采用这个办法)
- 利用onStart和onStop的配对出现原则:“内部跳转,当我们从activity A到Activity B时,执行的生命周期的执行顺序如下:
- onStop生命周期中检测代码当前运行在前台的进程不是我们自己的进程
- 在Activity的onStop生命周期中执行检测代码,如果发现当前运行在前台的进程不是我们自己的进程,说明应用切到了后台:当应用切到后台的时候,运行在前台的进程由我们的app变成了桌面app,依据这一点,我们可以实现检测应用前后台切换的功能
- 部分手机的前台进程判断有问题
一、通过延时任务的实现
1.1 目前实现方案存在的问题
由于口袋助理Android客户端需要在用户切换到后台时开启自动签到以及推送,但是在Android API并没有提供开发者监听前台切换的回调函数,所以开发者必须另辟蹊径,自己实现对前台切换监听。
之前的方案让app中所有的activity继承与一个公共的activity(例如:BaseActivity),然后在BaseActivity的onStop()中判断当前程序是否处于后台,这种做法虽然在大部分应用场景有效,但是也存在一些问题:
1、用户点击Home键退出应用时,在部分机型(三星SCH_N719)上在onStop()马上调用isBackground()会发现应此时为完全退到后台(processInf.importance=IMPORTANCE_PERCEPTIBLE),过一会儿才会完全推到后台,会造成判断失效;
2、随着第三方SDK的加入,在部分机型处于调用isBackground()会发现无法完全退到后台(processInf.importance= IMPORTANCE_SERVICE),会造成判断失效;
3、调用系统界面时也会激活前台切换逻辑;
1.2 监听前后台切换新的实现
原理:
如果用户在app的的不同activity中跳转,即由 activity_A 跳转到 activity_B,在执行完activity_A的onPause()后必定会执行activity_B的onResume()方法;如果用户从app跳转到外面,即由 activity_A 跳转到 系统activity,在执行完activity_A的onPause()后不会再执行app中activity的onResume()。
因此,我们可以在系统的activity的onPause()方法中执行一个开始两秒后执行的线程,然后在onResume()有执行关闭线程的方法。这样如果用户在app内跳转在由于执行onPause()后必定会执行onResume(),因此不会触发线程的逻辑,如果从app跳转到外面,由于不会执行后面的onResume()方法,因此就会触发线程的逻辑。
1.3 关键代码:
package com.sangfor.pocket;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.os.Handler;
import com.sangfor.pocket.log.LogCore;
/**
* 监听app 前后台切换
* Created by wangjf on 2016/7/19.
*/
public class BackgroundListener implements Application.ActivityLifecycleCallbacks{
private static final String TAG = "BackgroundListener";
private static final long CHECK_DELAY = 2000;
private boolean ready2Background = false;//准备进入后台
private boolean reallyInBackground = false;//真正进入后台
private boolean ignoreOnce = false;
private Handler handler = new Handler();
private Runnable check;
private Listener mListener;
public interface Listener {
void onBecameBackground(Activity activity);
void onBecameForeground(Activity activity);
}
public BackgroundListener(){
reallyInBackground = true;
}
public void addListener(Listener listener){
mListener = listener;
}
public void ignoreOnce(){
ignoreOnce = true;
}
public void cancelIgnore(){
ignoreOnce = false;
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
@Override
public void onActivityStarted(Activity activity) {}
@Override
public void onActivityStopped(Activity activity) {}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
@Override
public void onActivityDestroyed(Activity activity) {}
@Override
public void onActivityResumed(Activity activity) {
ready2Background = false;
if(check != null){
handler.removeCallbacks(check);
check = null;
}
if(reallyInBackground){
reallyInBackground = false;
if(!ignoreOnce && mListener != null){
mListener.onBecameForeground(activity);
LogCore.noStackI(TAG, "onActivityResumed() 应用切换到前台 并触发前后台切换逻辑");
}else{
LogCore.noStackI(TAG, "onActivityResumed() 应用切换到前台 但不触发前后台切换逻辑");
}
}
ignoreOnce = false;
}
@Override
public void onActivityPaused(final Activity activity) {
if(!ready2Background){
ready2Background = true;
handler.postDelayed(check = new Runnable() {
@Override
public void run() {
if (ready2Background) {
reallyInBackground = true;
if (!ignoreOnce && mListener != null) {
mListener.onBecameBackground(activity);
LogCore.noStackI(TAG, "onActivityPaused() 应用切换到后台 并触发前后台切换逻辑");
} else {
LogCore.noStackI(TAG, "onActivityPaused() 应用切换到后台 但不触发前后台切换逻辑");
}
}
}
}, CHECK_DELAY);
}
}
public boolean isReallyInBackground(){
if(reallyInBackground){
LogCore.i(TAG, "应用在后台");
}else{
LogCore.i(TAG, "应用在前台");
}
return reallyInBackground;
}
public void setBackgroundState(boolean isBackground){
reallyInBackground = isBackground;
}
}
二、onStart和onStop方法中用变量count计数
我们需要先明确一个生命周期规律:
MainActivity跳转到SecondActivity:
11-06 14:59:09.821 16683-16683/sangfor.com.teststaticlife I/MainActivity: onPause()
11-06 14:59:09.841 16683-16683/sangfor.com.teststaticlife I/SecondActivity: onCreate()
11-06 14:59:09.851 16683-16683/sangfor.com.teststaticlife I/SecondActivity: onStart()
11-06 14:59:09.851 16683-16683/sangfor.com.teststaticlife I/SecondActivity: onPostCreate()
11-06 14:59:09.851 16683-16683/sangfor.com.teststaticlife I/SecondActivity: onResume()
11-06 14:59:09.851 16683-16683/sangfor.com.teststaticlife I/SecondActivity: onPostResume()
11-06 14:59:09.861 16683-16683/sangfor.com.teststaticlife I/SecondActivity: onAttachedToWindow()
11-06 14:59:09.901 16683-16683/sangfor.com.teststaticlife I/SecondActivity: onWindowFocusChanged():true
11-06 14:59:09.941 16683-16683/sangfor.com.teststaticlife I/MainActivity: onWindowFocusChanged():false
11-06 14:59:10.311 16683-16683/sangfor.com.teststaticlife I/MainActivity: onStop()
也就是说:当我们从activity A到Activity B时,执行的生命周期的执行顺序如下:
A.onPause -> B.onCreate -> B.onStart -> B.onResume -> A.onStop
因此我们在Activity的onStart和onStop方法中用变量count计数,在onStart中先判断是否等于0再将变量加1,onStop中先减1再判断是否等于0
假设应用有两个Activity,分别为A和B。
情况1,首先启动A,A再启动B,然后关掉B:
- A启动后(onStart后),count=1;
- A启动B,先B.onStart 然后A.onStop,count先加1后减1,count为1。
- 关掉B,先A.onStart 然后B.onStop,count先加1后减1,count为1
情况2,首先启动A,然后按Home键返回桌面:
- A启动后(onStart后),count=1;
- 按Home键返回桌面,会执行A.onStop,count的计数变位0。
2.1 关键代码
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
/**
* Created by xufangzhen on 2016/6/23.
*/
public class BaseTaskSwitch implements Application.ActivityLifecycleCallbacks {
public int mCount = 0;
private OnTaskSwitchListener mOnTaskSwitchListener;
private static BaseTaskSwitch mBaseLifecycle;
public static BaseTaskSwitch init(Application application) {
if (null == mBaseLifecycle) {
mBaseLifecycle = new BaseTaskSwitch();
application.registerActivityLifecycleCallbacks(mBaseLifecycle);
}
return mBaseLifecycle;
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
@Override
public void onActivityStarted(Activity activity) {
if (mCount++ == 0) {
mOnTaskSwitchListener.onTaskSwitchToForeground();
}
}
@Override
public void onActivityResumed(Activity activity) {}
@Override
public void onActivityPaused(Activity activity) {}
@Override
public void onActivityStopped(Activity activity) {
if (--mCount == 0) {
mOnTaskSwitchListener.onTaskSwitchToBackground();
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
@Override
public void onActivityDestroyed(Activity activity) {}
public void setOnTaskSwitchListener(OnTaskSwitchListener listener) {
mOnTaskSwitchListener = listener;
}
public interface OnTaskSwitchListener {
void onTaskSwitchToForeground();
void onTaskSwitchToBackground();
}
}
在你的Application子类中的onCreate方法中调用:
@Override
public void onCreate() {
super.onCreate();
//前后台切换
BaseTaskSwitch.init(this).setOnTaskSwitchListener(new BaseTaskSwitch.OnTaskSwitchListener() {
@Override
public void onTaskSwitchToForeground() {
LogUtils.d("切换到前台");
}
@Override
public void onTaskSwitchToBackground() {
LogUtils.d("切换到后台");
}
});
}
或者直接:
public class MainApplication extends Application {
private int count = 0;
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityStopped(Activity activity) {
Log.v("LifecycleCallbacks", activity + "onActivityStopped");
count--;
if (count == 0) {
Log.v("LifecycleCallbacks", ">>>>>>>>>>>>>>>>>>>切到后台 lifecycle");
}
}
@Override
public void onActivityStarted(Activity activity) {
Log.v("LifecycleCallbacks", activity + "onActivityStarted");
if (count == 0) {
Log.v("LifecycleCallbacks", ">>>>>>>>>>>>>>>>>>>切到前台 lifecycle");
}
count++;
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.v("LifecycleCallbacks", activity + "onActivityCreated");
}
@Override
public void onActivityResumed(Activity activity) {
Log.v("LifecycleCallbacks", activity + "onActivityResumed");
}
@Override
public void onActivityPaused(Activity activity) {
Log.v("LifecycleCallbacks", activity + "onActivityPaused");
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.v("LifecycleCallbacks", activity + "onActivitySaveInstanceState");
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.v("LifecycleCallbacks", activity + "onActivityDestroyed");
}
});
}
}
三、onStop生命周期中检测代码当前运行在前台的进程不是我们自己的进程
当应用切到后台的时候,运行在前台的进程由我们的app变成了桌面app,依据这一点,我们可以实现检测应用前后台切换的功能。在Activity的onStop生命周期中执行检测代码,如果发现当前运行在前台的进程不是我们自己的进程,说明应用切到了后台。
想想为什么要在onStop中检测,而不是onPause?这是由于A启动B时,生命周期的执行顺序如下:A.onPause->B.onCreate->B.onStart->B.onResume->A.onStop,也就是说,在A的onPause方法中,B的生命周期还没有执行,进程没有进入前台,当然是检测不到的。我们把代码移到onPause生命周期中,发现确实没有效果
/用来控制应用前后台切换的逻辑
private boolean isCurrentRunningForeground = true;
@Override
protected void onStart() {
super.onStart();
if (!isCurrentRunningForeground) {
Log.d(TAG, ">>>>>>>>>>>>>>>>>>>切到前台 activity process");
}
}
@Override
protected void onStop() {
super.onStop();
isCurrentRunningForeground = isRunningForeground();
if (!isCurrentRunningForeground) {
Log.d(TAG,">>>>>>>>>>>>>>>>>>>切到后台 activity process");
}
}
public boolean isRunningForeground() {
ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses();
// 枚举进程
for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessInfos) {
if (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
if (appProcessInfo.processName.equals(this.getApplicationInfo().processName)) {
Log.d(TAG,"EntryActivity isRunningForeGround");
return true;
}
}
}
Log.d(TAG, "EntryActivity isRunningBackGround");
return false;
}