安卓程序进入后台和前台的判断

按照最简单的思路,一个app的所有Activity全部onStop后就算进入后台,但可能用户只是切出去看了一眼别的程序又跳了回来。从技术上来说,这种判断没有问题,但从用户活跃的角度来讲,用户并没有真正离开我们的app。还有一种情况,就是需要在程序进入前后台的时候,执行一些操作,如果用户切换的十分频繁,操作又比较耗时可能会产生一定的效率低下和浪费。因此最简单的解决办法就是添加一个延时,定义一个我们app的前后台概念,即app不可见后一段时间才当做进入后台,而一旦从后台状态显示出来就当做进入前台。

想要判断app进入了后台,就要关注app中每一个activity的声明周期,比较笨的方法是整个项目中所有Activity都使用同一个基类,在这个基类中重写声明周期方法来进行监控。

但还有一种简单的方式,可以监听整个app所有Activity的生命周期变化:Application.registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks)。这个方法可以注册多个callback,不是只能注册一个,因此即使有其他地方(统计SDK经常使用)使用了这个也没有影响。

判断Activity的显示和隐藏需要使用Activity生命周期中成对出现的方法,在显示的时候设置为前台,在隐藏的时候设置为后台。

首先考虑的是onStart/onStop,但是很快发现了问题,在一个Activity中start另一个Activity,第一个Activity的onStop方法是在第二个Activity的onStart之后执行的,会导致最后一次判定前后台发生错误。把onStart简记为左括号,onStop简记为右括号,则执行完启动另一个Activity的操作之后,执行的序列为:"(()",可以看出仍有Activity在显示状态,但最后一次执行的却是onStop,而onStop就会去设置为后台状态,所以状态是不对的。也可以通过其他手段纠正这个逻辑,但无疑比较复杂。

可以简单地使用onResume/onPause来替代onStart/onStop,这两个方法的调用序列用括号形式表示是这样的:"()(",最后有Activity为显示状态,最后的调用也是onResume设置前台的方法。因此采用onResume/onPause来判断前后台的状态。

定义一个负责后台判断和管理的类,这个类实现Application.ActivityLifecycleCallbacks来监听Activity声明周期变化,并维护当前状态和分发状态改变回调。

package com.ajeyone.sample1;

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;

import java.util.ArrayList;

public final class BackgroundManager implements Application.ActivityLifecycleCallbacks {
    public interface BackgroundListener { // 提供给外部使用的回调
        void onBackgroundStateChanged(); // 发生变化时回调,可通过isBackground方法判断当前状态
    }

    private static final String TAG = "BackgroundManager";

    private boolean mBackground;
    private final long mDelay;
    private final ArrayList<BackgroundListener> mBackgroundListeners = new ArrayList<>();
    private final Handler mHandler = new Handler();
    private final Runnable mChangeToBackgroundRunnable = new Runnable() { // 延迟执行的Runnable,设置为后台有一个延迟
        @Override
        public void run() {
            setBackground(true);
        }
    };

    BackgroundManager(long delay) { // 包访问权限,不能在其他地方随意创建对象,当然使用singleton也可以。
        mDelay = delay; // delay时间由外部决定
    }

    public void registerBackgroundListener(BackgroundListener listener) { // 需要监听可以使用这个方法注册回调
        synchronized (mBackgroundListeners) {
            mBackgroundListeners.add(listener);
        }
    }

    public void unregisterBackgroundListener(BackgroundListener listener) { // 保证不使用的时候注销,尤其是Activity使用时
        synchronized (mBackgroundListeners) {
            mBackgroundListeners.remove(listener);
        }
    }

    public boolean isBackground() {
        return mBackground;
    }

