iHorse
iHorse

Reputation: 4629

How to detect when an Android app goes to the background and come back to the foreground

I am trying to write an app that does something specific when it is brought back to the foreground after some amount of time. Is there a way to detect when an app is sent to the background or brought to the foreground?

Upvotes: 461

Views: 406566

Answers (30)

Martin Marconcini
Martin Marconcini

Reputation: 27246

2018: Android supports this natively through lifecycle components.

March 2018 UPDATE: There is now a better solution. See ProcessLifecycleOwner. You will need to use the new architecture components 1.1.0 (latest at this time) but it’s specifically designed to do this.

There’s a simple sample provided in this answer but I wrote a sample app and a blog post about it.

Ever since I wrote this back in 2014, different solutions arose. Some worked, some were thought to be working, but had flaws (including mine!) and we, as a community (Android) learned to live with the consequences and wrote workarounds for the special cases.

Never assume a single snippet of code is the solution you’re looking for, it’s unlikely the case; better yet, try to understand what it does and why it does it.

The MemoryBoss class was never actually used by me as written here, it was just a piece of pseudo code that happened to work.

Unless there’s valid reason for you not to use the new architecture components (and there are some, especially if you target super old apis), then go ahead and use them. They are far from perfect, but neither were ComponentCallbacks2.

UPDATE / NOTES (November 2015): People has been making two comments, first is that >= should be used instead of == because the documentation states that you shouldn't check for exact values. This is fine for most cases, but bear in mind that if you only care about doing something when the app went to the background, you will have to use == and also combine it with another solution (like Activity Lifecycle callbacks), or you may not get your desired effect. The example (and this happened to me) is that if you want to lock your app with a password screen when it goes to the background (like 1Password if you're familiar with it), you may accidentally lock your app if you run low on memory and are suddenly testing for >= TRIM_MEMORY, because Android will trigger a LOW MEMORY call and that's higher than yours. So be careful how/what you test.

Additionally, some people have asked about how to detect when you get back.

The simplest way I can think of is explained below, but since some people are unfamiliar with it, I'm adding some pseudo code right here. Assuming you have YourApplication and the MemoryBoss classes, in your class BaseActivity extends Activity (you will need to create one if you don't have one).

@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);
    }
}

I recommend onStart because Dialogs can pause an activity so I bet you don't want your app to think "it went to the background" if all you did was display a full screen dialog, but your mileage may vary.

And that's all. The code in the if block will only be executed once, even if you go to another activity, the new one (that also extends BaseActivity) will report wasInBackground is false so it won't execute the code, until onMemoryTrimmed is called and the flag is set to true again.

UPDATE / NOTES (April 2015): Before you go all Copy and Paste on this code, note that I have found a couple of instances where it may not be 100% reliable and must be combined with other methods to achieve the best results. Notably, there are two known instances where the onTrimMemory call back is not guaranteed to be executed:

  1. If your phone locks the screen while your app is visible (say your device locks after nn minutes), this callback is not called (or not always) because the lockscreen is just on top, but your app is still "running" albeit covered.

  2. If your device is relatively low on memory (and under memory stress), the Operating System seems to ignore this call and go straight to more critical levels.

Now, depending how important it's for you to know when your app went to the background, you may or may not need to extend this solution together with keeping track of the activity lifecycle and whatnot.

Just keep the above in mind and have a good QA team ;)

END OF UPDATE

It may be late but there's a reliable method in Ice Cream Sandwich (API 14) and Above.

Turns out that when your app has no more visible UI, a callback is triggered. The callback, which you can implement in a custom class, is called ComponentCallbacks2 (yes, with a two). This callback is only available in API Level 14 (Ice Cream Sandwich) and above.

You basically get a call to the method:

public abstract void onTrimMemory (int level)

The Level is 20 or more specifically

public static final int TRIM_MEMORY_UI_HIDDEN

I've been testing this and it always works, because level 20 is just a "suggestion" that you might want to release some resources since your app is no longer visible.

To quote the official docs:

Level for onTrimMemory(int): the process had been showing a user interface, and is no longer doing so. Large allocations with the UI should be released at this point to allow memory to be better managed.

