29

I am developing an Android app and I want to detect when the user exits my app either by clicking the Back button or the Home button.

Also, an event like onInit() would be useful in my scenario, as I just want to have the MyInıt action start at first.

onDestroy() is not called until other apps need more memory.

Daniel
  • 2,355
  • 9
  • 23
  • 30
enesness
  • 3,123
  • 5
  • 31
  • 33
  • further - `onDestroy()` is often not called at all. Often the system just kills the process without any callbacks. – Richard Le Mesurier Nov 12 '15 at 05:29
  • A small article implementing the solution using Lifecycle listeners - https://www.vishnusreddy.com/callbacks-for-when-the-user-exits-your-android-app/ – Vishnu Jul 22 '22 at 17:38

6 Answers6

31

Note: This only works if your target is >= API14


You can also use Application.registerActivityLifecycleCallbacks() and when any activity pauses post a delayed Runnable (that will invoke when user lefts app) to Handler. When activities are created/started/resumed you remove that Runnable from Handler. So when you navigate inside your app you always cancel that Runnable, but if another activity not from your app is activated - the Runnable will be invoked.

I used this to logout user when he lefts my app, here's code for callbacks:

public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks {

    private Handler handler;
    private Runnable runLogout = new Runnable() {
        @Override
        public void run() {
            //logoutUser()
        }
    };

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

        registerActivityLifecycleCallbacks(this);
        handler = new Handler(getMainLooper());
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        handler.removeCallbacks(runLogout);
    }

    @Override
    public void onActivityStarted(Activity activity) {
        handler.removeCallbacks(runLogout);
    }

    @Override
    public void onActivityResumed(Activity activity) {
        handler.removeCallbacks(runLogout);
    }

    @Override
    public void onActivityPaused(Activity activity) {
        handler.postDelayed(runLogout, 1000);
    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

However, Runnable will run not in the context of activity lifecycle, so to use this in activity you would need to set and check some application-wide flag or broadcast an intent.

PLNech
  • 3,087
  • 1
  • 23
  • 52
  • best solution I ever found for this problem. – Meet Vora Oct 06 '17 at 10:03
  • This solution is great but after introduction on ChromeTabs which opens a chrome instance like a part of your app this solution will behave as if the app is minimized as all activities show as paused and the only resumed one is of chrome which is not part of your app and you don't get a callback for onActivityResumed() . Any workaround on this? – bpr10 Oct 09 '17 at 11:32
  • but this won't work when app is closed by swiping right ? Its not working for me – Nabeel K Jun 08 '18 at 09:12
19

If your activity is the last in the stack then detecting a back button with onKeyDown would solve 1/2 of this

The home key is a little trickier, there is no absolute method but you could do something like this which works for my simple needs.

The onUserLeaveHint is called according to the documentation when the user clicks the home button OR when something interrupts your application (like an incoming phone call) so to guess which it is you use the onUserInteraction method to stamp the last user interaction time.

Now if that precedes the onUserLeaveHint closely enough you can assume (not guaranteed but has worked for me so far) that the home button was the reason your application is being pushed into the background (exiting)

Not sure what your intent is in catching the home button, anyway here is a simplistic way to do that, I use a 100ms fence around the two events which I have found has always worked for me. NOTE: I have only tested on a handful of phones, like all things in Android your mileage will vary dependent on OS / Hardware (heck even the stuff that's documented and supposed to work sometimes doesn't)

long userInteractionTime = 0;

@Override
public void onUserInteraction() {
    userInteractionTime = System.currentTimeMillis();
    super.onUserInteraction();
    Log.i("appname","Interaction");
}

@Override
public void onUserLeaveHint() {
    long uiDelta = (System.currentTimeMillis() - userInteractionTime);

    super.onUserLeaveHint();
    Log.i("bThere","Last User Interaction = "+uiLag);
    if (uiDelta < 100)
        Log.i("appname","Home Key Pressed");    
    else
        Log.i("appname","We are leaving, but will probably be back shortly!");  
}
Idistic
  • 6,281
  • 2
  • 28
  • 38
  • 6
    `onUserLeaveHint()` does not get called (at least on some systems) when incoming call pushed the activity to the background. In fact, from a phone call, you can press HOME and go to any app, and `onUserLeaveHint()` will still not be called. – Richard Le Mesurier Apr 05 '13 at 08:03
  • 3
    @Richard Le Mesurier well I don't doubt it works differently on some systems, but it's a defect if that's true. Welcome to the wonderful world of Android fragmentation ;-) – Idistic Apr 05 '13 at 21:14
  • 6
    The documentation as mentioned in @Sreedevi's answer (http://stackoverflow.com/a/7793412/383414) comes from the Android docs for `Activity.onUserLeaveHint()` (http://developer.android.com/reference/android/app/Activity.html#onUserLeaveHint()). The method is designed so that it is not called when a phonecall comes in. (Fragmentation... keeping us Android devs in work.) – Richard Le Mesurier Apr 06 '13 at 14:49
  • 2
    Always says Home Key Pressed, even when app goes in background due to an app being launched from the notification bar. Can never get the second message in any case. – A.J. Jun 29 '14 at 20:46
  • 1
    @RichardLeMesurier For some reason, `onUserLeaveHint()` gets called whenever I switch activities. How can I fix this? – Ruchir Baronia Nov 11 '15 at 19:47
  • 1
    So does this only work if user has left activity, rather than if user has left app? – Ruchir Baronia Nov 11 '15 at 19:48
  • 1
    @Rich these days you would most likely just use `Application.ActivityLifecycleCallbacks` as suggested in [Justin's answer](http://stackoverflow.com/a/29999738/383414). Also see [this Gist forked from work by Steve Liles](https://gist.github.com/mobiRic/c8467eab1c2f3e59fcdb) – Richard Le Mesurier Nov 12 '15 at 05:27
9

The onUserLeaveHint is called according to the documentation when the user clicks the home button OR when something interrupts your application (like an incoming phone call) so to guess which it is you use the onUserInteraction method to stamp the last user interaction time.

Just a small correction to the answer Idistic gave - OnUserLeaveHint() is not called when your activity is interrupted by an incoming call. Here is the snippet from Android documentation -

Called as part of the activity lifecycle when an activity is about to go into the background as the result of user choice. For example, when the user presses the Home key, onUserLeaveHint() will be called, but when an incoming phone call causes the in-call Activity to be automatically brought to the foreground, onUserLeaveHint() will not be called on the activity being interrupted.

Sreedevi J
  • 673
  • 1
  • 9
  • 15
0

With the help of Application.ActivityLifecycleCallbacks you can track which activity is destroying and new activity is starting but when user swipe or back button(on last stack) then postdelayed will not call and also Activitystop and destroy will always call when you're inside your application if you want to catch user swipe or back button(on last stack) then this code will work.

public class Yourapp extends Application implements Application.ActivityLifecycleCallbacks {

    private Activity currentActivity; 
    private Handler closecheckhandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                //do whatever you want
                default:
                    super.handleMessage(msg);
            }
        }
    };
    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(this);

    }

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

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {
        currentActivity = 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 destroActivity) {
      if (currentActivity.equals(destroActivity)) {
            closecheckhandler.sendEmptyMessage(//Custom Code);
        } else {
            //do whatever you want
        }
    }
}
pappbence96
  • 1,164
  • 2
  • 12
  • 20
