3

My current flow is the following:

  1. The user enters the main activity A
  2. The user then launches activity B which is a single instance
  3. Activity B then starts a foreground service

Method that starts the foreground service

public static void startService(Activity activity) {
    Intent serviceIntent = SomeService.newIntent(activity);
    ContextCompat.startForegroundService(activity, serviceIntent);
}

Service onStartCommand

Notification notification = getNotification();

startForeground(1, notification);

return START_NOT_STICKY;

Manifest

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
...
<activity
    android:name="com.proj.activities.ActivityA"
    android:screenOrientation="portrait" />
...
<activity
    android:name="com.proj.activities.ServiceActivityB"
    android:launchMode="singleInstance"
    android:screenOrientation="portrait" />
...
<service
    android:name="com.proj.SomeService"
    android:foregroundServiceType="location" />

The following actions keep my single instance alive on Android 11 and previous version.

  1. If activity B is minimized, the foreground service stays alive.
  2. If the user starts activity A from activity B (singleInstance), the foreground service stays alive.

Activity A launches B:

Intent activityBIntent = new Intent(activity, ServiceActivityB.class);
activity.startActivity(activityBIntent);

Logs:

* Task{328c3ce #30946 visible=true type=standard mode=fullscreen translucent=false A=10480:com.company.name U=0 StackId=30946 sz=1}
    mLastOrientationSource=ActivityRecord{b2900c9 u0 com.company.name/com.proj.activities.ServiceActivityB t30946}
    bounds=[0,0][1080,2280]
    * ActivityRecord{b2900c9 u0 com.company.name/com.proj.activities.ServiceActivityB t30946}
* Task{de6d50d #30945 visible=true type=standard mode=fullscreen translucent=false A=10480:com.company.name U=0 StackId=30945 sz=2}
    mLastOrientationSource=ActivityRecord{58892bb u0 com.company.name/com.proj.activities.SpringboardActivity 30945}
    bounds=[0,0][1080,2280]
    * ActivityRecord{58892bb u0 com.company.name/com.proj.activities.ActivityA t30945}
    * ActivityRecord{183c336 u0 com.company.name/com.proj.activities.SpringboardActivity t30945}

Activiy B launches A:

Intent activityAIntent = new Intent(activity, ActivityA.class);
activityAIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
activity.startActivity(activityAIntent);

Logs:

* Task{de6d50d #30945 visible=true type=standard mode=fullscreen translucent=false A=10480:com.company.name U=0 StackId=30945 sz=2}
  mLastOrientationSource=ActivityRecord{58892bb u0 com.company.name/com.proj.activities.SpringboardActivity t30945}
  bounds=[0,0][1080,2280]
    * ActivityRecord{58892bb u0 com.company.name/com.proj.activities.ActivityA t30945}
    * ActivityRecord{183c336 u0 com.company.name/com.proj.activities.SpringboardActivity t30945}
* Task{328c3ce #30946 visible=true type=standard mode=fullscreen translucent=false A=10480:com.company.name U=0 StackId=30946 sz=1}
mLastOrientationSource=ActivityRecord{b2900c9 u0 com.company.name/com.proj.activities.ServiceActivityB t30946}
bounds=[0,0][1080,2280]
    * ActivityRecord{b2900c9 u0 com.company.name/com.proj.activities.ServiceActivityB t30946

But in Android 11, unlike previous Android versions, if the user starts activity A from activity B(singleInstance), then minimizes the app, OnDestroy() is called on activity B which holds a running Service.

  • "if the user navigates back to activity A, then minimizes the app, OnDestroy() is called on activity B" -- that is standard behavior for back navigation (destroys the activity that had been in the foreground and returns control to the previous activity on the back stack). It has been that way since Android 1.0. "activity B which holds a running Service" -- `startForegroundService()` does not tie a service to a particular activity, nor does `startForegroundService()` prevent back navigation from working. – CommonsWare Dec 17 '20 at 00:03
  • @CommonsWare I made an edit to my post to clarify that I am not using back navigation as such, but I am starting a new activity back to my original one. Another thing I want to clarify is that activity B is a single instance, so it belongs to a separate task from activity A. – Sergio Adrian Nunez Dec 17 '20 at 19:46
  • @Adrian have you tried starting the service as a bound service from Activity B, then moving it to the foreground once Activity B is destroyed? – ginga_ninja217 Dec 17 '20 at 20:06
  • Please edit your question and post the contents of your manifest. At least the declarations for the 2 activities and the service. – David Wasser Dec 22 '20 at 13:02
  • How does `ActivityA` launch `ActivityB` and vice-versa? – David Wasser Dec 22 '20 at 13:07
  • @DavidWasser I edited my post with your suggestions, thanks! – Sergio Adrian Nunez Jan 03 '21 at 02:09
  • There are a number of issues here are certainly confusing things. You have declared `ActivityB` with `launchMode=:"singleInstance"` but you have not specified `taskAffinity`. This means that when `ActivityA` launches `ActivityB`, the launch mode is ignored and `ActivityB` is launched into the same task as `ActivityA`. This may or may not be what you intended. You can check this by launching `ActivityB` from `ActivityA` and then using `adb shell dumpsys activity activities` and you should see only 1 task for your app (not 2). – David Wasser Jan 03 '21 at 11:40
  • So you now have a task with `ActivityA` as root and `ActivityB` on top of that. When `ActivityB` now launches `ActivityA` using flags `CLEAR_TOP` and `SINGLE_TOP`, `ActivityB` is removed from the task (finished) and the user goes back to the original instance of `ActivityA`. You should also be able to see this using `adb shell dumpsys activity activities`. Because `ActivityB` is finished, at some point `onDestroy()` will be called on it. This should also be the behaviour previous to Android 11. – David Wasser Jan 03 '21 at 11:44
  • To help you, please check your code on both Android 11 and older Android and using the `adb shell` commands determine if you have 1 task or 2. I do not see how you could have 2 tasks, as `taskAffinity` overrides `launchMode` and both activities have the same (default) `taskAffinity`. Please let us know your findings (you can edit your question and add the relevant info) and then I can make other suggestions. – David Wasser Jan 03 '21 at 11:46
  • It does look like there are some changes in Android 11 to the behaviour of `taskAffinity`. I haven't checked this myself, but I already answered this question which is a situation similar to yours: https://stackoverflow.com/questions/64769530/not-working-corss-application-activities-with-taskaffinity-in-android-11 I need to do some more research myself to really understand what has changed. – David Wasser Jan 03 '21 at 11:51
  • @DavidWasser I updated my question to show the Android 11 logs. I checked the activities and there are two tasks been created with the same taskAffinity. I also found that on Android 11, when you have more than one task with the same affinity, after I minimize my app the least recently used task is destroyed, that's the behavior that's causing my issue. – Sergio Adrian Nunez Jan 07 '21 at 01:44
  • Please post the manifest entry for `SpringBoardActivity`, which is the root `Activity` of the task. – David Wasser Jan 07 '21 at 08:13
  • Have you checked the behaviour on pre-Android 11? What do the adb logs look like on pre-Android 11? – David Wasser Jan 07 '21 at 08:13
  • @ginga_ninja217 Re-architecting my feature and making my service a bound service solved my issue. – Sergio Adrian Nunez Jan 13 '21 at 01:17

1 Answers1

2

My original goal was to obtain location updates in Activity B while our users could still use the app (Open other activities). Keeping Activity B alive was important because it had a WebView that couldn't be reloaded otherwise the user would lose their progress.

I was able to achieve this with the single instance flag on activity B, and probably not the correct way of doing this since it’s clear that Google changed the way of managing tasks in the new Android 11 update.

Adding a different taskAffinity to Activity B would’ve easily solved this issue of my task being killed, but it created two windows in the recent apps and that caused other issues if the user killed the window with Activity A.

I re-architectured my solution to make my Service a bound service that could also go to the foreground, and it would contain all the necessary data that my UI would need when re-entering Activity B. https://developer.android.com/guide/components/bound-services