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

后端 未结 30 1318
独厮守ぢ
独厮守ぢ 2020-11-22 00:56

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

相关标签:
30条回答
  • 2020-11-22 01:16

    ActivityLifecycleCallbacks might be of interest, but it isn't well documented.

    Though, if you call registerActivityLifecycleCallbacks() you should be able to get callbacks for when Activities are created, destroyed, etc. You can call getComponentName() for the Activity.

    0 讨论(0)
  • 2020-11-22 01:18

    Here's how I've managed to solve this. It works on the premise that using a time reference between activity transitions will most likely provide adequate evidence that an app has been "backgrounded" or not.

    First, I've used an android.app.Application instance (let's call it MyApplication) which has a Timer, a TimerTask, a constant to represent the maximum number of milliseconds that the transition from one activity to another could reasonably take (I went with a value of 2s), and a boolean to indicate whether or not the app was "in the background":

    public class MyApplication extends Application {
    
        private Timer mActivityTransitionTimer;
        private TimerTask mActivityTransitionTimerTask;
        public boolean wasInBackground;
        private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
        ...
    

    The application also provides two methods for starting and stopping the timer/task:

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

    The last piece of this solution is to add a call to each of these methods from the onResume() and onPause() events of all activities or, preferably, in a base Activity from which all of your concrete Activities inherit:

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

    So in the case when the user is simply navigating between the activities of your app, the onPause() of the departing activity starts the timer, but almost immediately the new activity being entered cancels the timer before it can reach the max transition time. And so wasInBackground would be false.

    On the other hand when an Activity comes to the foreground from the Launcher, device wake up, end phone call, etc., more than likely the timer task executed prior to this event, and thus wasInBackground was set to true.

    0 讨论(0)
  • 2020-11-22 01:19

    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

    0 讨论(0)
  • 2020-11-22 01:19

    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.

    0 讨论(0)
  • 2020-11-22 01:21

    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) {
    
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-11-22 01:23

    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;
    }
    
    0 讨论(0)
提交回复
热议问题