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

后端 未结 30 1351
独厮守ぢ
独厮守ぢ 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:07

    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.

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

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

    You can use:

    protected void onRestart ()

    To differ between new starts and restarts.

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

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

    If your app consists of multiple activites and/or stacked activites like a tab bar widget, then overriding onPause() and onResume() will not work. I.e when starting a new activity the current activites will get paused before the new one is created. The same applies when finishing (using "back" button) an activity.

    I've found two methods that seem to work as wanted.

    The first one requires the GET_TASKS permission and consists of a simple method that checks if the top running activity on the device belongs to application, by comparing package names:

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

    This method was found in the Droid-Fu (now called Ignition) framework.

    The second method that I've implemented my self does not require the GET_TASKS permission, which is good. Instead it is a little more complicated to implement.

    In you MainApplication class you have a variable that tracks number of running activities in your application. In onResume() for each activity you increase the variable and in onPause() you decrease it.

    When the number of running activities reaches 0, the application is put into background IF the following conditions are true:

    • The activity being paused is not being finished ("back" button was used). This can be done by using method activity.isFinishing()
    • A new activity (same package name) is not being started. You can override the startActivity() method to set a variable that indicates this and then reset it in onPostResume(), which is the last method to be run when an activity is created/resumed.

    When you can detect that the application has resigned to the background it is easy detect when it is brought back to foreground as well.

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

    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

    0 讨论(0)
提交回复
热议问题