如何检测当一个Android应用程序去后台和回到前台

我试图写一个应用程序,做一些具体的时候,它被带回前台后一段时间。是否有一种方法可以检测应用程序是被发送到后台还是被带到前台?

369926 次浏览

onPause()onResume()方法在应用程序被移到后台并再次移到前台时被调用。但是,在应用程序第一次启动时和关闭之前也会调用它们。你可以在Activity中阅读更多。

没有任何直接的方法来获得应用程序状态,而在后台或前台,但即使我也面临这个问题,并找到了解决方案与onWindowFocusChangedonStop

更多细节请查看这里Android:解决方案来检测当一个Android应用程序去后台,回到前台没有getRunningTasks或getRunningAppProcesses

ActivityLifecycleCallbacks可能是有趣的,但它没有很好的文档。

不过,如果你调用registerActivityLifecycleCallbacks(),你应该能够在activity被创建、销毁等时候得到回调。你可以为活动调用getComponentName()。

这些答案似乎不正确。当另一个活动开始和结束时,也会调用这些方法。你能做的是保留一个全局标志(是的,全局不好:),并在每次启动一个新活动时将其设置为true。在每个活动的onCreate中将其设置为false。然后,在onPause中检查这个标志。如果为假,你的应用就会进入后台,或者被杀死。

如果你的应用由多个活动和/或堆叠的活动组成,就像一个标签栏小部件,那么覆盖onPause()和onResume()将不起作用。例如,当启动一个新的活动,当前的活动将得到暂停之前,新的一个被创建。当完成一个活动(使用“后退”按钮)时,也同样适用。

我发现有两种方法似乎很有效。

第一个需要GET_TASKS权限,由一个简单的方法组成,通过比较包名来检查设备上运行最多的活动是否属于应用程序:

private boolean isApplicationBroughtToBackground() {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
return true;
}
}


return false;
}

这种方法是在Droid-Fu(现在称为Ignition)框架中发现的。

我自己实现的第二个方法不需要GET_TASKS权限,这很好。相反,它的实现有点复杂。

在MainApplication类中,有一个变量用于跟踪应用程序中正在运行的活动的数量。在onResume()中为每个活动增加变量,在onPause()中减少变量。

当正在运行的activity数量达到0时,如果满足以下条件,应用程序将被放入后台:

  • 正在暂停的活动没有结束(使用了“后退”按钮)。这可以通过使用activity.isFinishing()方法来完成。
  • 没有启动一个新活动(相同的包名)。你可以重写startActivity()方法来设置一个变量来指示这一点,然后在onPostResume()中重置它,这是创建/恢复活动时运行的最后一个方法。

当您可以检测到应用程序已经退出到后台时,当它被带回前台时也很容易检测到。

我所做的是确保所有应用程序内的活动启动startActivityForResult,然后检查onActivityResult是否在onResume之前被调用。如果不是,这意味着我们刚刚从应用程序之外的某个地方返回。

boolean onActivityResultCalledBeforeOnResume;


@Override
public void startActivity(Intent intent) {
startActivityForResult(intent, 0);
}


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
onActivityResultCalledBeforeOnResume = true;
}


@Override
protected void onResume() {
super.onResume();
if (!onActivityResultCalledBeforeOnResume) {
// here, app was brought to foreground
}
onActivityResultCalledBeforeOnResume = false;
}

编辑2:我在下面写的东西实际上是行不通的。谷歌拒绝了一个包含对ActivityManager.getRunningTasks()调用的应用程序。从的文档开始,很明显这个API仅用于调试和开发目的。一旦我有时间更新下面的GitHub项目,我就会更新这篇文章,使用一个使用计时器的新方案,几乎一样好。

编辑1:我已经写了一个博客,并创建了一个简单的GitHub存储库,使这非常容易。

公认的和最高评价的答案都不是最好的方法。排名最高的答案是isApplicationBroughtToBackground()的实现,它不处理应用程序的主活动屈服于同一个应用程序中定义的活动,但它有不同的Java包的情况。我想到了一种方法,在这种情况下行得通。

在onPause()中调用它,它会告诉你你的应用程序是否因为另一个应用程序已经启动而进入后台,或者用户已经按下了home键。

public static boolean isApplicationBroughtToBackground(final Activity activity) {
ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);


// Check the top Activity against the list of Activities contained in the Application's package.
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
try {
PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
for (ActivityInfo activityInfo : pi.activities) {
if(topActivity.getClassName().equals(activityInfo.name)) {
return false;
}
}
} catch( PackageManager.NameNotFoundException e) {
return false; // Never happens.
}
}
return true;
}

考虑使用onUserLeaveHint。它只会在应用进入后台时被调用。onPause会有一些极端情况需要处理,因为它可以被用于其他原因;例如,如果用户在你的应用程序中打开另一个活动,比如你的设置页面,你的主活动的onPause方法将被调用,即使它们仍然在你的应用程序中;当你可以简单地使用onUserLeaveHint回调函数来做你所要求的事情时,跟踪正在进行的事情将导致错误。

当调用on UserLeaveHint时,你可以设置一个boolean inBackground标志为true。当onResume被调用时,如果inBackground标志被设置,只假设你回到前台。这是因为onResume也将被调用在你的主要活动,如果用户只是在你的设置菜单,从来没有离开应用程序。

记住,如果用户在你的设置界面中点击home键,onUserLeaveHint将在你的设置活动中被调用,当他们返回时,onResume将在你的设置活动中被调用。如果你的主要活动中只有这个检测代码,你就会错过这个用例。为了在所有的活动中使用这些代码而不重复代码,可以使用一个扩展activity的抽象活动类,并将您的公共代码放入其中。然后,您拥有的每个活动都可以扩展这个抽象活动。

例如:

public abstract AbstractActivity extends Activity {
private static boolean inBackground = false;


@Override
public void onResume() {
if (inBackground) {
// You just came from the background
inBackground = false;
}
else {
// You just returned from another activity within your own app
}
}


@Override
public void onUserLeaveHint() {
inBackground = true;
}
}


public abstract MainActivity extends AbstractActivity {
...
}


