我正在尝试编写一个应用程序,该应用程序在经过一段时间后又回到前台时会执行特定的操作。有没有一种方法可以检测何时将应用程序发送到后台或带到前台?
我正在尝试编写一个应用程序,该应用程序在经过一段时间后又回到前台时会执行特定的操作。有没有一种方法可以检测何时将应用程序发送到后台或带到前台?
Answers:
在onPause()
和onResume()
当应用程序被带到后台,并到前台再次方法被调用。但是,在首次启动应用程序时以及在终止应用程序之前,它们也会被调用。您可以在Activity中阅读更多内容。
没有任何直接的方式获取应用程序的状态,而在背景或前景,但即使我面临这个问题,并找到了解决方案onWindowFocusChanged
和onStop
。
有关更多详细信息,请在此处查看Android:一种解决方案,用于检测Android应用何时进入后台并返回到前台,而无需getRunningTasks或getRunningAppProcesses。
2018年3月更新:现在有一个更好的解决方案。请参阅ProcessLifecycleOwner。您将需要使用新的体系结构组件1.1.0(目前是最新的),但是它专门用于为此目的而设计的。
这个答案提供了一个简单的示例,但是我写了一个示例应用程序和一篇博客文章有关它。
自从我2014年写这篇文章以来,出现了各种解决方案。一些有效,一些被认为有效,但存在缺陷(包括我的!),我们作为一个社区(Android)学会后果,并针对特殊情况编写了解决方法。
永远不要以为您正在寻找的解决方案就是一小段代码,这种情况不太可能发生;更好的是,尝试了解它的作用及其原因。
该MemoryBoss
班从未实际使用我这里写的,它只是一块正巧工作的伪代码。
除非有正当的理由不使用新的体系结构组件(并且有一些,尤其是针对超级旧api的组件),否则请继续使用它们。它们远非完美,但都不完美ComponentCallbacks2
。
UPDATE / NOTES(2015年11月):人们一直在发表两条评论,第一条>=
应该使用它,而不是==
因为文档指出不应检查确切值。这是适用于大多数情况,但请记住,如果你只关心做事情时,应用程序去的背景下,你将不得不使用== 并且还与另一种解决方案(如活动生命周期回调)结合起来,或者你可能无法达到您想要的效果。这个例子(这发生在我身上)是,如果你想锁定您的应用在进入后台时会带有密码屏幕(例如1Password,如果您熟悉的话),如果内存不足并突然测试>= TRIM_MEMORY
,可能会意外锁定应用,因为Android会触发LOW MEMORY
通话,比你的高。因此,请谨慎进行测试。
此外,有些人询问如何在您回来时进行检测。
我可以想到的最简单的方法将在下面说明,但是由于有些人对此不熟悉,因此我在此处添加了一些伪代码。假设您有YourApplication
和中的MemoryBoss
班级class BaseActivity extends Activity
(如果您没有班级,则需要创建一个)。
@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
)将报告wasInBackground
,false
因此它将不会执行该代码,直到onMemoryTrimmed
被调用并将标志再次设置为true。
希望能有所帮助。
UPDATE / NOTES(2015年4月):在对该代码进行所有复制和粘贴之前,请注意,我发现了一些实例,其中它可能不是100%可靠的,必须将其与其他方法结合使用才能达到最佳效果。值得注意的是,在两个已知的实例中,onTrimMemory
不能保证执行回叫:
如果您的手机在您的应用程序可见时锁定了屏幕(例如,您的设备在nn分钟后锁定),则不会调用(或并非始终如此)此回调,因为锁定屏幕仅位于顶部,但您的应用程序仍在“运行”,尽管已被覆盖。
如果您的设备内存相对较低(并且处于内存压力下),则操作系统似乎会忽略此调用,而直接进入更关键的级别。
现在,根据了解应用何时进入后台的重要性,您可能需要也可能不需要扩展此解决方案,同时跟踪活动生命周期和其他活动。
只要记住以上几点,并拥有一支好的QA团队;)
更新结束
可能已经晚了,但是Ice Cream Sandwich(API 14)及更高版本中有一种可靠的方法。
事实证明,当您的应用程序不再具有可见的UI时,将触发回调。您可以在自定义类中实现的回调称为ComponentCallbacks2(是,有两个)。此回调仅在API级别14(冰淇淋三明治)及更高版本中可用。
您基本上可以调用该方法:
public abstract void onTrimMemory (int level)
等级为20或更高
public static final int TRIM_MEMORY_UI_HIDDEN
我一直在测试它,它始终有效,因为20级只是一个“建议”,由于您的应用程序不再可见,您可能希望释放一些资源。
引用官方文档:
onTrimMemory(int)的级别:该进程一直在显示用户界面,现在不再这样做。此时应释放与UI的大量分配,以更好地管理内存。
当然,您应该实现此功能以实际执行它所说的内容(清除一段时间内未使用的内存,清除一些未使用的集合,等等。可能性无穷无尽(有关更多其他信息,请参阅官方文档)临界水平)。
但是,有趣的是,操作系统正在告诉您:嘿,您的应用程序进入了后台!
首先,这正是您想知道的。
您如何确定何时回来?
好吧,这很容易,我确定您有一个“ BaseActivity”,因此您可以使用onResume()来标记您回来了。因为您唯一会说您不回来的时间是您实际上收到上述onTrimMemory
方法的调用时。
有用。您不会得到误报。如果某个活动正在恢复,那么您有100%的机会回来了。如果用户再次走到后面,您会onTrimMemory()
接到另一个电话。
您需要预订“活动”(或者更好的是,自定义类)。
确保始终收到此消息的最简单方法是创建一个像这样的简单类:
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
并实施ComponentCallbacks
(不含2)以下API 14.任何使用的回调只是有onLowMemory()
方法和当你去到后台不会被调用,但你应该用它来装饰记忆。
现在启动您的应用程序,然后按主页。您的onTrimMemory(final int level)
方法应被调用(提示:添加日志记录)。
最后一步是从回调中注销。最好的地方可能是onTerminate()
您的App 的方法,但是,该方法不会在实际设备上被调用:
/** * 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. */
因此,除非您确实有不想再注册的情况,否则您可以放心地忽略它,因为您的进程无论如何都将在OS级别上死掉。
如果您决定在某个时候取消注册(例如,如果您为应用程序提供了关闭机制以使其清理并死亡),则可以执行以下操作:
unregisterComponentCallbacks(mMemoryBoss);
就是这样。
level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
它来避免更新中的问题,第2点。关于第1点,我并不担心,因为该应用程序并没有真正进入后台,因此这就是它应该起作用的方式。
这是我设法解决此问题的方法。它的前提是,在活动转换之间使用时间参考将最有可能提供充分的证据来证明应用程序是否已“后台”。
首先,我使用了一个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()事件,或者最好在一个基本Activity中添加对每个方法的调用,您的所有具体Activity都将继承自该基本Activity:
@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将是错误的。
另一方面,当活动从启动器进入前台,设备唤醒,结束电话等时,很有可能在此事件之前执行了计时器任务,因此wasInBackground设置为true。
编辑:新的体系结构组件带来了一些有希望的东西:ProcessLifecycleOwner,请参见@vokilam的答案
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
}
}
}
是。我知道很难相信这个简单的解决方案会起作用,因为我们这里有很多奇怪的解决方案。
但是有希望。
ProcessLifecycleOwner
似乎也是一个有前途的解决方案。
ProcessLifecycleOwner将出动
ON_START
,ON_RESUME
活动,通过这些活动第一活性移动。ON_PAUSE
,,ON_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"
implementation "android.arch.lifecycle:extensions:1.0.0"
并annotationProcessor "android.arch.lifecycle:compiler:1.0.0"
从Google的存储库(即google()
)中添加
基于MartínMarconcinis的回答(谢谢!),我终于找到了可靠(非常简单)的解决方案。
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);
}
}
我们使用这种方法。它看起来工作起来太简单了,但是在我们的应用程序中经过了良好的测试,实际上在所有情况下都表现出色,包括通过“主页”按钮,“返回”按钮或屏幕锁定后进入主屏幕。试试看。
想法是,当处于前台时,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()。另外,我还要添加超级调用,这是我最初的帖子中所没有的,因为这更多的是概念而非有效的代码。
onStop is called when the activity is no longer visible to the user
。
如果您的应用程序包含多个活动和/或堆叠的活动(例如标签栏小部件),则覆盖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(现在称为点火)框架中找到了此方法。
我自己实现的第二种方法不需要GET_TASKS权限,这很好。相反,它的实现有点复杂。
在MainApplication类中,您有一个变量,用于跟踪应用程序中正在运行的活动的数量。在onResume()中,为每个活动增加变量,在onPause()中,减少变量。
当运行的活动数达到0时,如果满足以下条件,则将应用程序置于后台:
当您可以检测到应用程序已退出后台时,很容易检测到何时又将其带回前台。
创建一个扩展类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
}
}
FragmentActivity
您也可能想要添加level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE
。
考虑使用onUserLeaveHint。仅当您的应用进入后台时才会调用此方法。onPause将处理一些特殊情况,因为可以出于其他原因调用它;例如,如果用户在您的应用程序中打开了另一个活动(例如您的“设置”页面),则即使主活动的onPause方法仍在您的应用程序中,也会被调用;当您可以简单地使用onUserLeaveHint回调来执行您要查询的操作时,跟踪发生的情况将导致错误。
调用UserLeaveHint时,可以将boolean inBackground标志设置为true。调用onResume时,仅当设置了inBackground标志时才假定您回到了前台。这是因为,如果用户只是在您的设置菜单中并且从未离开过该应用程序,则onResume也会在您的主要活动中被调用。
请记住,如果用户在您的设置屏幕中按下主页按钮,则将在您的设置活动中调用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 {
...
}
ActivityLifecycleCallbacks可能很有趣,但是没有充分的文档记录。
但是,如果调用registerActivityLifecycleCallbacks(),则应该能够获取创建,销毁Activity 时的回调。您可以为Activity 调用getComponentName()。
该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");
}
}
为此,您需要将此依赖项添加到build.gradle文件中:
dependencies {
implementation "android.arch.lifecycle:extensions:1.1.1"
}
按照Google的建议,您应尽量减少在活动的生命周期方法中执行的代码:
一种常见的模式是在活动和片段的生命周期方法中实现依赖组件的动作。但是,这种模式导致代码的组织不善,并导致错误扩散。通过使用支持生命周期的组件,您可以将依赖组件的代码移出生命周期方法,然后移入组件本身。
您可以在这里阅读更多信息:https : //developer.android.com/topic/libraries/architecture/lifecycle
在您的应用程序中,添加回调并以如下方式检查root活动:
@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();
}
}
});
}
我在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用作您所有Activity的超类,例如MainActivity扩展BaseActivity,当您启动应用程序时将调用onAppStart,而当应用程序从任何屏幕转到背景时将调用onAppPause()。
使用ProcessLifecycleOwner非常简单
添加这些依赖
implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"
在Kotlin:
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 })
}
您可以使用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");
}
}
然后在onCreate()
您的Application类的上调用此代码:
ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());
这样,你就能捕捉到的事件ON_PAUSE
和ON_STOP
你的应用程序时,它会在后台这种情况发生。
没有简单的生命周期方法可以告诉您整个应用程序何时变为后台/前景。
我已经用简单的方法做到了。请按照以下说明检测应用程序的背景/前景阶段。
只需稍作解决,便可以实现。在这里,ActivityLifecycleCallbacks可以解救。让我逐步讲解。
首先,创建一个扩展android.app.Application并实现ActivityLifecycleCallbacks接口的类。在Application.onCreate()中,注册回调。
public class App extends Application implements
Application.ActivityLifecycleCallbacks {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
}
如下所示,在清单中注册“ App”类<application android:name=".App"
。
当应用程序处于前台状态时,至少有一个处于启动状态的活动,而当应用程序处于后台时,则不会处于启动状态。
在“ App”类中声明两个变量,如下所示。
private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;
activityReferences
将使活动数量保持在开始状态。isActivityChangingConfigurations
是一个标志,用于指示当前“活动”是否正在经历配置更改(例如方向开关)。
使用以下代码,您可以检测该应用程序是否成为前台。
@Override
public void onActivityStarted(Activity activity) {
if (++activityReferences == 1 && !isActivityChangingConfigurations) {
// App enters foreground
}
}
这是检测应用程序是否进入后台的方法。
@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()
然后用户按下“主页”按钮,
A.onPause()
A.onStop()(--activityReferences == 0)(应用进入后台)
如果用户按下活动B中的“主页”按钮而不是“返回”按钮,则仍然是相同的,activityReferences将是 0
。因此,我们可以检测到App进入后台。
那么,它的作用是isActivityChangingConfigurations
什么?在上述情况下,假设活动B改变了方向。回调顺序为
B.onPause()
B.onStop()(--activityReferences == 0)(应用进入后台?)
B.onDestroy()
B.onCreate()
B.onStart()(++ activityReferences == 1)(应用进入前台?)
B.onResume()
这就是为什么我们要进行额外检查isActivityChangingConfigurations
以避免活动在进行配置更改时发生的情况。
我找到了一种检测应用程序是否进入前台还是后台的好方法。这是我的代码。希望这对您有所帮助。
/**
* 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();
}
编辑2:我在下面编写的内容实际上无法正常工作。Google拒绝了一个包含对ActivityManager.getRunningTasks()的调用的应用程序。从文档中可以明显看出,此API仅用于调试和开发目的。我将有时间使用以下使用计时器的新方案来更新下面的GitHub项目时,立即对本帖子进行更新。
编辑1:我写了一篇博客文章,并创建了一个简单的GitHub存储库以使其变得非常容易。
公认的和评分最高的答案并不是真正的最佳方法。评分最高的答案是isApplicationBroughtToBackground()的实现,无法处理应用程序的主Activity屈服于在同一Application中定义的Activity的情况,但是它具有不同的Java包。我想出了一种在这种情况下可行的方法。
在onPause()中调用此函数,它将告诉您您的应用程序是否由于另一个应用程序启动而进入后台,或者用户按下了主页按钮。
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;
}
正确答案在这里
创建名称为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);
做完了!现在,当应用程序在后台运行时,我们将获得日志, status : we are out
而当我们进入应用程序时,我们将获得日志status : we are out
我的解决方案受到@ 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();
}
}
其中SingletonApplication
是Application
类的扩展:
public class SingletonApplication extends Application {
public boolean isInBackground = false;
public long lastForegroundTransition = 0;
}
我将它与Google 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) {
}
});
}
}
我知道有点晚了,但是我认为所有这些答案的确在遇到问题时遇到了一些问题,如下所示,并且效果很好。
创建一个活动生命周期回调,如下所示:
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());
}
这似乎是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
为每个活动以及将and覆盖为主要onResume()
,而不是使用a 作为父项onPause
,执行以下操作:
在您的应用程序类中,添加以下行:
registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks回调);
它callback
具有所有活动生命周期方法,您现在可以覆盖onActivityResumed()
和onActivityPaused()
。
看看这个要点:https : //gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b
您可以借助ActivityLifecycleCallbacks
和ComponentCallbacks2
类似下面的内容轻松实现此目的。
创建一个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");
}
}
希望这可以帮助。
编辑 作为替代方案,您现在可以使用生命周期感知架构组件。
由于我没有找到任何方法,该方法也可以在不检查时间戳的情况下处理旋转,因此我想我也分享了我们现在如何在应用程序中进行操作。这个答案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)
}
而已。希望这可以帮助某人:)
我们可以使用以下方法扩展此解决方案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 */ }
}
}
这些答案似乎不正确。当另一个活动开始和结束时,也会调用这些方法。您可以做的是保留一个全局标志(是的,全局变量不好:),并在每次启动新活动时将其设置为true。在每个活动的onCreate中将其设置为false。然后,在onPause中检查此标志。如果为假,则说明您的应用已进入后台,或者已被杀死。