    private void setBackground(boolean background) {
        Log.d(TAG, "setBackground: " + background);
        boolean old = mBackground;
        mBackground = background;
        if (old != background) { // 防止多余的回调,只有在发生变化时才通知
            Log.d(TAG, "setBackground: state changed");
            for (BackgroundListener listener : mBackgroundListeners) {
                listener.onBackgroundStateChanged();
            }
        }
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.d(TAG, "onActivityCreated: " + activity);
    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.d(TAG, "onActivityStarted: " + activity);
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.d(TAG, "onActivityResumed: " + activity);
        mHandler.removeCallbacks(mChangeToBackgroundRunnable); // 取消延时执行的Runnable
        setBackground(false); // 只要显示出来,直接设置为前台,如果没显示前就已经是前台,也不会发生回调
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.d(TAG, "onActivityPaused: " + activity);
        mHandler.postDelayed(mChangeToBackgroundRunnable, mDelay); // 隐藏后开始计时,延迟执行后台转换
    }

    @Override
    public void onActivityStopped(Activity activity) {
        Log.d(TAG, "onActivityStopped: " + activity);
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.d(TAG, "onActivitySaveInstanceState: " + activity);
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.d(TAG, "onActivityDestroyed: " + activity);
    }
}

使用自定义的Application类:提供获取BackgroundManager的方法和注册Callback

package com.ajeyone.sample1;

import android.app.Application;

public class MyApplication extends Application {
    private static final long BACKGROUND_DELAY_MS = 30000;

    private static MyApplication sInstance;
    private BackgroundManager mBackgroundManager;

    @Override
    public void onCreate() {
        super.onCreate();
        sInstance = this;
        mBackgroundManager = new BackgroundManager(BACKGROUND_DELAY_MS);
        registerActivityLifecycleCallbacks(mBackgroundManager);
    }

    public static MyApplication getInstance() {
        return sInstance;
    }

    public BackgroundManager getBackgroundManager() {
        return mBackgroundManager;
    }
}

需要监听的Activity:

package com.ajeyone.sample1;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity implements BackgroundManager.BackgroundListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyApplication.getInstance().getBackgroundManager().registerBackgroundListener(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        MyApplication.getInstance().getBackgroundManager().unregisterBackgroundListener(this); // 不要忘记注销
    }

    @Override
    public void onBackPressed() {
        moveTaskToBack(true);
    }

    private void showBackgroundState() {
        boolean background = MyApplication.getInstance().getBackgroundManager().isBackground();
        Log.d("Main", "showBackgroundState: " + background);
    }

    @Override
    public void onBackgroundStateChanged() {
        showBackgroundState();
    }

    public void onOpenDetail(View view) {
        startActivity(new Intent(this, DetailActivity.class)); // 打开另一个Activity,查看前后台效果
    }
}

代码在github上:samplebackground

时间: 2024-10-14 12:16:40

安卓程序进入后台和前台的判断的相关文章

C# 后台获取前台交互判断