public abstract SettingsActivity extends AbstractActivity {
...
}
主要的问题是,当你从后台启动一个活动时,你必须得到一个特定的行为。如果你重写onPause()和onResume()方法,你会得到一个接近的答案,但不是解决方案。问题是onPause()和onResume()方法被调用,即使你不最小化你的应用程序,他们可以被调用时,你启动一个活动,然后你按下返回按钮返回到你的活动。 要消除这个问题,并真正了解应用程序何时来自后台,您必须获取正在运行的进程并与您的进程进行比较:

private boolean isApplicationBroughtToBackground() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(getPackageName())) {
return true;
}
}
return false;
}

现在你必须声明一个布尔变量:

public boolean wasPaused = false;

并询问你的活动何时进入后台:

@Override
public void onPause(){
super.onPause();
if(isApplicationBroughtToBackground())
wasPaused = true;
}

现在,当你的活动再次出现在屏幕上时,在onResume()方法中询问:

@Override
public void onResume(){
super.onResume();
if(wasPaused){
lockScreen(true);
}
wasPaused = false;
}

就是这样。现在,当您的活动进入后台,稍后用户将其带到前台时,锁定屏幕将出现。

如果你想为你的应用程序的任何活动重复这个行为,你必须创建一个活动(可以是BaseActivity),放这个方法,你所有的活动都必须从BaseActivity继承。

我希望这对你有帮助。

问候!

下面是我解决这个问题的方法。它的工作前提是,在活动转换之间使用时间参考最有可能提供足够的证据来证明应用程序是否“背景化”。

首先,我使用了一个android.app.Application实例(让我们称它为MyApplication),它有一个Timer,一个TimerTask,一个常量来表示从一个活动过渡到另一个活动可以合理地花费的最大毫秒数(我用了一个2s的值),一个布尔值来指示应用程序是否“在后台”:

public class MyApplication extends Application {


private Timer mActivityTransitionTimer;
private TimerTask mActivityTransitionTimerTask;
public boolean wasInBackground;
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;
}

这个解决方案的最后一部分是从所有活动的onResume()和onPause()事件中添加对这些方法的调用,或者,最好是在所有具体活动继承的基础活动中:

@Override
public void onResume()
{
super.onResume();


MyApplication myApp = (MyApplication)this.getApplication();
if (myApp.wasInBackground)
{
//Do specific came-here-from-background code
}


myApp.stopActivityTransitionTimer();
}


@Override
public void onPause()
{
super.onPause();
((MyApplication)this.getApplication()).startActivityTransitionTimer();
}

所以在这种情况下,当用户只是在你的应用程序的活动之间导航,离开活动的onPause()启动定时器,但几乎立即进入的新活动取消定时器之前,它可以达到最大过渡时间。因此,wasInBackground将是

另一方面,当一个Activity从Launcher来到前台,设备被唤醒,结束电话呼叫等,更有可能是定时器任务在此事件之前执行,因此wasInBackground被设置为真正的

我使用这个解决方案: http://nathanael.hevenet.com/android-dev-detecting-when-your-app-is-in-the-background-across-activities/ < / p >

简而言之——构建一个专门的服务,让每个活动向他报告每个生命周期事件,这个服务获得关于应用程序状态的信息。

很像@oldschool4664的解决方案,但在我看来更干净

在你的应用程序中添加回调并检查根活动,如下所示:

@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityStopped(Activity activity) {
}


@Override
public void onActivityStarted(Activity activity) {
}


@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}


@Override
public void onActivityResumed(Activity activity) {
}


@Override
public void onActivityPaused(Activity activity) {
}


@Override
public void onActivityDestroyed(Activity activity) {
}


@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
loadDefaults();
}
}
});
}

2018: Android通过生命周期组件原生支持这一点。

2018年3月更新:现在有一个更好的解决方案。看到ProcessLifecycleOwner。你将需要使用新的体系结构组件1.1.0(目前最新),但它是具体地说设计来做这件事的。

有一个简单的例子提供了在这个答案中,但我写了一个样例应用程序和一个博客关于它。

自从2014年我写了这篇文章以来,出现了不同的解决方案。有些工作,有些是被认为是有效的,但有缺陷(包括我的!),我们作为一个社区(Android)学会了忍受后果,并为特殊情况编写了变通方案。

永远不要假设一个代码片段就是你想要的解决方案,这是不可能的;更好的做法是,试着理解它是做什么的,以及它为什么这么做。

MemoryBoss类实际上从未被我在这里所写的那样使用过,它只是一段碰巧工作的伪代码。

除非有充分的理由不使用新的体系结构组件(确实有一些,特别是如果您的目标是超级老的api),否则就继续使用它们。他们远不是完美的,但也不是ComponentCallbacks2

更新/说明(2015年11月):人们提出了两个意见,第一个是应该使用>=而不是==,因为文档声明你不应该检查确切的值。这在大多数情况下都很好,但请记住,如果你只有关心在应用程序进入后台时执行某物,你将不得不使用== 而且将它与另一个解决方案(如活动生命周期回调)结合起来,或者你可能得不到达到你想要的效果。例子(这发生在我身上)是,如果你想==0你的应用程序的密码屏幕,当它进入后台(如1Password,如果你熟悉它),你可能不小心锁定你的应用程序,如果你运行在内存低,突然测试>= TRIM_MEMORY,因为Android会触发一个LOW MEMORY调用,这比你的高。所以要小心你测试的方式/内容。

此外,有些人问过如何检测你什么时候回来。

下面解释了我能想到的最简单的方法,但由于有些人不熟悉它,所以我在这里添加了一些伪代码。假设在你的class BaseActivity extends Activity中有YourApplicationMemoryBoss类(如果你没有的话,你需要创建一个)。

@Override
protected void onStart() {
super.onStart();


if (mApplication.wasInBackground()) {
// HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
mApplication.setWasInBackground(false);
}
}

我推荐onStart,因为对话框可以暂停一个活动,所以我打赌你不希望你的应用程序认为“它去了后台”。如果你所做的只是显示一个全屏对话框,但你的里程可能会有所不同。

就这些。if块中的代码将只执行一次,即使你去到另一个活动,新的活动(也是extends BaseActivity)将报告wasInBackgroundfalse,因此它不会执行代码直到onMemoryTrimmed被调用并且标志再次被设置为true

希望这能有所帮助。