Of course, you should implement this to actually do what it says (purge memory that hasn't been used in certain time, clear some collections that have been sitting unused, etc. The possibilities are endless (see the official docs for other possible more critical levels).

But, the interesting thing, is that the OS is telling you: HEY, your app went to the background!

Which is exactly what you wanted to know in the first place.

How do you determine when you got back?

Well that's easy, I'm sure you have a "BaseActivity" so you can use your onResume() to flag the fact that you're back. Because the only time you will be saying you're not back is when you actually receive a call to the above onTrimMemory method.

It works. You don't get false positives. If an activity is resuming, you're back, 100% of the times. If the user goes to the back again, you get another onTrimMemory() call.

You need to suscribe your Activities (or better yet, a custom class).

The easiest way to guarantee that you always receive this is to create a simple class like this:

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.
    }
}

In order to use this, in your Application implementation (you have one, RIGHT?), do something like:

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

If you create an Interface you could add an else to that if and implement ComponentCallbacks (without the 2) used in anything below API 14. That callback only has the onLowMemory() method and does not get called when you go to the background, but you should use it to trim memory.

Now launch your App and press home. Your onTrimMemory(final int level) method should be called (hint: add logging).

The last step is to unregister from the callback. Probably the best place is the onTerminate() method of your App, but, that method doesn't get called on a real device:

/**
 * 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.
 */

So unless you really have a situation where you no longer want to be registered, you can safety ignore it, since your process is dying at OS level anyway.

If you decide to unregister at some point (if you, for example, provide a shutdown mechanism for your app to clean up and die), you can do:

unregisterComponentCallbacks(mMemoryBoss);

And that's it.

Upvotes: 233

Micro
Micro

Reputation: 10891

LifecycleObserver is deprecated. Use DefaultLifecycleObserver instead:

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);
    }
}

Dependencies:

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

Upvotes: 2

aminography
aminography

Reputation: 22832

Here is the solution that by using a debouncing logic, makes sure we are not getting consecutive background/foreground events. So, it always reflects a stable state of backgrounding/foregrounding.

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)
        }
    }
}

Just needs to create an instance of 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()

Don't forget to add a proper version of the dependency:

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

Upvotes: 2

vokilam
vokilam

Reputation: 10323

UPDATE November 2021

Actual setup is as follows

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
    }
}

Dependencies

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

ORIGINAL ANSWER

ProcessLifecycleOwner seems to be a promising solution also.

ProcessLifecycleOwner will dispatch ON_START, ON_RESUME events, as a first activity moves through these events. ON_PAUSE, ON_STOP, events will be dispatched with a delay after a last activity passed through them. This delay is long enough to guarantee that ProcessLifecycleOwner won't send any events if activities are destroyed and recreated due to a configuration change.

An implementation can be as simple as

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())

According to source code, current delay value is 700ms.

Also using this feature requires the dependencies:

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

Upvotes: 182

Martin Zeitler
Martin Zeitler

Reputation: 76849

I like the ProcessLifecycleOwner approach, but actually one can skip all of that, because in an Activity's onCreate() method, one can easily determine if it's the first or a subsequent run:

@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 */
    }
}

Upvotes: 0

Linh
Linh

Reputation: 61019

An example to detect app from background to foreground in Activity (or any class) using ProcessLifecycleOwner.
When the application start, I cache the start time and then in each activity I will check with application time to know if activity start at first time or from background

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>

DEMO https://github.com/PhanVanLinh/AndroidDetectAppFromBackgroundToForeground

Upvotes: 1

WitWicky
WitWicky

Reputation: 291

There are three ways through which you can achieve this:

  • Single Activity architecture
  • ActivityLifecycleCallback
  • LifecycleObserver and ProcessLifecycleOwner

Have written an article in detail on this over here. Hope it helps.

Upvotes: 1

Priyanka
Priyanka

Reputation: 3699

you can simply call this method in your application class

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 will simply return the state of the application

ON_CREATE
ON_START
ON_RESUME
ON_PAUSE
ON_STOP
ON_DESTROY
ON_ANY

it will return ON_PAUSE & ON_STOP when the app goes to background and will return ON_START & ON_RESUME when the app comes to the foreground

Upvotes: 3

Al&#233;cio Carvalho
Al&#233;cio Carvalho

Reputation: 13657

You can use the ProcessLifecycleOwner attaching a lifecycle observer to it.

  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");
    }
}

then on the onCreate() of your Application class you call this:

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

