如何检测Android应用程序何时进入后台并回到前台
我正在尝试编写一个应用程序,在经过一段时间之后将它带回到前台时执行某些特定的操作。 有没有办法来检测应用程序何时发送到后台或带到前台?
onPause()
和onResume()
方法在应用程序被带到后台并再次进入前台时被调用。 但是,当应用程序第一次启动时,它们也会被调用。 你可以阅读更多的活动 。
在后台或前台没有任何直接获取应用程序状态的方法,但是即使我遇到了这个问题,也find了使用onWindowFocusChanged
和onStop
的解决scheme。
有关更多详细信息,请点击此处Android:解决scheme来检测Android应用程序何时进入后台,并返回到前台没有getRunningTasks或getRunningAppProcesses 。
以下是我已经设法解决这个问题。 它的工作原理是,在活动转换之间使用时间参考很可能会提供足够的证据,表明应用程序已经“后台”。
首先,我使用了一个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; }
这个解决scheme的最后一部分是从所有活动的onResume()和onPause()事件向这些方法中的每一个添加调用,或者最好在所有具体活动inheritance的基本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()将启动计时器,但几乎立即进入的新活动会在达到最大转换时间之前取消计时器。 所以背景是错的 。
另一方面,当一个活动来自Launcher的前台,设备唤醒,结束电话等等,更可能是在这个事件之前执行的定时器任务,因此wasInBackground被设置为true 。
警告:如果您正在开发ICE CREAM SANDWICH(API LEVEL 14)或更高版本,那么这样做是非常好的。
更新/说明(2015年11月) :人们一直在做两个评论,首先是>=
应该被用来代替==
因为文档指出你不应该检查确切的值 。 这在大多数情况下都可以,但请记住,如果您只是在应用程序转到后台时执行某些操作 ,则必须使用== ,并将其与另一个解决scheme(如Activity Lifecyclecallback)结合使用,否则可能无法达到您想要的效果。 这个例子(这发生在我身上)是,如果你想用密码屏幕locking你的应用程序,当它走到后台(如1Password,如果你熟悉它),你可能会意外地locking你的应用程序,如果你跑低在内存中,并突然testing>= TRIM_MEMORY
,因为Android会触发一个LOW MEMORY
调用,这比你的更高。 所以要小心如何/你testing什么。
另外,有些人在回来的时候询问如何检测。
我能想到的最简单的方法解释如下,但由于有些人不熟悉它,我在这里添加一些伪代码。 假设你有YourApplication
和MemoryBoss
类,在你的class BaseActivity extends Activity
(如果你没有的话,你需要创build一个)。
@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); } }
我build议onStart,因为对话框可以暂停一个活动,所以我敢打赌你不希望你的应用程序认为“它走到了背景”,如果你所做的只是显示一个全屏对话框,但你的里程可能会有所不同。
就这样。 if块中的代码只会执行一次 ,即使你去了另一个活动,新的(也extends BaseActivity
)会报告wasInBackground
是false
所以它不会执行代码, 直到onMemoryTrimmed
被调用,标志再次设置为真 。
希望有所帮助。
更新/注解(2015年4月) :在你去所有复制和粘贴这个代码之前,请注意,我发现了一些可能不是100%可靠的实例, 必须与其他方法结合才能达到最佳效果。 值得注意的是,有两个已知的实例不能保证onTrimMemory
callback被执行:
-
如果您的手机在应用程序可见的情况下locking了屏幕(例如在nn分钟后您的设备locking),则不会调用此callback(或不总是),因为locking屏幕位于最上方,但您的应用程序仍处于“正在运行”状态。
-
如果您的设备内存相对较低(并且内存不足),操作系统似乎会忽略此调用并直接进入更关键的级别。
现在,取决于您知道应用程序何时转到后台有多重要,您可能需要也可能不需要将此解决scheme一起扩展,并跟踪活动的生命周期等等。
只要牢记上述内容,并有一个良好的QA团队;)
更新结束
这可能是晚了,但冰淇淋三明治(API 14)及以上有一个可靠的方法。
原来,当你的应用程序没有更多的可见的用户界面,一个callback被触发。 可以在自定义类中实现的callback称为ComponentCallbacks2 (是的,有两个)。 此callback仅适用于API Level 14(冰淇淋三明治)及以上版本。
你基本上可以调用这个方法:
public abstract void onTrimMemory (int level)
等级是20或更具体的
public static final int TRIM_MEMORY_UI_HIDDEN
我一直在testing这个,它总是有效的,因为20级只是一个“build议”,你可能想释放一些资源,因为你的应用程序不再可见。
引用官方文档:
onTrimMemory(int)的级别:进程已经显示一个用户界面,并且不再这样做。 此时应该释放大量的用户界面分配,以便更好地pipe理内存。
当然,你应该实现这一点,以实际上做它所说的(清除在一定时间内没有使用的内存,清除一些未使用的集合等等。可能性是无止境的(请参阅其他可能的更多的官方文档) 临界水平)。
但是,有趣的是,操作系统告诉你:嘿,你的应用程序去背景!
这正是你想要知道的第一位。
你如何确定你什么时候回来?
这很简单,我相信你有一个“BaseActivity”,所以你可以使用你的onResume()来标记你回来的事实。 因为唯一的一次你会说你不回来的时候,你实际上接到了上面的onTrimMemory
方法的调用。
有用。 你不会得到误报。 如果一个活动正在恢复,你就回来了,100%的时间。 如果用户再次返回,则会得到另一个onTrimMemory()
调用。
你需要订阅你的活动(或更好的,一个自定义的类)。
保证你总是收到这个最简单的方法是创build一个简单的类,如下所示:
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); } }
如果你创build一个Interface
你可以添加一个else
来实现ComponentCallbacks
(不包含2),它用在API 14以下的任何东西中。这个callback只有onLowMemory()
方法, 当你到达后台时不会被调用 ,你应该用它来修剪内存。
现在启动你的应用程序并按回家。 你的onTrimMemory(final int level)
方法应该被调用(提示:添加日志logging)。
最后一步是从callback中取消注册。 可能最好的地方是你的应用程序的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. */
所以除非你真的有不想再被注册的情况,否则你可以放心地忽略它,因为无论如何,你的进程在操作系统级别都会死亡。
如果您决定取消某个注册点(例如,如果您为应用程序提供closures机制来清理并且死掉),则可以执行以下操作:
unregisterComponentCallbacks(mMemoryBoss);
就是这样。
如果你的应用程序由多个活动和/或堆叠活动(如标签栏小部件)组成,则重写onPause()和onResume()将不起作用。 也就是说,当开始一个新的活动时,当前的活动将在创build新活动之前暂停。 完成(使用“后退”button)活动时也是如此。
我发现两种方法似乎按照要求工作。
第一个需要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)框架中find。
我已经实现了自我的第二个方法不需要GET_TASKS权限,这是很好的。 相反,实施起来要复杂一点。
在你的MainApplication类中,你有一个跟踪应用程序中运行活动的数量的variables。 在onResume()为每个活动你增加variables,并在onPause()你减less它。
当正在运行的活动数量达到0时,如果满足以下条件,应用程序将被置于后台:
- 正在暂停的活动未完成(使用“后退”button)。 这可以通过使用方法activity.isFinishing()
- 一个新的活动(相同的包名称)没有启动。 你可以重载startActivity()方法来设置一个variables来表示这个variables,然后在onPostResume()中重置它,这是创build/恢复活动的最后一个方法。
当您可以检测到应用程序已经退出后台时,很容易检测到该应用程序何时回到前台。
我们使用这种方法。 它看起来太简单了,但是它在我们的应用程序中经过了充分的testing,实际上在所有情况下的工作都非常出色,包括通过“home”button,“返回”button或屏幕locking后进入主屏幕。 试一试。
想法是,在前台,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()在更高版本的代码。 此外,我添加了超级调用,这是从我的初始职位缺less,因为这是一个更多的概念,而不是一个工作的代码。
编辑:新的架构组件带来了一些有希望的: ProcessLifecycleOwner ,请参阅@ vokilam的答案
根据Google I / O谈话的实际解决scheme:
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 } } }
是。 我知道很难相信这个简单的解决scheme,因为我们有这么多奇怪的解决scheme在这里。
但是有希望。
基于马丁Marconcinis答案(谢谢!)我终于find了一个可靠的(和非常简单)的解决scheme。
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); } }
创build一个扩展Application
的类 。 然后在其中,我们可以使用它的覆盖方法onTrimMemory()
。
要检测应用程序是否进入后台,我们将使用:
@Override public void onTrimMemory(final int level) { if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Get called every-time when application went to background. } }
考虑使用onUserLeaveHint。 这只会在您的应用进入后台时调用。 onPause将有angular落案件处理,因为它可以被称为其他原因; 例如,如果用户在您的应用程序中打开了另一个活动,例如您的设置页面,则即使您的应用程序仍在您的应用程序中,您的主要活动的onPause方法也会被调用; 跟踪发生了什么会导致错误,而不是简单地使用onUserLeaveHintcallback,这是你所要求的。
当调用UserLeaveHint时,可以将boolean inBackground标志设置为true。 当onResume被调用时,如果设置了inBackground标志,只假设你回到了前台。 这是因为如果用户只是在您的设置菜单中,并且永远不会离开应用程序,onResume也会在您的主要活动中被调用。
请记住,如果用户在设置屏幕中点击主页button,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 (),您应该能够获得有关活动创build,销毁等情况的callback。您可以为该活动调用getComponentName ()。
ProcessLifecycleOwner
似乎也是一个有前途的解决scheme。
ProcessLifecycleOwner将调度
ON_START
,ON_RESUME
事件,作为第一个活动在这些事件中移动。ON_PAUSE
,ON_STOP
,在最后一个活动通过它们之后,事件将被延迟调度。 此延迟足够长,以确保在活动由于configuration更改而被销毁和重新创build时,ProcessLifecycleOwner
不会发送任何事件。
一个实现可以像
public class AppLifecycleListener implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) public void onMoveToForeground() { // app moved to foreground } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) public void onMoveToBackground() { // app moved to background } } // register observer ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycleListener());
根据源代码,当前延时值为700ms
。
在您的应用程序中添加callback,并以这样的方式检查根活动:
@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上创build了一个项目
为您的应用程序中的所有Activity创build一个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将在您启动应用程序时调用,onAppPause()将在应用程序从任何屏幕转到后台时调用。
编辑2:我下面写了什么不会实际工作。 Google拒绝了一个包含对ActivityManager.getRunningTasks()的调用的应用程序。 从文档中可以看出,该API仅用于debugging和开发目的。 一旦我有时间用一个新的计时器来更新下面的GitHub项目,我将会更新这篇文章。
编辑1:我写了一篇博客文章,并创build了一个简单的GitHub存储库 ,使这真的很容易。
被接受和最高评价的答案都不是最好的方法。 The top rated answer's implementation of isApplicationBroughtToBackground() does not handle the situation where the Application's main Activity is yielding to an Activity that is defined in the same Application, but it has a different Java package. I came up with a way to do this that will work in that case.
Call this in onPause(), and it will tell you if your application is going into the background because another application has started, or the user has pressed the home button.
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; }
My solution was inspired by @d60402's answer and also relies on a time-window, but not using the 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(); } }
where the SingletonApplication
is an extension of Application
class:
public class SingletonApplication extends Application { public boolean isInBackground = false; public long lastForegroundTransition = 0; }
I found a good method to detect application whether enter foreground or background. Here is my code . 希望这对你有所帮助。
/** * 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();
}
您可以使用:
protected void onRestart ()
To differ between new starts and restarts.
i know its a little late but i think all these answers do have some problems while i did it like below and that works perfect.
create a activity life cycle callback like this:
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) { } }
and just register it on your application class like below:
public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(new ActivityLifeCycle()); }
This is the modified version of @d60402's answer: https://stackoverflow.com/a/15573121/4747587
Do everything mentioned there. But instead of having a Base Activity
and making that as a parent for every activity and the overriding the onResume()
and onPause
, do the below:
In your application class, add the line:
registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback);
This callback
has all the activity lifecycle methods and you can now override onActivityResumed()
and onActivityPaused()
.
Take a look at this Gist: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b
What I did is make sure that all in-app activities are launched with startActivityForResult
then checking if onActivityResult was called before onResume. If it wasn't, it means we just returned from somewhere outside our app.
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; }
This is my solution https://github.com/doridori/AndroidUtils/blob/master/App/src/main/java/com/doridori/lib/app/ActivityCounter.java
Basically involved counting the lifecycle methods for all Activity's with a timer to catch cases where there is no activity currently in the foreground but the app is (ie on rotation)
这是我的解决scheme。 Just register this ActivityLifecycleCallbacks in your main Application class. In the comments, I mention a user profile Activity edge case. That Activity is simply one with transparent edges.
/** * 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) { } }
My app needs to "reboot" after return from background – show a series of activities, according to client solicitations. After extensive search on how to manage the background/foreground transitions (treated very differently between iOS and Android), I crossed this question. Found very useful help here, specially from the most voted answer and the one flagged as correct. However, simply reinstantiate the root activity EVERY TIME the app enters foreground looked too annoying, when you think about UX. The solution that worked for me, and the one I think's most adequated – based on the Youtube and Twitter apps functionality – was to combine the answers from @GirishNair and @d60402: Calling the timer when the app's trimming memory, as follows:
@Override public void onTrimMemory(int level) { if (stateOfLifeCycle.equals("Stop")) { startActivityTransitionTimer(); } super.onTrimMemory(level); }
My Timer limit is set to 30 seconds – I'm thinking about increasing this a little.
private final long MAX_ACTIVITY_TRANSITION_TIME = 30000;
And when app goes into foreground, is relaunched, or the app's destroyed, call the method to cancel timer.
On App extension:
@Override public void onActivityCreated(Activity activity, Bundle arg1) { stopActivityTransitionTimer(); stateOfLifeCycle = "Create"; } @Override public void onActivityDestroyed(Activity activity) { stopActivityTransitionTimer(); stateOfLifeCycle = "Destroy"; }
On the activity (preferably on a base activity, inherited by the others):
@Override protected void onStart() { super.onStart(); if (App.wasInBackground) { stopActivityTransitionTimer(); } }
In my case, when app goes foreground after the max time, a new task is created, so the stopActivityTransitionTimer() is called upon onActivityCreated() or onActivityDestroyed(), in the app extension class – turning unnecessary to call the method in an activity. 希望它有帮助。
I was using this with Google Analytics EasyTracker, and it worked. It could be extended to do what you seek using a simple integer.
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) { } }); } }
How about this solution
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(); } } }
All Activity need to extends BaseActivity.
When an activity call another (A->B) then currentAct is not equal getLocalClassName() because the onStart() of the second activity (B) is called before the onStop() of the first (A) ( https://developer.android.com/guide/components/activities.html#CoordinatingActivities ).
When the user press the home button or change between application will just call onStop() and then currentAct is equal getLocalClassName().
This appears to be one of the most complicated questions in Android since (as of this writing) Android doesn't have iOS equivalents of applicationDidEnterBackground()
or applicationWillEnterForeground()
callbacks. I used an AppState Library that was put together by @jenzz .
[AppState is] a simple, reactive Android library based on RxJava that monitors app state changes. It notifies subscribers every time the app goes into background and comes back into foreground.
It turned out this is exactly what I needed, especially because my app had multiple activities so simply checking onStart()
or onStop()
on an activity wasn't going to cut it.
First I added these dependencies to gradle:
dependencies { compile 'com.jenzz.appstate:appstate:3.0.1' compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1' }
Then it was a simple matter of adding these lines to an appropriate place in your code:
//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; } } });
Depending on how you subscribe to the observable, you may have to unsubscribe from it to avoid memory leaks. Again more info on the github page .
These answers don't seem to be correct. These methods are also called when another activity starts and ends. What you can do is keep a global flag (yes, globals are bad:) and set this to true each time you start a new activity. Set it to false in the onCreate of each activity. Then, in the onPause you check this flag. If it's false, your app is going into the background, or it's getting killed.
I'm using this solution: http://nathanael.hevenet.com/android-dev-detecting-when-your-app-is-in-the-background-across-activities/
In short- Build a dedicate service that every activity report him about each lifecycle event, and this service get the info about the status of the app.
Very much like @oldschool4664 solution, but cleaner in my opinion
The principal problem is that you have to get an specific behavior when you start an activity from background. If you override your onPause() and onResume() methods, you'll have a close answer, but not the solution. The problem is that onPause() and onResume() methods are called even if you don't minimize your application, they can be called when you start an activity and later you press the back button to return to your activity. To eliminate that problem and to know really when your application comes from background, you must to get the running process and compare with your process:
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; }
Now you have to declare a boolean variable:
public boolean wasPaused = false;
And ask when your activity comes to background:
@Override public void onPause(){ super.onPause(); if(isApplicationBroughtToBackground()) wasPaused = true; }
Now, when your activity comes to the screen again, ask in onResume() method:
@Override public void onResume(){ super.onResume(); if(wasPaused){ lockScreen(true); } wasPaused = false; }
这就是它。 Now, when your activity comes to background, and later the user brings it to foreground, the lock screen will appear.
If you want to repeat this behavior for whatever activity of your app, you have to create an activity (could be BaseActivity), put this methods, and all your activities have to inherit from BaseActivity.
I hope that this help to you.
问候!