更新/备注(2015年4月):在你对这段代码进行全部复制和粘贴之前,请注意,我已经发现了一些实例,其中它可能不是100%可靠的,并且必须组合与其他方法一起实现了最佳结果。 值得注意的是,存在两个已知的例子,其中onTrimMemory回调不保证被执行:

  1. 如果你的手机在应用程序可见时锁定了屏幕(比如你的设备在nn分钟后锁定),这个回调不会被调用(或不总是),因为锁定屏幕只是在顶部,但你的应用程序仍在“运行”;尽管覆盖。

  2. 如果您的设备内存相对较低(并且处于内存压力下),操作系统似乎会忽略这个调用,直接进入更关键的级别。

现在,取决于你知道你的应用什么时候进入后台有多重要,你可能需要也可能不需要扩展这个解决方案,同时跟踪活动生命周期等等。

只要记住以上几点,并拥有一个优秀的QA团队;)

更新结束

可能有点晚了,但在冰淇淋夹心(api14)及以上中有一个可靠的方法。

当你的应用没有更多可见的UI时,一个回调被触发。你可以在自定义类中实现回调,它被称为ComponentCallbacks2(是的,带有一个2)。这个回调是API级别14(冰淇淋三明治)及以上的只有

你基本上得到一个方法的调用:

public abstract void onTrimMemory (int level)

级别是20或更多

public static final int TRIM_MEMORY_UI_HIDDEN

我一直在测试这个,它总是有效的,因为20级只是一个“建议”。你可能想要释放一些资源,因为你的应用程序不再可见。

引用官方文件:

onTrimMemory(int)的级别:进程已经显示了一个用户界面,并且不再这样做。这时候应该释放大量的UI分配,以便更好地管理内存。

当然,您应该实现它来实现它所说的(清除在特定时间内未使用的内存,清除一些未使用的集合,等等。可能性是无限的(请参阅官方文档了解其他可能的更关键的级别)。

但是,有趣的是,操作系统告诉你:嘿,你的应用进入了后台!

这正是你一开始就想知道的。

你怎么确定你什么时候回来?

好吧,这很简单,我相信你有一个“baseactivity”;所以你可以使用你的onResume()来标记你回来的事实。因为只有当你实际接收到对上述onTrimMemory方法的调用时,你才会说你还没有回来。

它的工作原理。你不会得到假阳性。如果一项活动恢复了,那么你就百分百地回来了。如果用户再次返回,则会得到另一个onTrimMemory()调用。

您需要订阅您的Activities(或者更好的是,定制类)。

保证你总是收到这个的最简单的方法是创建一个简单的类,像这样:

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) {
// We're in the Background
}
// you might as well implement some memory cleanup here and be a nice Android dev.
}
}

为了使用它,在你的应用程序实现(你有一个,对吧?)中,执行如下操作:

MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mMemoryBoss = new MemoryBoss();
registerComponentCallbacks(mMemoryBoss);
}
}

如果你创建了一个Interface,你可以将一个else添加到那个if中,并实现在API 14以下的任何东西中使用的ComponentCallbacks(没有2)。该回调只有onLowMemory()方法和当你进入后台时不会被调用,但你应该使用它来精简内存。

现在启动你的App,按home键。应该调用你的onTrimMemory(final int level)方法(提示:添加日志记录)。

最后一步是从回调取消注册。也许最好的地方是你的App的onTerminate()方法,,这个方法不会在真实的设备上被调用:

/**
* This method is for use in emulated process environments.  It will
* never be called on a production Android device, where processes are
* removed by simply killing them; no user code (including this callback)
* is executed when doing so.
*/

因此,除非你真的遇到了不想再注册的情况,否则你可以安全地忽略它,因为你的进程在操作系统级已经死亡了。

如果你决定在某些时候取消注册(例如,如果你为你的应用程序提供了一个关闭机制来清理和死亡),你可以这样做:

unregisterComponentCallbacks(mMemoryBoss);

就是这样。

我们用这个方法。它看起来太简单了,但它在我们的应用程序中经过了良好的测试,实际上在所有情况下都运行得非常好,包括通过“home”按钮进入主屏幕,通过“返回”按钮,或在屏幕锁定后返回主屏幕。试一试。

想法是,当在前景,Android总是开始新的活动之前停止前一个。这并不能保证,但这就是它的工作原理。顺便说一句,Flurry似乎使用了同样的逻辑(只是猜测,我没有检查,但它在相同的事件中挂钩)。

public abstract class BaseActivity extends Activity {


private static int sessionDepth = 0;


@Override
protected void onStart() {
super.onStart();
sessionDepth++;
if(sessionDepth == 1){
//app came to foreground;
}
}


@Override
protected void onStop() {
super.onStop();
if (sessionDepth > 0)
sessionDepth--;
if (sessionDepth == 0) {
// app went to background
}
}


}

编辑:根据注释,我们也移动到onStart()在以后的版本的代码。此外,我还添加了超级调用,这是我最初的文章中所没有的,因为这更像是一个概念,而不是一个工作代码。

这是我的解决方案https://github.com/doridori/AndroidUtils/blob/master/App/src/main/java/com/doridori/lib/app/ActivityCounter.java

基本上涉及到用计时器计算所有Activity的生命周期方法,以捕捉当前前台没有活动但应用程序(即在旋转)的情况

我在谷歌Analytics EasyTracker中使用了这个,它起作用了。可以将其扩展为使用简单整数来完成您想要的任务。

public class MainApplication extends Application {


int isAppBackgrounded = 0;


@Override
public void onCreate() {
super.onCreate();
appBackgroundedDetector();
}


private void appBackgroundedDetector() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {


}


@Override
public void onActivityStarted(Activity activity) {
EasyTracker.getInstance(MainApplication.this).activityStart(activity);
}


@Override
public void onActivityResumed(Activity activity) {
isAppBackgrounded++;
if (isAppBackgrounded > 0) {
// Do something here
}
}


@Override
public void onActivityPaused(Activity activity) {
isAppBackgrounded--;
}


@Override
public void onActivityStopped(Activity activity) {
EasyTracker.getInstance(MainApplication.this).activityStop(activity);
}


@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {


}


@Override
public void onActivityDestroyed(Activity activity) {


}
});
}
}

我的解决方案受到@d60402的答案的启发,也依赖于一个时间窗口,但不使用Timer:

public abstract class BaseActivity extends ActionBarActivity {


protected boolean wasInBackground = false;


@Override
protected void onStart() {
super.onStart();
wasInBackground = getApp().isInBackground;
getApp().isInBackground = false;
getApp().lastForegroundTransition = System.currentTimeMillis();
}


@Override
protected void onStop() {
super.onStop();
if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
getApp().isInBackground = true;
}


protected SingletonApplication getApp(){
return (SingletonApplication)getApplication();
}
}

其中SingletonApplicationApplication类的扩展:

public class SingletonApplication extends Application {
public boolean isInBackground = false;
public long lastForegroundTransition = 0;
}

基于Martín marconcini的回答(谢谢!)我终于找到了一个可靠(而且非常简单)的解决方案。

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {


private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
private static boolean isInBackground = false;


@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}


@Override
public void onActivityStarted(Activity activity) {
}


@Override
public void onActivityResumed(Activity activity) {


if(isInBackground){
Log.d(TAG, "app went to foreground");
isInBackground = false;
}
}


@Override
public void onActivityPaused(Activity activity) {
}


@Override
public void onActivityStopped(Activity activity) {
}


@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}


@Override
public void onActivityDestroyed(Activity activity) {
}


@Override
public void onConfigurationChanged(Configuration configuration) {
}


@Override
public void onLowMemory() {
}


@Override
public void onTrimMemory(int i) {
if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
Log.d(TAG, "app went to background");
isInBackground = true;
}
}
}

然后将其添加到Application类的onCreate()中

public class MyApp extends android.app.Application {


@Override
public void onCreate() {
super.onCreate();


ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
registerActivityLifecycleCallbacks(handler);
registerComponentCallbacks(handler);


}


}

创建扩展Application。然后在它中我们可以使用它的覆盖方法onTrimMemory()

为了检测应用程序是否进入了后台,我们将使用:

 @Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
// Get called every-time when application went to background.
}
else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
}
}

我在Github app-foreground-background-listen上创建了一个项目

为应用程序中的所有Activity创建一个BaseActivity。

public class BaseActivity extends Activity {


public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}


public static boolean isAppInFg = false;
public static boolean isScrInFg = false;
public static boolean isChangeScrFg = false;


@Override
protected void onStart() {
if (!isAppInFg) {
isAppInFg = true;
isChangeScrFg = false;
onAppStart();
}
else {
isChangeScrFg = true;
}
isScrInFg = true;


super.onStart();
}


@Override
protected void onStop() {
super.onStop();


if (!isScrInFg || !isChangeScrFg) {
isAppInFg = false;
onAppPause();
}
isScrInFg = false;
}


public void onAppStart() {


// Remove this toast
Toast.makeText(getApplicationContext(), "App in foreground",    Toast.LENGTH_LONG).show();


// Your code
}


public void onAppPause() {


// Remove this toast
Toast.makeText(getApplicationContext(), "App in background",  Toast.LENGTH_LONG).show();


// Your code
}
}

现在使用这个BaseActivity作为一个超类的所有你的活动像MainActivity扩展BaseActivity和onAppStart将被调用时,你启动你的应用程序和onappause()将被调用时,应用程序从后台从任何屏幕。

我发现了一个很好的方法来检测应用程序是否进入前台或后台。 这是我的代码

.

.
/**
* Custom Application which can detect application state of whether it enter
* background or enter foreground.
*
* @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/android-solution-to-detect-when-android.html
*/
public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {


public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;


private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;


private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;


@Override
public void onCreate() {
super.onCreate();
mCurrentState = STATE_UNKNOWN;
registerActivityLifecycleCallbacks(this);
}


@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
// mCurrentState = STATE_CREATED;
}


@Override
public void onActivityStarted(Activity activity) {
if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
if (mStateFlag == FLAG_STATE_BACKGROUND) {
applicationWillEnterForeground();
mStateFlag = FLAG_STATE_FOREGROUND;
}
}
mCurrentState = STATE_STARTED;


}


@Override
public void onActivityResumed(Activity activity) {
mCurrentState = STATE_RESUMED;


}


@Override
public void onActivityPaused(Activity activity) {
mCurrentState = STATE_PAUSED;


}


@Override
public void onActivityStopped(Activity activity) {
mCurrentState = STATE_STOPPED;


}


@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {


}


@Override
public void onActivityDestroyed(Activity activity) {
mCurrentState = STATE_DESTROYED;
}


@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
if (mStateFlag == FLAG_STATE_FOREGROUND) {
applicationDidEnterBackground();
mStateFlag = FLAG_STATE_BACKGROUND;
}
}else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
if (mStateFlag == FLAG_STATE_FOREGROUND) {
applicationDidDestroyed();
mStateFlag = FLAG_STATE_BACKGROUND;
}
}
}


/**
* The method be called when the application been destroyed. But when the
* device screen off,this method will not invoked.
*/
protected abstract void applicationDidDestroyed();


/**
* The method be called when the application enter background. But when the
* device screen off,this method will not invoked.
*/
protected abstract void applicationDidEnterBackground();


/**
* The method be called when the application enter foreground.
*/
protected abstract void applicationWillEnterForeground();

这是我的解决方案。只需在您的主Application类中注册这个ActivityLifecycleCallbacks。在评论中,我提到了一个用户配置文件活动边缘情况。该活动只是一个具有透明边缘的活动。

/**
* This class used Activity lifecycle callbacks to determine when the application goes to the
* background as well as when it is brought to the foreground.
*/
public class Foreground implements Application.ActivityLifecycleCallbacks
{
/**
* How long to wait before checking onStart()/onStop() count to determine if the app has been
* backgrounded.
*/
public static final long BACKGROUND_CHECK_DELAY_MS = 500;


private static Foreground sInstance;


private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private boolean mIsForeground = false;
private int mCount;


public static void init(final Application application)
{
if (sInstance == null)
{
sInstance = new Foreground();
application.registerActivityLifecycleCallbacks(sInstance);
}
}


public static Foreground getInstance()
{
return sInstance;
}


public boolean isForeground()
{
return mIsForeground;
}


public boolean isBackground()
{
return !mIsForeground;
}


@Override
public void onActivityStarted(final Activity activity)
{
mCount++;


// Remove posted Runnables so any Meteor disconnect is cancelled if the user comes back to
// the app before it runs.
mMainThreadHandler.removeCallbacksAndMessages(null);


if (!mIsForeground)
{
mIsForeground = true;
}
}


@Override
public void onActivityStopped(final Activity activity)
{
mCount--;


// A transparent Activity like community user profile won't stop the Activity that launched
// it. If you launch another Activity from the user profile or hit the Android home button,
// there are two onStops(). One for the user profile and one for its parent. Remove any
// posted Runnables so we don't get two session ended events.
mMainThreadHandler.removeCallbacksAndMessages(null);
mMainThreadHandler.postDelayed(new Runnable()
{
@Override
public void run()
{
if (mCount == 0)
{
mIsForeground = false;
}
}
}, BACKGROUND_CHECK_DELAY_MS);
}


@Override
public void onActivityCreated(final Activity activity, final Bundle savedInstanceState)
{


}


@Override
public void onActivityResumed(final Activity activity)
{


}


@Override
public void onActivityPaused(final Activity activity)
{


}


@Override
public void onActivitySaveInstanceState(final Activity activity, final Bundle outState)
{


}


@Override
public void onActivityDestroyed(final Activity activity)
{


}
}

