android监测程序进入后台以及从后台返回

最近项目需要监测android程序从后台返回的事件,百度了下,没有什么系统的总结,于是上StackOverFlow找到了个关于这个问题的讨论:How to detect when an Android app goes to the background and come back to the foreground。讨论过程中出现了几种都比较可行的方法,在此总结一下,供大家参考。

一、定时器方法

在按Home键的时候,Activity会调用onResume和onPause方法,重写这两个方法是一个基本入口。但是,在正常跳转的时候,Activity也会调用onResume和onPause方法,所以必须要对onResume和onPause的调用做一个判断,从而忽略掉正常跳转时的调用问题。那么怎么来实现这个判断呢?于是就有人想到了利用时间来判断:正常跳转过程中,从前一个Activity的onPause被调用,到新的Activity的onResume被调用,应该在一个很短的时间内就可以完成(除非你的Activity在onCreate中干了耗时操作);而进入桌面再从桌面返回的速度就会慢很多(需要人手动操作,相比跳转的毫秒级时间确实差距比较明显)。根据这个思路,就可以利用计时器去解决这个问题了。主要代码逻辑如下:
首先,你需要定义一个全局的Timer和TimerTask,确保在Activity之间可以共享,可以放在自定义Application中,也可以使用单例模式。Application示例如下:
public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    // 阀值,可定义为一个可接受的时间(足够Activity跳转,来不及从桌面返回)
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000; 
    
    public void startActivityTransitionTimer() { 
        this.mActivityTransitionTimer = new Timer();
        this.mActivityTransitionTimerTask = new TimerTask() {
            public void run() {
                MyApplication.this.wasInBackground = true;
            }
        };

        this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
                                           MAX_ACTIVITY_TRANSITION_TIME_MS);
    }

    public void stopActivityTransitionTimer() {
        if (this.mActivityTransitionTimerTask != null) {
            this.mActivityTransitionTimerTask.cancel();
        }

        if (this.mActivityTransitionTimer != null) {
            this.mActivityTransitionTimer.cancel();
        }

        this.wasInBackground = false;
    }
}
然后,再你的BaseActivity中(我相信一个合格的开发人员都会定义这样一个Activity的基类),重写onResume和onPause方法:
@Override
public void onResume()
{
    super.onResume();

    MyApplication myApp = (MyApplication)this.getApplication();
    // 如果定时器已经结束了,wasInBackgroudn就会变成true,这个时候onResume就是从背景返回
    if (myApp.wasInBackground){
        //从背景返回了
    }

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    // 启动定时器
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}
这个方法简单易懂,准确率通常也会比较高。不过也有不足的地方,比如没有办法监测进入背景的事件。同时,由于每次跳转都会开启一个计时器任务,存在一定程度上的内存浪费(不过基本可以忽略)。

二、onTrimMemory监听(API>=14)

在API14中,android提供了一个新的状态监听的接口 ComponentCallbacks2。在当中的onTrimMemory方法中,提供了一个状态参数 TRIM_MEMORY_UI_HIDDEN。官方文档对这个状态的解释如下:
“Level for onTrimMemory(int): the process had been showing a user interface, and is no longer doing so. Large allocations with the UI should be released at this point to allow memory to be better managed.” 该进程已经展示一个UI界面(onCreate完成?),并且停止了展示。UI中分配了的大块内存应该在这个时候被释放,以保证更佳的内存管理。
所以,这应该算是一个“正式”的进入后台标志了,之所以打引号,是因为这个状态本意上并不是为了监听进入后台而设计的,它只是为了让开发人员能够在程序进入后台的时候去释放一些内存。总而言之,这也是一种方法。
首先,你需要实现一个ComponentCallbacks2接口,并监听TRIM_MEMORY_UI_HIDDEN状态:
public class MemoryBoss implements ComponentCallbacks2 {
    @Override
    public void onConfigurationChanged(final Configuration newConfig) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // 进入后台
        }
        // 如果有必要,你也可以进行一些清理内存操作
    }
}
然后,在BaseActivity注册监听:
MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
   super.onCreate();
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      mMemoryBoss = new MemoryBoss();
      registerComponentCallbacks(mMemoryBoss);
   } 
}