Skand kumar
  • 93
  • 1
  • 1
  • 9
0

Yes, check the lifecycle of the app - when a user minimizes or leaves a couple of methods get called onPause() onStop() and onDestroy()

http://developer.android.com/reference/android/app/Activity.html

BZ.
  • 1,928
  • 2
  • 17
  • 26
  • 2
    These are per activity, but I mean the app. – enesness Jul 13 '11 at 17:19
  • 3
    I don't think android supports something like that on a per app basis - however, keep in mind any App is just a collection of Activities. One option would be to do a parent activity class for all your individual ones with the behavior you want. Another option might be a common class each activity calls to do your clean up. – BZ. Jul 13 '11 at 19:29
  • 1
    There is no guarantee that `onDestroy()` is ever called, as the task can be killed directly after `onPause()` and thus never reaching `onStop()`. And when the user presses the home button, `onDestroy()` is not automatically called. Worse, `onStop()` is called both when you leave the Activity with the home key and when you start another activity. – Heiko Rupp Dec 21 '12 at 18:45
-1

another option that you might consider is to check for the back button or home key press. This is done by overriding the onKeyDown() , listen for the back key/home key and override the default behaviour.

I've found a similar question that might help you out.

Android: Prompt user to save changes when Back button is pressed

Cheers

Community
  • 1
  • 1
Dave
  • 3,178
  • 5
  • 28
  • 44
  • The home key will never be caught by onKeyDown (or any other method for that matter) – Idistic Jul 13 '11 at 20:08
  • 1
    At the time this was written it could with the KeyEvent.KEYCODE_HOME. example here: http://stackoverflow.com/questions/5547818/can-i-override-the-home-button-in-my-application It looks like that has changed since, but that was over 4 yrs ago. People should consider looking at the date of the post before downvoting. – Dave Nov 17 '15 at 19:10