你可以使用:

onRestart ()

重新开始和重新开始的区别。

enter image description here

根据客户请求,我的应用程序需要在从后台返回后“重启”-显示一系列活动。在深入研究了如何管理后台/前台转换(iOS和Android之间的处理非常不同)后,我跳过了这个问题。在这里找到了非常有用的帮助,特别是投票最多的答案和标记为正确的答案。然而,当你考虑用户体验时,每次应用进入前台时,简单地重新实例化根活动看起来太烦人了。对我有效的解决方案,也是我认为最合适的——基于Youtube和Twitter应用程序的功能——是结合@GirishNair和@d60402的答案:当应用程序精简内存时调用计时器,如下:

@Override
public void onTrimMemory(int level) {
if (stateOfLifeCycle.equals("Stop")) {
startActivityTransitionTimer();
}


super.onTrimMemory(level);
}

我的定时器限制设置为30秒-我正在考虑增加一点。

private final long MAX_ACTIVITY_TRANSITION_TIME = 30000;

当app进入前台,重新启动,或者app被销毁时,调用方法取消定时器。

在应用程序扩展:

@Override
public void onActivityCreated(Activity activity, Bundle arg1) {
stopActivityTransitionTimer();
stateOfLifeCycle = "Create";
}


@Override
public void onActivityDestroyed(Activity activity) {
stopActivityTransitionTimer();
stateOfLifeCycle = "Destroy";
}

在活动上(最好是在一个基础活动上,由其他活动继承):

@Override
protected void onStart() {
super.onStart();
if (App.wasInBackground) {
stopActivityTransitionTimer();
}
}
在我的情况下,当应用程序在最大时间后进入前台时,会创建一个新任务,因此在应用程序扩展类的onActivityCreated()或onActivityDestroyed()上调用stopActivityTransitionTimer() -在活动中调用该方法变得不必要。

编辑:新的体系结构组件带来了一些有前途的东西:ProcessLifecycleOwner,参见@vokilam的回答


根据谷歌I/O会话的实际解决方案:

class YourApplication : Application() {


override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(AppLifecycleTracker())
}


}




class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {


private var numStarted = 0


override fun onActivityStarted(activity: Activity?) {
if (numStarted == 0) {
// app went to foreground
}
numStarted++
}


override fun onActivityStopped(activity: Activity?) {
numStarted--
if (numStarted == 0) {
// app went to background
}
}


}

是的。我知道很难相信这个简单的解决方案有效,因为我们有这么多奇怪的解决方案。

但还是有希望的。

这个解决方案呢

public class BaseActivity extends Activity
{


static String currentAct = "";


@Override
protected void onStart()
{
super.onStart();


if (currentAct.equals(""))
Toast.makeText(this, "Start", Toast.LENGTH_LONG).show();


currentAct = getLocalClassName();
}


@Override
protected void onStop()
{
super.onStop();


if (currentAct.equals(getLocalClassName()))
{
currentAct = "";
Toast.makeText(this, "Stop", Toast.LENGTH_LONG).show();
}
}
}

所有的Activity都需要扩展BaseActivity。

当一个活动调用另一个(A->B)时,currentAct不等于getLocalClassName(),因为第二个活动(B)的onStart()在第一个活动(A) (https://developer.android.com/guide/components/activities.html#CoordinatingActivities)的onStop()之前被调用。

当用户按下home键或应用程序之间的变化将只调用onStop(),然后currentAct等于getLocalClassName()。

我知道有点晚了,但我认为所有这些答案都有一些问题,而我这样做了,这是完美的。

创建一个活动生命周期回调,如下所示:

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{


@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {


}


@Override
public void onActivityStarted(Activity activity) {


}


Activity lastActivity;
@Override
public void onActivityResumed(Activity activity) {
//if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when  app has been killed or started for the first time
if (activity != null && activity == lastActivity)
{
Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
}


lastActivity = activity;
}


@Override
public void onActivityPaused(Activity activity) {


}


@Override
public void onActivityStopped(Activity activity) {


}


@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {


}


@Override
public void onActivityDestroyed(Activity activity) {


}
}

然后像下面这样在你的应用类上注册它:

public class MyApp extends Application {


@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}

2021年11月更新

实际设置如下

class App : Application() {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())
}
}


class AppLifecycleListener : DefaultLifecycleObserver {


override fun onStart(owner: LifecycleOwner) { // app moved to foreground
}


override fun onStop(owner: LifecycleOwner) { // app moved to background
}
}

依赖关系

implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common:$lifecycle_version"

原来的答案

ProcessLifecycleOwner似乎也是一个有前途的解决方案。

ProcessLifecycleOwner将分派ON_STARTON_RESUME事件,当第一个活动通过这些事件时。ON_PAUSEON_STOP,事件将在最后一个活动通过它们后使用延迟分派。这个延迟足够长,可以保证如果由于配置更改而破坏并重新创建活动,ProcessLifecycleOwner不会发送任何事件。

实现可以简单到

class AppLifecycleListener : LifecycleObserver {


@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() { // app moved to foreground
}


@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() { // app moved to background
}
}


// register observer
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())

根据源代码,当前延迟值为700ms

使用此特性还需要dependencies:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"