with this you will be able to capture the events of ON_PAUSE and ON_STOP of your application that happen when it goes in background.

Upvotes: 7

Aliaksei
Aliaksei

Reputation: 3642

We can expand this solution using 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
    }
}

Now we can subscribe to this LiveData and catch the needed events. For example:

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

Upvotes: 1

Komal Nikhare
Komal Nikhare

Reputation: 252

There are no straightforward lifecycle methods to tell you when the whole Application goes background/foreground.

I have done this with simple way. Follow the below instructions to detect application background/foreground phase.

With a little workaround, it is possible. Here, ActivityLifecycleCallbacks comes to the rescue. Let me walk through step-by-step.

  1. First, create a class that extends the android.app.Application and implements the ActivityLifecycleCallbacks interface. In the Application.onCreate(), register the callback.

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
    
  2. Register the “App” class in the Manifest as below, <application android:name=".App".

  3. There will be at least one Activity in the started state when the app is in the foreground and there will be no Activity in the started state when the app is in the background.

    Declare 2 variables as below in the “App” class.

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

    activityReferences will keep the count of number of activities in the started state. isActivityChangingConfigurations is a flag to indicate if the current Activity is going through configuration change like an orientation switch.

  4. Using the following code you can detect if the App comes foreground.

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
    
  5. This is how to detect if the App goes background.

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

How it works:

This is a little trick done with the way the Lifecycle methods are called in sequence. Let me walkthrough a scenario.

Assume that the user launches the App and the Launcher Activity A is launched. The Lifecycle calls will be,

A.onCreate()

A.onStart() (++activityReferences == 1) (App enters Foreground)

A.onResume()

Now Activity A starts Activity B.

A.onPause()

B.onCreate()

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

B.onResume()

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

Then the user navigates back from Activity B,

B.onPause()

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

A.onResume()

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

B.onDestroy()

Then the user presses Home button,

A.onPause()

A.onStop() (--activityReferences == 0) (App enters Background)

In case, if the user presses Home button from Activity B instead of Back button, still it will be the same and activityReferences will be 0. Hence, we can detect as the App entering Background.

So, what’s the role of isActivityChangingConfigurations? In the above scenario, suppose the Activity B changes the orientation. The callback sequence will be,

B.onPause()

B.onStop() (--activityReferences == 0) (App enters Background??)

B.onDestroy()

B.onCreate()

B.onStart() (++activityReferences == 1) (App enters Foreground??)

B.onResume()

That’s why we have an additional check of isActivityChangingConfigurations to avoid the scenario when the Activity is going through the Configuration changes.

Upvotes: 6

Erfan
Erfan

Reputation: 3353

Correct Answer here

Create class with name MyApp like below:

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() {

    }
}

Then, everywhere you want (better first activity launched in app), add the code below:

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);

Done! Now when the app is in the background, we get log status : we are out and when we go in app, we get log status : we are out

Upvotes: 2

Rhusfer
Rhusfer

Reputation: 581

I managed to monitor app navigation going to background and back to foreground by implementing a BaseActivity that exploits the use of onResume, onPause and onStop activity callbacks. Here is my implementations.

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()
    }
}

After creating BaseActivity, you just have to extend this activity to any activity on your app.

In these type of implementation, you can accurately detect the following: - onBackground > app will go to background - onForeground > app will go back to foreground - onLaunch > app just opened

I hope this will help you :)

Upvotes: 0

matdev
matdev

Reputation: 4283

The android.arch.lifecycle package provides classes and interfaces that let you build lifecycle-aware components

Your application should implement the LifecycleObserver interface:

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");
    }
}

To do that, you need to add this dependency to your build.gradle file:

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

As recommended by Google, you should minimize the code executed in the lifecycle methods of activities:

A common pattern is to implement the actions of the dependent components in the lifecycle methods of activities and fragments. However, this pattern leads to a poor organization of the code and to the proliferation of errors. By using lifecycle-aware components, you can move the code of dependent components out of the lifecycle methods and into the components themselves.

You can read more here: https://developer.android.com/topic/libraries/architecture/lifecycle

Upvotes: 17

Julian Horst
Julian Horst

Reputation: 805

Since I did not find any approach, which also handles rotation without checking time stamps, I thought I also share how we now do it in our app. The only addition to this answer https://stackoverflow.com/a/42679191/5119746 is, that we also take the orientation into consideration.

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

