Checking if an Android application is running in the background

前端 未结 30 2648
无人共我
无人共我 2020-11-21 06:19

By background, I mean none of the application\'s activities are currently visible to the user?

相关标签:
30条回答
  • 2020-11-21 06:55

    I tried the recommended solution that uses Application.ActivityLifecycleCallbacks and many others, but they didn't work as expected. Thanks to Sarge, I came up with a pretty easy and straightforward solution that I am describing below.

    They key of the solution is the fact of understanding that if we have ActivityA and ActivityB, and we call ActivityB from ActivityA (and not call ActivityA.finish), then ActivityB's onStart() will be called before ActivityA onStop().

    That's also the main difference between onStop() and onPause() that none did mention in the articles I read.

    So based on this Activity's Lifecycle behavior, you can simply count how many times did onStart() and onPause() got called in your program. Note that for each Activity of your program, you must override onStart() and onStop(), in order to increment/decrement the static variable used for counting. Below is the code implementing this logic. Note that I am using a class that extends Application, so dont forget to declare on Manifest.xml inside Application tag: android:name=".Utilities", although it can be implemented using a simple custom class too.

    public class Utilities extends Application
    {
        private static int stateCounter;
    
        public void onCreate()
        {
            super.onCreate();
            stateCounter = 0;
        }
    
        /**
         * @return true if application is on background
         * */
        public static boolean isApplicationOnBackground()
        {
            return stateCounter == 0;
        }
    
        //to be called on each Activity onStart()
        public static void activityStarted()
        {
            stateCounter++;
        }
    
        //to be called on each Activity onStop()
        public static void activityStopped()
        {
            stateCounter--;
        }
    }
    

    Now on each Activity of our program, we should override onStart() and onStop() and increment/decrement as shown below:

    @Override
    public void onStart()
    {
        super.onStart();
        Utilities.activityStarted();
    }
    
    @Override
    public void onStop()
    {
        Utilities.activityStopped();
        if(Utilities.isApplicationOnBackground())
        {
            //you should want to check here if your application is on background
        }
        super.onStop();
    }
    

    With this logic, there are 2 possible cases:

    1. stateCounter = 0 : The number of stopped is equal with the number of started Activities, which means that the application is running on the background.
    2. stateCounter > 0 : The number of started is bigger than the number of stopped, which means that the application is running on the foreground.

    Notice: stateCounter < 0 would mean that there are more stopped Activities rather than started, which is impossible. If you encounter this case, then it means that you are not increasing/decreasing the counter as you should.

    You are ready to go. You should want to check if your application is on background inside onStop().

    0 讨论(0)
  • 2020-11-21 06:55

    None of the answers quite fitted the specific case if you're looked to know if a specfic activity is in the forground and if you're an SDK without direct access to the Application. For me I was in background thread having just recieved a push notification for a new chat message and only want to display a system notification if the chat screen isn't in the foreground.

    Using the ActivityLifecycleCallbacks that as been recommended in other answers I've created a small util class that houses the logic to whether MyActivity is in the Foreground or not.

    class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {
    
    private var isMyActivityInForeground = false
    
    init {
        (context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
    }
    
    fun isMyActivityForeground() = isMyActivityInForeground
    
    override fun onActivityPaused(activity: Activity?) {
        if (activity is MyActivity) {
            isMyActivityInForeground = false
        }
    }
    
    override fun onActivityResumed(activity: Activity?) {
        if (activity is MyActivity) {
            isMyActivityInForeground = true
        }
    }
    

    }

    0 讨论(0)
  • 2020-11-21 06:56

    I did my own implementation of ActivityLifecycleCallbacks. I'm using SherlockActivity, but for normal Activity class might work.

    First, I'm creating an interface that have all methods for track the activities lifecycle:

    public interface ActivityLifecycleCallbacks{
        public void onActivityStopped(Activity activity);
        public void onActivityStarted(Activity activity);
        public void onActivitySaveInstanceState(Activity activity, Bundle outState);
        public void onActivityResumed(Activity activity);
        public void onActivityPaused(Activity activity);
        public void onActivityDestroyed(Activity activity);
        public void onActivityCreated(Activity activity, Bundle savedInstanceState);
    }
    

    Second, I implemented this interface in my Application's class:

    public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{
    
        @Override
        public void onCreate() {
            super.onCreate();           
        }
    
        @Override
        public void onActivityStopped(Activity activity) {
            Log.i("Tracking Activity Stopped", activity.getLocalClassName());
    
        }
    
        @Override
        public void onActivityStarted(Activity activity) {
            Log.i("Tracking Activity Started", activity.getLocalClassName());
    
        }
    
        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
        }
    
        @Override
        public void onActivityResumed(Activity activity) {
            Log.i("Tracking Activity Resumed", activity.getLocalClassName());
        }
    
        @Override
        public void onActivityPaused(Activity activity) {
            Log.i("Tracking Activity Paused", activity.getLocalClassName());
        }
    
        @Override
        public void onActivityDestroyed(Activity activity) {
            Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
        }
    
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            Log.i("Tracking Activity Created", activity.getLocalClassName());
        }
    }
    

    Third, I'm creating a class that extends from SherlockActivity:

    public class MySherlockActivity extends SherlockActivity {
    
        protected MyApplication nMyApplication;
    
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            nMyApplication = (MyApplication) getApplication();
            nMyApplication.onActivityCreated(this, savedInstanceState);
        }
    
        protected void onResume() {
            // TODO Auto-generated method stub
            nMyApplication.onActivityResumed(this);
            super.onResume();
    
        }
    
        @Override
        protected void onPause() {
            // TODO Auto-generated method stub
            nMyApplication.onActivityPaused(this);
            super.onPause();
        }
    
        @Override
        protected void onDestroy() {
            // TODO Auto-generated method stub
            nMyApplication.onActivityDestroyed(this);
            super.onDestroy();
        }
    
        @Override
        protected void onStart() {
            nMyApplication.onActivityStarted(this);
            super.onStart();
        }
    
        @Override
        protected void onStop() {
            nMyApplication.onActivityStopped(this);
            super.onStop();
        }
    
        @Override
        protected void onSaveInstanceState(Bundle outState) {
            nMyApplication.onActivitySaveInstanceState(this, outState);
            super.onSaveInstanceState(outState);
        }   
    }
    

    Fourth, all class that extend from SherlockActivity, I replaced for MySherlockActivity:

    public class MainActivity extends MySherlockActivity{
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
        }
    
    }
    

    Now, in the logcat you will see the logs programmed in the Interface implementation made in MyApplication.

    0 讨论(0)
  • 2020-11-21 06:58

    It might be too late to answer but if somebody comes visiting then here is the solution I suggest, The reason(s) an app wants to know it's state of being in background or coming to foreground can be many, a few are, 1. To show toasts and notifications when the user is in BG. 2.To perform some tasks for the first time user comes from BG, like a poll, redraw etc.

    The solution by Idolon and others takes care of the first part, but does not for the second. If there are multiple activities in your app, and the user is switching between them, then by the time you are in second activity, the visible flag will be false. So it cannot be used deterministically.

    I did something what was suggested by CommonsWare, "If the Service determines that there are no activities visible, and it remains that way for some amount of time, stop the data transfer at the next logical stopping point."

    The line in bold is important and this can be used to achieve second item. So what I do is once I get the onActivityPaused() , don not change the visible to false directly, instead have a timer of 3 seconds (that is the max that the next activity should be launched), and if there is not onActivityResumed() call in the next 3 seconds, change visible to false. Similarly in onActivityResumed() if there is a timer then I cancel it. To sum up,the visible becomes isAppInBackground.

    Sorry cannot copy-paste the code...

    0 讨论(0)
  • 2020-11-21 06:59

    This code will check foreground and background in any condition:

    Java Code:

    private static boolean isApplicationForeground(Context context) {
        KeyguardManager keyguardManager =
                (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
    
        if (keyguardManager.isKeyguardLocked()) {
            return false;
        }
        int myPid = Process.myPid();
    
        ActivityManager activityManager =
                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    
        List<ActivityManager.RunningAppProcessInfo> list;
    
        if ((list = activityManager.getRunningAppProcesses()) != null) {
            for (ActivityManager.RunningAppProcessInfo aList : list) {
                ActivityManager.RunningAppProcessInfo info;
                if ((info = aList).pid == myPid) {
                    return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
                }
            }
        }
        return false;
    }
    

    Kotlin Code:

    private fun isApplicationForeground(context: Context): Boolean {
            val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
            if (keyguardManager.isKeyguardLocked) {
                return false
            }
            val myPid = Process.myPid()
            val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            var list: List<ActivityManager.RunningAppProcessInfo>
            if (activityManager.runningAppProcesses.also { list = it } != null) {
                for (aList in list) {
                    var info: ActivityManager.RunningAppProcessInfo
                    if (aList.also { info = it }.pid == myPid) {
                        return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                    }
                }
            }
            return false
        }
    
    0 讨论(0)
  • 2020-11-21 07:05

    In my activities onResume and onPause I write an isVisible boolean to SharedPrefences.

        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        Editor editor = sharedPrefs.edit();
        editor.putBoolean("visible", false);
        editor.commit();
    

    And read it elsewhere when needed via,

        // Show a Toast Notification if App is not visible (ie in background. Not running, etc) 
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
        if(!sharedPrefs.getBoolean("visible", true)){...}
    

    Maybe not elegant, but it works for me...

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