前台传来明细 ,判断是否修改,在把前台 的数据组成新的类保存 1 public class tt 2 { 3 public string id { get; set; } 4 public string e_id { get; set; } 5 public string name { get; set; } 6 public int age { get; set; } 7 public bool sex { get; set; } 8 9 } 10 public class t 11 { 12

iOS App 获取从后台返回前台时的页面

产品美美的给小伙伴提了一个需求,当程序从后台进入前台时,如果是指定的页面,则弹出提示框. 大家首先想到的方法就是通过 AppDelegate.h 进行控制,相对复杂的步骤就是 在程序进入后台时对当前页面进行存储,然后在从后台进入前台再判断是否是符合条件的页面,判断是否弹框.基本思想就是这样 然后 我就开始需找新的方法,然后我就开始了新的实践 新方法 比较适用于匹配页面较少的情况, 对单个UIViewController进行监听 方法来源:http://www.shangxueba.com/jin

如何判断QML应用被推送到后台和前台

在这篇文章中,我们来介绍如何判断一个QML应用被推到后台或前台.我们知道,在Ubuntu手机平台中,它是一个单应用的操作系统.当一个应用被推到后台后,应用就被挂起,不能运行.我们有时需要这个标志来判断我们的应用什么时候是在前台,什么时候是在后台. 我们用Ubuntu SDK创建一个简单的QML应用: import QtQuick 2.0 import Ubuntu.Components 1.1 /*! \brief MainView with a Label and Button element

c#后台调用前台与js方法互调

c#后台调用前台与js方法互调 分类: 每天学一点2009-10-22 00:32 1527人阅读 评论(0) 收藏 举报 c#javascript脚本buttonvbscriptstring 很多人都向在服务器端调用客户端的函数来操作,也就是在asp中调用javascript脚本中已经定义好的脚本函数.经过研究,发现了一些勉强的方法. 1. 用Response.Write方法写入脚本 比如在你单击按钮后,先操作数据库,完了后显示已经完成,可以在最后想调用的地方写上Response.Write(

RSA后台签名前台验签的应用(前台采用jsrsasign库)

写在前面 安全测试需要, 为防止后台响应数据返给前台过程中被篡改前台再拿被篡改后的数据进行接下来的操作影响正常业务, 决定采用RSA对响应数据进行签名和验签, 于是有了这篇<RSA后台签名前台验签的应用>. 我这里所谓的返给前台的数据只是想加密用户验证通过与否的字段success是true还是false, 前台拿这个success作为判断依据进行下一步的操作, 是进一步向后台发起请求还是直接弹出错误消息.照测试结果看这是个逻辑漏洞, 即使后台返回的是false, 在返回前台的过程中响应包被劫获

iOS 程序进入后台,包含用户上拉快捷菜单导致程序失去活跃的研究

今日在使用某App时候,突然发现上拉菜单.程序视频扔在播放,咦!引起了我的兴趣. 首先,列出两个方法, 第一个方法是AppDelegate的代理.当程序进入后台时候调用 - (void)applicationDidEnterBackground:(UIApplication *)application NS_AVAILABLE_IOS(4_0); 第二个方法是.注冊系统通知,name是UIApplicationWillResignActiveNotification,当程序释放活跃的时候调用.

原创翻译:iOS 应用程序 蓝牙后台运行

默认情况下,普通的CoreBluetooth 任务中的大多数,无论是Central还是peripheral ,在后台或者挂起状况下都是无法进行的.也就是说,你可以通过宣布你的应用程序支持后台处理模式来允许你的一个应用程序把你从一个挂起状态中唤醒来执行某些蓝牙相关的事件. 即使你的应用程序支持一种或者两种CoreBluetooth后台执行模式,那么他也不会永远运行.在某些时候,系统也许由于当前的前台应用程序而终止你的应用程序来释放内存,这将会导致任何活跃的或者挂起的连接会丢失.在iOS7中,Cor

学习IOS开发项目篇--如何让程序在后台保持挂起状态

程序的状态分为:前台运行,后台挂起,后台休眠,为了让项目的网络请求保持活跃状态,需要对程序进行设置. 在applicationDidEnterBackground方法中调用下面的方法,可以让程序进入挂起状态,但在未知时间内,可能会被系统设置为休眠,如果在将程序设置为播放器,并且循环播放一个MP3文件,可以保持永久挂起状态. UIBackgroundTaskIdentifier task =[application beginBackgroundTaskWithExpirationHandler:

Linux之nohup命令:实现退出终端后程序继续后台运行

转自:http://tech.ccidnet.com/art/302/20070618/1115599_1.html 简单而有用的nohup命令在UNIX/LINUX中,普通进程用&符号放到后台运行,如果启动该程序的控制台logout,则该进程随即终止. 要实现守护进程,一种方法是按守护进程的规则去编程(本站有文章介绍过),比较麻烦:另一种方法是仍然用普通方法编程,然后用nohup命令启动程序: nohup<程序名>& 则控制台logout后,进程仍然继续运行,起到守护进程的作