@Override
public void onDestroy(){
    super.onDestroy();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        unregisterComponentCallbacks(mMemoryBoss);
    }
}
如果想要监听从后台返回的事件的话,在进入后台的时候设置个boolean值,然后在onResume时候进行判断即可。如果onResume的时候,程序已经进入后台,则可以判断是从后台返回的了。准确率是100%,但是只支持API14及之后。

三、计数方法

在Activity跳转的时候,总是先调用新Activity的onStart方法,然后再调用旧Activity的onStop方法。因此,可以利用这个逻辑,做一个Activity的计数效果,在onStart的时候加1,在onStop的时候减一。那么跳转的时候,因为是先加后减,所以值是恒大于0的。而进入后台的时候,先减后加,会出现等于0的效果。实现代码如下所示:
public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

    @Override
    protected void onStart() {
        super.onStart();
	if (sessionDepth == 0) {
	    // 从后台返回
	}       
        sessionDepth++;
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (sessionDepth > 0)
            sessionDepth--;
        if (sessionDepth == 0) {
            // 进入后台
        }
    }

}
这个方法就显得比较优美了,代码逻辑简单,效果也不错。

四、利用onWindowFocusChanged的方法

这个方法是利用onWindowFocusChanged事件来进行跳转的判断。这个方法的原理和计数法类似,都是利用onStart在onStop之前被调用。当Activity结束时,activity会首先失去焦点,因此,可以通过这个事件去获取进入后台事件。代码如下:
    // 用于判断是否从后台返回或者是否到后台
    public static boolean isAppWentToBg = false;
    public static boolean isWindowFocused = false;
    public static boolean isBackPressed = false;

    @Override
    public void onBackPressed() {
        isBackPressed = true;
	super.onBackPressed();
    }

    @Override
    protected void onStart() {
        if (isAppWentToBg) {
            isAppWentToBg = false;
            // 从后台返回
        }
        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (!isWindowFocused) {
            isAppWentToBg = true;
            // 进入后台
        }
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        isWindowFocused = hasFocus;
        if (isBackPressed && !hasFocus) {
            isBackPressed = false;
            isWindowFocused = true;
        }
        super.onWindowFocusChanged(hasFocus);
    }
同样的,这个逻辑准确率也高。同时,它还加入了判断返回键的逻辑,更加人性化。

五、总结

从理论上来说,监测后台和程序之间的跳转事件,应该是需要从onStart和onStop入手的。因为在官方文档中,对这两个方法的解释是这样的:
onStart:Called when the activity is becoming visible to the user.当Activity可见时被调用
onStop:Called when the activity is no longer visible to the user, because another activity has been resumed and is covering this one. This may happen either because a new activity is being started, an existing one is being brought in front of this one, or this one is being destroyed.当Activity不可见时被调用,因为另一个Activity覆盖到了当前Activity上。这可能是由于一个新的Activity被启动了,一个已经存在的Activity被重新显示了,或者当前Activity被销毁了
因此,上述逻辑中的重写onResume和onPause操作都应当改为onStart和 onStop。上述的各种方法都是可用的,准确率也有保障,因此可以根据理解自行选择。
另外,在我自己实现这个逻辑的过程中,发现了这样一种现象。如果当前页面存在多个Activity的层级,从后台返回时会先调用第二个Activity的onStart,而不是栈顶的Activity。这可能是因为我设置了滑动返回的效果,即在栈顶Activity滑动过程中,之前的Activity会被显示出来。因此,必须保证两个Activity都是可见状态。这种情况下,想要在栈顶Activity实现返回弹出一个对话框的效果,如果直接按上述逻辑进行处理,则对话框会被显示在之前的Activity中,然后被新的Activity给覆盖掉。于是,我在监测到返回事件的时候,作了一个延迟操作,然后通过动态的获取栈顶Activity来弹出对话框,进而解决了这个问题。


总得来说,研究这个的过程还是十分的有趣,也让我一直以来对onStart和onStop的误解得到了修正。还是不得不吐槽一下,Google还是比百度强大很多。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值