Android activity not started if already in the backstack as the root?

Viewed 893

I have a sample app with 3 activities: Main, A and B. None of them has any launchMode.

Now I do this:

  1. open the app, Main activity is displayed
  2. leave the app using back button, so that there is no activity running
  3. receive a broadcast which starts a new activity A (new_task flag)
  4. open activity B by clicking on a button in activity A (no flags)
  5. receive a broadcast which starts a new activity A (new_task flag)
  6. new activity A is not started (if I go back, there's still the previous Activity A)

At step #6, activity A should be presented but is not.

If I try to present activity C instead (in #5), it is presented as expected.

If in #2 I leave the app by homebutton instead, everything works as expected.

How is this possible? And how can I ensure that the activity is always presented? I could use FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP, but I want to maintain the backstack.

UPDATE - SOURCE: You can look at the source, but I am using Xamarin (which is basically native Android written in C#, very similar to Java). This is the content of the built manifest (removed the Xamarin stuff):

<?xml version="1.0" encoding="utf-8" standalone="no"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" android:compileSdkVersion="28" android:compileSdkVersionCodename="9" package="com.companyname.StartTest" platformBuildVersionCode="28" platformBuildVersionName="9">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <application android:allowBackup="true" android:debuggable="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:name="android.app.Application" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity android:label="A" android:name="A"/>
        <activity android:label="B" android:name="B"/>
        <activity android:label="@string/app_name" android:name="MainActivity" android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <receiver android:name="Receiver"/>
    </application>
</manifest>
2 Answers

Alright lets look at this as per the sequence of operations.

  1. open the app, Main activity is displayed

    Status of backstack: Main Activity (root)

  2. leave the app using back button, so that there is no activity running

    Status of backstack: <No activity present as back was pressed>

  3. receive a broadcast which starts a new activity A (new_task flag)

    Status of backstack: ActivityA (root)

  4. open activity B by clicking on a button in activity A (no flags)

    Status of backstack: ActivityA (root) -> ActivityB (top)

  5. receive a broadcast which starts a new activity A (new_task flag)

    Status of backstack: ActivityA (root) -> ActivityB (top)

    Here Actvitity A is not launched because it was launched with the intent flag 'FLAG_ACTIVITY_NEW_TASK' which has the following documentation:

When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in.

  1. new activity A is not started (if I go back, there's still the previous Activity A)

At step #6, activity A should be presented but is not.

So the current task looks like this ActivityA (root) -> ActivityB(top), ActivityA is already present in the task and so the task as a whole is just brought to the front which means ActivityB is still at the top of the task.

If I try to present activity C instead (in #5), it is presented as expected.

ActivityC is a new activity that is not there in the task and so it is created for you.

If in #2 I leave the app by homebutton instead, everything works as expected.

The thing about homebutton press is that the MainActivity is not destroyed in this case and is just pushed to the background.

So after #2 your status of backstack would look still have the MainActivity instance as the root like :

Main Activity (root)

Now, before #5, backstack would look like this:

Main Activity (root) -> ActivityA -> ActivityB (top)

After #5, your backstack looks like this:

Main Activity (root) -> ActivityA -> ActivityB -> ActivityA (top)

I believe the reason for this seems to be the way MainActivity is launched by default with the intent flags :

FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

This also triggers a task reparenting and so it has control over new activities created with the new task flag (with the same affinity as the parent application) and how it can move such an activity to its task.

You can find more information on this here

And how can I ensure that the activity is always presented? I could use FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP, but I want to maintain the backstack.

Since this is a stack data structure you cannot reorder your activities, however you can fix this by adding the flag : FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK in the receiver that creates ActivityA as this will ensure a new instance/task is created for the activity irrespective of whether it was already present or not. (Not a recommended solution - but works)

But if you want to maintain the same instance of ActivityA then you will have to add a taskAffinity parameter to your activities in the manifest.

<activity android:name=".ActivityB" android:taskAffinity="com.example.pendingintent"></activity>
<activity android:name=".ActivityA" android:taskAffinity="com.example.abctest" />

and then start both your activities A and B with the new_task flag

Here is an excellent slideshow explaining taskAffinity and how it affects creation of activities.

Update Option 3: This should bring the Activity to top.

intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);

option1: set FLAG_ACTIVITY_REORDER_TO_FRONT when launching Activity A. This will bring Activity A to top of the stack.

activityAIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

Option2: Going through the Android documentation, only "Standard" launch mode creates multiple instances of the activity.

Refer below table.

This documentation explains usage of each mode: https://developer.android.com/guide/topics/manifest/activity-element.html#lmode

Add standard launchMode to your Activity A if you want its new instance every time.

android:launchMode="standard"

enter image description here

Related