简体   繁体   中英

Android App, Activity State (Running, Not Running, Foreground/ Background)

I have come across a requirement but I am not able to get the correct way of implementation and hence need your help.

What I want to do? - I want to perform an action depending upon notification I get as follows:

  1. When app is open and in foreground ie visible to user and I get notification I simply show a popup to start my Activity B
  2. When app is closed ie neither in background nor in foreground and I get the notification I will start my application first and then start Activity B
  3. When the app is running BUT in background ie in recents but not visible to user then I want to start my Activity B without re-starting the application. Also, in this case when user presses back on Activity B, they should see the screen they left off before sending it to background.

What I have done? I have achieved point #1 and #2. I want to achieve point #3. I have tried the below

public static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}

However this returns true in both the cases, point #2 and #3, so I am not able to distinguish only #3.

Also I tried the below in every Activity I have,

@Override
protected void onPause() {
    super.onPause();
    saveIsPausedInPref(true);
}

@Override
protected void onResume() {
    super.onResume();
    saveIsPausedInPref(false);
}   

But, it also doesn't give the desired result coz if the app is sent to background by pressing the Home button my Preference will have isPaused = true and if the user removes the app from recent then it will stay true and again I will not be able to differentiate Point #2 and #3 when the notification arrives.

Apologies for the whole story, but I hope I am able to explain my requirement.

Thanks in advance. :)

Edit:

        <activity
            android:name=".HomeActivity"
            android:screenOrientation="portrait" >
        </activity>
        <activity
            android:name=".ChatProfileActivity"
            android:screenOrientation="portrait" >
        </activity>

Below code works for me

In AndroidManifest.xml

<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:name=".MyApplication"
            >

MyApplication.java

public class MyApplication extends Application {
private ActivityLifecycleCallbacks myLifecycleHandler;

@Override
    public void onCreate() {
        super.onCreate();
        myLifecycleHandler=new MyLifecycleHandler();
        registerActivityLifecycleCallbacks(myLifecycleHandler);
    }
}

MyLifecycleHandler.java

public class MyLifecycleHandler implements Application.ActivityLifecycleCallbacks {
    private static final String TAG = MyLifecycleHandler.class.getSimpleName();

private static int resumed;
private static int paused;
private static int started;
private static int stopped;

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

@Override
public void onActivityDestroyed(Activity activity) {
}

@Override
public void onActivityResumed(Activity activity) {
    ++resumed;
}

@Override
public void onActivityPaused(Activity activity) {
    ++paused;
    Log.d(TAG, "application is in foreground: " + (resumed > paused));
}

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

@Override
public void onActivityStarted(Activity activity) {
    ++started;
}

@Override
public void onActivityStopped(Activity activity) {
    ++stopped;
    Log.d(TAG, "application is visible: " + (started > stopped));
}

public static boolean isApplicationVisible() {
    return started > stopped;
}

public static boolean isApplicationInForeground() {
    return resumed > paused;
}
}

Now using myLifecycleHandler methods you can get all states you need.

isApplicationInForeground means -> Atleast one activity is in visible state.

isApplicationVisible means -> Atleast one activity is there which got started but not stopped, means application is running state

if isApplicationInForeground is true isApplicationVisible will be always true, but vice versa is not true

To differentiate between case #2 and case #3, you can do the following:

Launch ActivityB if it is not case #1. In ActivityB.onCreate() do this:

super.onCreate(...);
if (isTaskRoot()) {
    // ActivityB has been started when the app is not running,
    //  start the app from the beginning
    Intent restartIntent = new Intent(this, MyRootActivity.class);
    startActivity(restartIntent);
    finish();
    return;
}
... rest of onCreate() code here...

I know that u figured out 1 & 2 but ill right my own toughts in case u did something else and u want to take a look.

  1. How to know whether your app is in foreground?

    this should be the correct way, if its in foreground then post an event via eventbus , eventbus is my preffered way.

  2. Start the app at a specific activity with stack? take a look at TaskStackBuilder with this answer , it will provide information how to this properly.
  3. Resume app with a certain activity while retaining the current stack? Check the state of the app with the callbacks, then when receiving the notification open the activity without FLAG_ACTIVITY_NEW_TASK. This should do the tricl.

If you change to not show any pop up, just to go to the Activity B always (destroyed, background or foreground), you don't need any of this check. Just to work with the flags FLAG_ACTIVITY_X on your notification (it's GCM this notification?).

LaunchMode on Manifest

Intent Flags

You can use the onNewIntent method to check the intent if comes from a notification and start Activity B.

Notification Proper BackStack

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM