Checking if an Android application is running in the background

前端 未结 30 2843
无人共我
无人共我 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 07:14

    In my opinion, many answers introduce a heavy load of code and bring lots of complexity and non-readability.

    When people ask on SO how to communicate between a Service and a Activity, I usually advice to use the LocalBroadcastManager.


    Why?

    Well, by quoting the docs:

    • You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.

    • It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.

    • It is more efficient than sending a global broadcast through the system.

    Not in the the docs:

    • It does not require external libraries
    • The code is minimal
    • It's fast to implement and understand
    • No custom self-implemented callbacks / ultra-singleton / intra-process pattern whatsoever...
    • No strong references on Activity, Application, ...

    Description

    So, you want to check if any of the Activity is currently in the foreground. You usually do that in a Service, or your Application class.

    This means, your Activity objects become the sender of a signal (I'm on / I'm off). Your Service, on the other hand, becomes the Receiver.

    There are two moments in which your Activity tells you if it's going in the foreground or in the background (yes only two... not 6).

    When the Activity goes into the foreground, the onResume() method is triggered (also called after onCreate()).

    When the Activity goes in the back, onPause() is called.

    These are the moments in which your Activity should send the signal to your Service to describe its state.

    In case of multiple Activity's, remember the an Activity goes into the background first, then another one comes into the foreground.

    So the situation would be:*

    Activity1 -- send --> Signal:OFF
    Activity2 -- send --> Signal:ON
    

    The Service / Application will simply keep listening for those signals and act accordingly.


    Code (TLDR)

    Your Service must implement a BroadcastReceiver in order to listen for signals.

    this.localBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // received data if Activity is on / off
        }
    }
    
    public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL") 
    

    Register the Receiver in Service::onCreate()

    @Override
    protected void onCreate() {
        LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
    }
    

    Un-register it in Service::onDestroy()

    @Override
    protected void onDestroy() {
        // I'm dead, no need to listen to anything anymore.
        LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
    }
    

    Now your Activity's must communicated their state.

    In Activity::onResume()

    Intent intent = new Intent();
    intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent    
    LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
    

    In Activity::onPause()

    Intent intent = new Intent();
    intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent    
    LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
    

    A very, very common situation

    Developer: I want to send data from my Service and update the Activity. How do I check if the Activity is in the foreground?

    There is usually no need to check if the Activity is in the foreground or not. Just send the data via LocalBroadcastManager from your Service. If the Activity is on, then it will respond and act.

    For this very common situation, the Service becomes the sender, and the Activity implements the BroadcastReceiver.

    So, create a Receiver in your Activity. Register it in onResume() and un-register it in onPause(). There is no need to use the other life-cycle methods.

    Define the Receiver behavior in onReceive() (update ListView, do this, do that, ...).

    This way the Activity will listen only if it's in the foreground and nothing will happen if it's in the back or is destroyed.

    In case of multiple Activity's, whichever Activity is on will respond (if they also implement the Receiver).

    If all are in the background, nobody will respond and the signal will simply get lost.

    Send the data from the Service via Intent (see code above) by specifying the signal ID.


    • Except for Multi-Window Support. It may be tricky (please test it if needed)...
    0 讨论(0)
  • 2020-11-21 07:15

    Idolon's answer is error prone and much more complicated althought repeatead here check android application is in foreground or not? and here Determining the current foreground application from a background task or service

    There is a much more simpler approach:

    On a BaseActivity that all Activities extend:

    protected static boolean isVisible = false;
    
     @Override
     public void onResume()
     {
         super.onResume();
         setVisible(true);
     }
    
    
     @Override
     public void onPause()
     {
         super.onPause();
         setVisible(false);
     }
    

    Whenever you need to check if any of your application activities is in foreground just check isVisible();

    To understand this approach check this answer of side-by-side activity lifecycle: Activity side-by-side lifecycle

    0 讨论(0)
  • 2020-11-21 07:15

    To piggyback on what CommonsWare and Key have said, you could perhaps extend the Application class and have all of your activities call that on their onPause/onResume methods. This would allow you to know which Activity(ies) are visible, but this could probably be handled better.

    Can you elaborate on what you have in mind exactly? When you say running in the background do you mean simply having your application still in memory even though it is not currently on screen? Have you looked into using Services as a more persistent way to manage your app when it is not in focus?

    0 讨论(0)
  • 2020-11-21 07:16

    There is no way, short of you tracking it yourself, to determine if any of your activities are visible or not. Perhaps you should consider asking a new StackOverflow question, explaining what it is you are trying to achieve from a user experience, so we can perhaps give you alternative implementation ideas.

    0 讨论(0)
  • 2020-11-21 07:17

    If you turn on developer settings "Don't keep actvities" - check only count of created activites is not enough. You must check also isSaveInstanceState. My custom method isApplicationRunning() check is android app is running:

    Here my work code:

    public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
        private int created;
        private boolean isSaveInstanceState;
        private static AppLifecycleService instance;
    
        private final static String TAG = AppLifecycleService.class.getName();
    
        public static AppLifecycleService getInstance() {
            if (instance == null) {
                instance = new AppLifecycleService();
            }
            return instance;
        }
    
        public static boolean isApplicationRunning() {
            boolean isApplicationRunning = true;
            if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
                isApplicationRunning = false;
            }
            return isApplicationRunning;
        }
    
        public static boolean isSaveInstanceState() {
            return AppLifecycleService.getInstance().isSaveInstanceState;
        }
    
        public static int getCountCreatedActvities() {
            return AppLifecycleService.getInstance().created;
        }
    
        private AppLifecycleService() {
        }
    
        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            this.isSaveInstanceState = true;
        }
    
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            ++created;
        }
    
        @Override
        public void onActivityDestroyed(Activity activity) {
            --created;
        }
    
        @Override
        public void onActivityResumed(Activity activity) {   }
    
        @Override
        public void onActivityPaused(Activity activity) { }
    
    
        @Override
        public void onActivityStarted(Activity activity) { }
    
        @Override
        public void onActivityStopped(Activity activity) { }        
    
    }
    
    0 讨论(0)
  • 2020-11-21 07:18

    Another solution for this old post (for those that it might help) :


    <application android:name=".BaseApplication" ... >
    

    public class BaseApplication extends Application {
    
        private class Status {
            public boolean isVisible = true;
            public boolean isFocused = true;
        }
    
        private Map<Activity, Status> activities;
    
        @Override
        public void onCreate() {
            activities = new HashMap<Activity, Status>();
            super.onCreate();
        }
    
        private boolean hasVisibleActivity() {
            for (Status status : activities.values())
                if (status.isVisible)
                    return true;
            return false;
        }
    
        private boolean hasFocusedActivity() {
            for (Status status : activities.values())
                if (status.isFocused)
                    return true;
            return false;
        }
    
        public void onActivityCreate(Activity activity, boolean isStarting) {
            if (isStarting && activities.isEmpty())
                onApplicationStart();
            activities.put(activity, new Status());
        }
    
        public void onActivityStart(Activity activity) {
            if (!hasVisibleActivity() && !hasFocusedActivity())
                onApplicationForeground();
            activities.get(activity).isVisible = true;
        }
    
        public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
            activities.get(activity).isFocused = hasFocus;
        }
    
        public void onActivityStop(Activity activity, boolean isFinishing) {
            activities.get(activity).isVisible = false;
            if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
                onApplicationBackground();
        }
    
        public void onActivityDestroy(Activity activity, boolean isFinishing) {
            activities.remove(activity);
            if(isFinishing && activities.isEmpty())
                onApplicationStop();
        }
    
        private void onApplicationStart() {Log.i(null, "Start");}
        private void onApplicationBackground() {Log.i(null, "Background");}
        private void onApplicationForeground() {Log.i(null, "Foreground");}
        private void onApplicationStop() {Log.i(null, "Stop");}
    
    }
    

    public class MyActivity extends BaseActivity {...}
    

    public class BaseActivity extends Activity {
    
        private BaseApplication application;
    
        @Override
        protected void onCreate(Bundle state) {
            application = (BaseApplication) getApplication();
            application.onActivityCreate(this, state == null);
            super.onCreate(state);
        }
    
        @Override
        protected void onStart() {
            application.onActivityStart(this);
            super.onStart();
        }
    
        @Override
        public void onWindowFocusChanged(boolean hasFocus) {
            application.onActivityWindowFocusChanged(this, hasFocus);
            super.onWindowFocusChanged(hasFocus);
        }
    
        @Override
        protected void onStop() {
            application.onActivityStop(this, isFinishing());
            super.onStop();
        }
    
        @Override
        protected void onDestroy() {
            application.onActivityDestroy(this, isFinishing());
            super.onDestroy();
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题