Then, for the callbacks we have the resume first:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

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

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

And onActivityStopped:

   override fun onActivityStopped(activity: Activity?) {

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

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

        mAppIsInBackground = true
    }

And then, here comes the addition: Checking for orientation changes:

   override fun onConfigurationChanged(newConfig: Configuration) {

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

That's it. Hope this helps someone :)

Upvotes: 2

Naveen T P
Naveen T P

Reputation: 7295

You can achieve this easily with the help of ActivityLifecycleCallbacks and ComponentCallbacks2 something like below.

Create a class AppLifeCycleHandler implementing above said interfaces.

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();
  }
}

In your class which extends Application implement AppLifeCycleCallback to get the callbacks when app switches between foreground and background. Something like below.

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");
    }
}

Hope this helps.

EDIT As an alternative you can now use Life cycle aware architecture component.

Upvotes: 2

Harpreet
Harpreet

Reputation: 3070

Create a class that extends Application. Then in it we can use its override method, onTrimMemory().

To detect if the application went to the background, we will use:

 @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
        }
    }

Upvotes: 37

Jitendra Singh
Jitendra Singh

Reputation: 149

By using below code I'm able to get my app foreground or background state.

For more detail about it's working, strong text click here

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");
    }
  }
}

Upvotes: 0

Ege Kuzubasioglu
Ege Kuzubasioglu

Reputation: 6282

This is pretty easy with ProcessLifecycleOwner

Add these dependencies

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

In 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")
    }
}

Then in your base activity:

override fun onCreate() {
        super.onCreate()

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

See my article on this topic: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48

Upvotes: 6

Henry
Henry

Reputation: 17851

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

Upvotes: 1

Fred Porci&#250;ncula
Fred Porci&#250;ncula

Reputation: 8902

Edit: the new architecture components brought something promising: ProcessLifecycleOwner, see @vokilam's answer


The actual solution according to a Google I/O talk:

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
    }
  }

}

Yes. I know it's hard to believe this simple solution works since we have so many weird solutions here.

But there is hope.

Upvotes: 172

deniz
deniz

Reputation: 975

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.

Upvotes: 1

Amir Ziarati
Amir Ziarati

Reputation: 15107

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());
}

Upvotes: 1

Ismael
Ismael

Reputation: 357

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().

Upvotes: 0

Nick Frolov
Nick Frolov

Reputation: 1855

We use this method. It looks too simple to work, but it was well-tested in our app and in fact works surprisingly well in all cases, including going to home screen by "home" button, by "return" button, or after screen lock. Give it a try.

Idea is, when in foreground, Android always starts new activity just before stopping previous one. That's not guaranteed, but that's how it works. BTW, Flurry seems to use the same logic (just a guess, I didn't check that, but it hooks at the same events).

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
        }
    }

}

Edit: as per comments, we also moved to onStart() in later versions of the code. Also, I'm adding super calls, which were missing from my initial post, because this was more of a concept than a working code.

Upvotes: 64

kiran boghra
kiran boghra

Reputation: 3822

I have created a project on Github app-foreground-background-listen

Create a BaseActivity for all Activity in your application.

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
    }
}

Now use this BaseActivity as a super class of all your Activity like MainActivity extends BaseActivity and onAppStart will be called when you start your application and onAppPause() will be called when the application goes the background from any screen.

Upvotes: 6

Bill Mote
Bill Mote

Reputation: 12823

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) {

            }
        });
    }
}

Upvotes: 2

Sky Kelsey
Sky Kelsey

Reputation: 19290

Edit 2: What I've written below will not actually work. Google has rejected an app that includes a call to ActivityManager.getRunningTasks(). From the documentation, it is apparent that this API is for debugging and development purposes only. I'll be updating this post as soon as I have time to update the GitHub project below with a new scheme that uses timers and is almost as good.

Edit 1: I've written up a blog post and created a simple GitHub repository to make this really easy.

The accepted and top rated answer are both not really the best approach. 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;
}

Upvotes: 3

Pablo
Pablo

Reputation: 391

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. Hope it helps.

Upvotes: 0

AYBABTU
AYBABTU

Reputation: 1026

You can use:

protected void onRestart ()

To differ between new starts and restarts.

enter image description here

Upvotes: 5

Related Questions