这似乎是Android中最复杂的问题之一,因为(在撰写本文时)Android没有iOS中等价的applicationDidEnterBackground()applicationWillEnterForeground()回调。我使用了AppState图书馆,它是由@jenzz组合在一起的。

[AppState]是一个基于RxJava的简单的响应式Android库,用于监控应用程序状态的变化。每当应用程序进入后台并返回前台时,它都会通知订阅者。

事实证明,这正是我所需要的,特别是因为我的应用程序有多个活动,所以简单地检查活动上的onStart()onStop()并不会削减它。

首先,我将这些依赖项添加到gradle:

dependencies {
compile 'com.jenzz.appstate:appstate:3.0.1'
compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

然后,将这些行添加到代码中适当的位置就很简单了:

//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of android.app.Application
appState.subscribe(new Consumer<AppState>() {
@Override
public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
switch (appState) {
case FOREGROUND:
Log.i("info","App entered foreground");
break;
case BACKGROUND:
Log.i("info","App entered background");
break;
}
}
});

根据你订阅可观察对象的方式,你可能不得不取消订阅以避免内存泄漏。关于github页面的更多信息。

这是@d60402的答案的修改版本:https://stackoverflow.com/a/15573121/4747587

按照上面提到的去做。但是,与其拥有Base Activity并将其作为每个活动的父对象并重写onResume()onPause,不如执行以下操作:

在你的应用程序类中,添加这样一行:

registerActivityLifecycleCallbacks(应用程序。ActivityLifecycleCallbacks回调);

这个callback有所有的活动生命周期方法,你现在可以覆盖onActivityResumed()onActivityPaused()

看看这个Gist: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b

使用ProcessLifecycleOwner,这非常简单

添加这些依赖关系

implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"

芬兰湾的科特林:

class ForegroundBackgroundListener : LifecycleObserver {




@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startSomething() {
Log.v("ProcessLog", "APP IS ON FOREGROUND")
}


@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopSomething() {
Log.v("ProcessLog", "APP IS IN BACKGROUND")
}
}

然后在你的基本活动中:

override fun onCreate() {
super.onCreate()


ProcessLifecycleOwner.get()
.lifecycle
.addObserver(
ForegroundBackgroundListener()
.also { appObserver = it })
}

查看我关于这个主题的文章: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48 < / p >

通过使用下面的代码,我能够得到我的应用程序的前台或后台状态。

关于它的工作的更多细节,强文本点击这里

import android.content.ComponentCallbacks2;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {


private Context context;
private Toast toast;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
}


private void showToast(String message) {
//If toast is already showing cancel it
if (toast != null) {
toast.cancel();
}


toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
toast.show();
}


@Override
protected void onStart() {
super.onStart();
showToast("App In Foreground");
}


@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
showToast("App In Background");
}
}
}

正确答案

创建名为MyApp的类,如下所示:

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {


private Context context;
public void setContext(Context context)
{
this.context = context;
}


private boolean isInBackground = false;


@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {




isInBackground = true;
Log.d("status = ","we are out");
}
}




@Override
public void onActivityCreated(Activity activity, Bundle bundle) {


}


@Override
public void onActivityStarted(Activity activity) {


}


@Override
public void onActivityResumed(Activity activity) {


if(isInBackground){


isInBackground = false;
Log.d("status = ","we are in");
}


}


@Override
public void onActivityPaused(Activity activity) {


}


@Override
public void onActivityStopped(Activity activity) {


}


@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {


}


@Override
public void onActivityDestroyed(Activity activity) {


}


@Override
public void onConfigurationChanged(Configuration configuration) {


}


@Override
public void onLowMemory() {


}
}

然后,在你想要的任何地方(最好是在应用程序中启动的第一个活动),添加下面的代码:

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);
< p >完成了!现在当应用程序在后台时,我们得到log status : we are out 当我们进入app,我们得到log status : we are out

你可以在下面的ActivityLifecycleCallbacksComponentCallbacks2的帮助下轻松实现这一点。

创建一个实现上述接口的类AppLifeCycleHandler

package com.sample.app;


import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.os.Bundle;


/**
* Created by Naveen on 17/04/18
*/
public class AppLifeCycleHandler
implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {


AppLifeCycleCallback appLifeCycleCallback;


boolean appInForeground;


public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
this.appLifeCycleCallback = appLifeCycleCallback;
}


@Override
public void onActivityResumed(Activity activity) {
if (!appInForeground) {
appInForeground = true;
appLifeCycleCallback.onAppForeground();
}
}


@Override
public void onTrimMemory(int i) {
if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
appInForeground = false;
appLifeCycleCallback.onAppBackground();
}
}


@Override
public void onActivityCreated(Activity activity, Bundle bundle) {


}


@Override
public void onActivityStarted(Activity activity) {


}


@Override
public void onActivityPaused(Activity activity) {


}


@Override
public void onActivityStopped(Activity activity) {


}


@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {


}


@Override
public void onActivityDestroyed(Activity activity) {


}


@Override
public void onConfigurationChanged(Configuration configuration) {


}


@Override
public void onLowMemory() {


}


interface AppLifeCycleCallback {


void onAppBackground();


void onAppForeground();
}
}

在扩展Application的类中,实现AppLifeCycleCallback来获取应用程序在前台和后台之间切换时的回调。如下图所示。

public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{


@Override
public void onCreate() {
super.onCreate();
AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
registerActivityLifecycleCallbacks(appLifeCycleHandler);
registerComponentCallbacks(appLifeCycleHandler);
}


@Override
public void onAppBackground() {
Log.d("LifecycleEvent", "onAppBackground");
}


@Override
public void onAppForeground() {
Log.d("LifecycleEvent", "onAppForeground");
}
}

希望这能有所帮助。

<强>编辑 作为替代方案,您现在可以使用生命周期感知的体系结构组件。< / p >

因为我没有找到任何方法,它也处理旋转而不检查时间戳,我想我也分享我们现在如何在我们的应用程序中这样做。 对这个答案https://stackoverflow.com/a/42679191/5119746唯一的补充是,我们还考虑了方向
class MyApplication : Application(), Application.ActivityLifecycleCallbacks {


// Members


private var mAppIsInBackground = false
private var mCurrentOrientation: Int? = null
private var mOrientationWasChanged = false
private var mResumed = 0
private var mPaused = 0

然后,对于回调,我们首先有简历:

   // ActivityLifecycleCallbacks


override fun onActivityResumed(activity: Activity?) {


mResumed++


if (mAppIsInBackground) {


// !!! App came from background !!! Insert code


mAppIsInBackground = false
}
mOrientationWasChanged = false
}

和onActivityStopped:

   override fun onActivityStopped(activity: Activity?) {


if (mResumed == mPaused && !mOrientationWasChanged) {


// !!! App moved to background !!! Insert code


mAppIsInBackground = true
}

然后,这里是附加的:检查方向变化:

   override fun onConfigurationChanged(newConfig: Configuration) {


if (newConfig.orientation != mCurrentOrientation) {
mCurrentOrientation = newConfig.orientation
mOrientationWasChanged = true
}
super.onConfigurationChanged(newConfig)
}

就是这样。希望这能帮助到一些人:)

android.arch.lifecycle包提供了类和接口,允许您构建生命周期感知的组件

您的应用程序应该实现LifecycleObserver接口:

public class MyApplication extends Application implements LifecycleObserver {


@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}


@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private void onAppBackgrounded() {
Log.d("MyApp", "App in background");
}


@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onAppForegrounded() {
Log.d("MyApp", "App in foreground");
}
}

为此,您需要将此依赖项添加到构建中。gradle文件:

dependencies {
implementation "android.arch.lifecycle:extensions:1.1.1"
}

正如谷歌所建议的,你应该最小化在activity的生命周期方法中执行的代码:

一个常见的模式是实现依赖的动作 活动和片段的生命周期方法中的组件。 然而,这种模式会导致代码组织不佳,并且导致 错误的扩散。通过使用生命周期感知组件,您 是否可以将依赖组件的代码移出生命周期方法 进入组件本身

你可以在这里阅读更多: https://developer.android.com/topic/libraries/architecture/lifecycle < / p >

我设法监控应用导航到后台和回前台通过实现一个BaseActivity,利用onResume, onPause和onStop活动回调的使用。这是我的实现。

override fun onResume() {
super.onResume()
if (AppActivityState.state == AppState.ON_LAUNCHED) {
// We are in the first launch.
onLaunched()
} else {
if (AppActivityState.state == AppState.ON_BACKGROUND) {
// We came from background to foreground.
AppActivityState.state = AppState.ON_FOREGROUND
onForeground()
} else {
// We are just navigating through pages.
AppActivityState.state = AppState.RESUMED
}
}
}


override fun onPause() {
super.onPause()
// If state is followed by onStop then it means we will going to background.
AppActivityState.state = AppState.PAUSED
}


override fun onStop() {
super.onStop()


// App will go to background base on the 'pause' cue.
if (AppActivityState.state == AppState.PAUSED) {
AppActivityState.state = AppState.ON_BACKGROUND
onBackground()
}
}

在创建BaseActivity之后,你只需要将这个activity扩展到应用程序中的任何activity。

在这些类型的实现中,您可以准确地检测到以下内容: onBackground >应用程序将进入后台 onForeground >应用将返回前台 - onLaunch >应用程序刚刚打开

我希望这对你有帮助:)

没有直接的生命周期方法来告诉你整个应用程序何时进入后台或前台。

我用简单的方法做了这件事。按照下面的说明检测应用程序后台/前台阶段。

只要有一点变通办法,这是可能的。这里,ActivityLifecycleCallbacks开始救援。让我一步一步来。

  1. 首先,创建一个扩展android.app.Application并实现ActivityLifecycleCallbacks接口的类。在Application.onCreate()中,注册回调。

    public class App extends Application implements
    Application.ActivityLifecycleCallbacks {
    
    
    @Override
    public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(this);
    }
    }
    
  2. 在Manifest中注册“App”类,如下所示,<application android:name=".App"

  3. 当应用程序在前台时,至少有一个Activity处于启动状态,当应用程序在后台时,将没有Activity处于启动状态。

    在“App”类中声明如下2个变量。

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;
    

    activityReferences将保持started状态下的活动数的计数。isActivityChangingConfigurations是一个标志,指示当前Activity是否正在经历配置更改,如方向开关。

  4. 使用下面的代码,你可以检测App是否出现在前台。

    @Override
    public void onActivityStarted(Activity activity) {
    if (++activityReferences == 1 && !isActivityChangingConfigurations) {
    // App enters foreground
    }
    }
    
  5. 这是如何检测应用程序是否进入后台。

    @Override
    public void onActivityStopped(Activity activity) {
    isActivityChangingConfigurations = activity.isChangingConfigurations();
    if (--activityReferences == 0 && !isActivityChangingConfigurations) {
    // App enters background
    }
    }
    

工作原理:

这是按顺序调用Lifecycle方法的一个小技巧。让我来演练一个场景。

假设用户启动了应用程序,启动器活动A被启动。生命周期调用将是,

A.onCreate ()

A.onStart() (++activityReferences == 1)(应用程序进入前台)

A.onResume ()

现在活动A启动活动B。

A.onPause ()

B.onCreate ()

B.onStart() (++activityReferences == 2)

B.onResume ()

A.onStop()(——activityReferences == 1)

然后用户从活动B导航回来,

B.onPause ()

A.onStart() (++activityReferences == 2)

A.onResume ()

B.onStop()(——activityReferences == 1)

B.onDestroy ()

然后用户按Home键,

A.onPause ()

A.onStop()(——activityReferences == 0)(应用程序进入后台)

在这种情况下,如果用户从活动B按Home键而不是后退键,它仍然是一样的,activityReferences将是0。因此,我们可以检测作为应用程序进入后台。

那么,isActivityChangingConfigurations的作用是什么?在上面的场景中,假设活动B改变了方向。回调序列将是,

B.onPause ()

B.onStop()(——activityReferences == 0)(应用程序进入后台??)

B.onDestroy ()

B.onCreate ()

B.onStart() (++activityReferences == 1)(应用程序进入前台??)

B.onResume ()

这就是为什么我们有一个额外的isActivityChangingConfigurations检查,以避免活动正在经历配置更改时的场景。

我们可以使用LiveData展开这个解决方案:

class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {


private var lifecycleListener: LifecycleObserver? = null


override fun onActive() {
super.onActive()
lifecycleListener = AppLifecycleListener().also {
ProcessLifecycleOwner.get().lifecycle.addObserver(it)
}
}


override fun onInactive() {
super.onInactive()
lifecycleListener?.let {
this.lifecycleListener = null
ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
}
}


internal inner class AppLifecycleListener : LifecycleObserver {


@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
value = State.FOREGROUND
}


@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
value = State.BACKGROUND
}
}


enum class State {
FOREGROUND, BACKGROUND
}
}


现在我们可以订阅这个LiveData并捕获所需的事件。例如:

appForegroundStateLiveData.observeForever { state ->
when(state) {
AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
}
}

你可以使用ProcessLifecycleOwner将一个生命周期观察者附加到它。

  public class ForegroundLifecycleObserver implements LifecycleObserver {


@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void onAppCreated() {
Timber.d("onAppCreated() called");
}


@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppStarted() {
Timber.d("onAppStarted() called");
}


@OnLifecycleEvent(Event.ON_RESUME)
public void onAppResumed() {
Timber.d("onAppResumed() called");
}


@OnLifecycleEvent(Event.ON_PAUSE)
public void onAppPaused() {
Timber.d("onAppPaused() called");
}


@OnLifecycleEvent(Event.ON_STOP)
public void onAppStopped() {
Timber.d("onAppStopped() called");
}
}

然后在你的Application类的onCreate()中调用这个:

ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());

有了它,你将能够捕获应用程序在后台运行时发生的ON_PAUSEON_STOP事件。

您可以在应用程序类中简单地调用此方法

ProcessLifecycleOwner.get().getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
Log.e(TAG, "onStateChanged: " + event.toString());
}
});

Lifecycle.Event将返回应用程序的状态

ON_CREATE
ON_START
ON_RESUME
ON_PAUSE
ON_STOP
ON_DESTROY
ON_ANY

它将返回ON_PAUSE &当应用程序进入后台时ON_STOP 并且返回ON_START &ON_RESUME当应用程序来到前台

有三种方法可以实现这一点:

  • 单一活动架构
  • ActivityLifecycleCallback
  • 生命周期观察者和进程生命周期所有者

已经写了一篇关于在这里的详细文章。希望能有所帮助。

Activity(或任何类)中使用ProcessLifecycleOwner从后台到前台检测应用程序的例子。
当应用程序启动时,我缓存开始时间,然后在每个活动中,我将检查应用程序时间,以知道活动是否在第一时间启动或从后台

class MyApplication : Application(), LifecycleObserver {


var appStartBeginTime: Long? = null


override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this);
}


@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
Log.i("TAG", "onMoveToForeground")
appStartBeginTime = System.currentTimeMillis()
}
}

LoginActivity

class LoginActivity : AppCompatActivity() {
var localAppStartBeginTime: Long? = null


...
    

// Detect in onResume() instead of onStart because
// onMoveToForeground() in MyApplication will fired before onStart
override fun onResume() {
super.onResume()
if (isOpenedFirstTimeOrFromBackground()) {
Log.i("TAG", "open first time or from background")


// do something: eg, call API
} else {
Log.i("TAG", "on in another time")
}
}


private fun isOpenedFirstTimeOrFromBackground(): Boolean {
val globalStartBeginTime = (application as MyApplication).appStartBeginTime
if (localAppStartBeginTime != globalStartBeginTime) {
localAppStartBeginTime = globalStartBeginTime
return true
}
return false
}
}

AndroidManifest

<manifest ...>


<application
android:name=".MyApplication"
...>
            

</application>


</manifest>
< p >演示 https://github.com/PhanVanLinh/AndroidDetectAppFromBackgroundToForeground < / p >

我喜欢ProcessLifecycleOwner方法,但实际上可以跳过所有这些,因为在ActivityonCreate()方法中,可以很容易地确定它是第一次运行还是后续运行:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
/* savedInstanceState is always null on first run */
} else {
/* it's a subsequent run */
}
}

这里有一个解决方案,通过使用deboning逻辑,确保我们不会得到连续的背景/前景事件。所以,它总是反映一种稳定的背景/前景状态。

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import java.util.Timer
import java.util.TimerTask


/**
* An observer class to listen on the app's lifecycle.
*/
class AppLifecycleObserver(
private val onAppGoesToBackground: () -> Unit = {},
private val onAppEntersForeground: () -> Unit = {}
) : LifecycleEventObserver {


private val debounce = DebouncingTimer(timeout = 10)


override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
debounce.refresh {
when (event.targetState) {
Lifecycle.State.CREATED -> onAppGoesToBackground()
Lifecycle.State.RESUMED -> onAppEntersForeground()
else -> Unit
}
}
}


fun attach() {
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}


fun detach() {
ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
}


private class DebouncingTimer(private val timeout: Long) {


private var timer: Timer? = null


fun refresh(job: () -> Unit) {
timer?.cancel()
timer = Timer()
timer?.schedule(object : TimerTask() {
override fun run() = job.invoke()
}, timeout)
}
}
}

只需要创建一个AppLifecycleObserver的实例:

private val appLifecycleObserver = AppLifecycleObserver(
onAppGoesToBackground = { // do whatever... },
onAppEntersForeground = { // do whatever... }
)
// Attach the observer when it is needed:
appLifecycleObserver.attach()


// Remove when there is no need to it:
appLifecycleObserver.detach()

不要忘记添加一个适当的依赖项版本:

implementation("androidx.lifecycle:lifecycle-process:$lifecycle_version")

LifecycleObserver已弃用。使用DefaultLifecycleObserver代替:

public class YourApplication extends Application implements DefaultLifecycleObserver {


@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}


@Override
public void onStart(@NonNull LifecycleOwner owner) {
DefaultLifecycleObserver.super.onStart(owner);
}


@Override
public void onResume(@NonNull LifecycleOwner owner) {
DefaultLifecycleObserver.super.onResume(owner);
}


@Override
public void onPause(@NonNull LifecycleOwner owner) {
DefaultLifecycleObserver.super.onPause(owner);
}


@Override
public void onStop(@NonNull LifecycleOwner owner) {
DefaultLifecycleObserver.super.onStop(owner);
}


@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
DefaultLifecycleObserver.super.onDestroy(owner);
}
}

依赖关系:

  implementation 'androidx.lifecycle:lifecycle-common:2.5.1'
implementation 'androidx.lifecycle:lifecycle-process:2.5.1'