(4.3.2.11)Android前后台判断逻辑和切换回调的实现

  • 通过延时任务的实现
    • 利用:“内部跳转,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支持水平模式,则不能采用这个办法)
  • 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;  
  }  

参考文献

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值