I have one ActivityA that contains a specific ID in the intent data, one ActivityB that is open from this ActivityA, and ActivityB can also open the same ActivityA with the same ID or a different one. Seeing that opening activities this way can lead to an infinity activities in the stack, I need to manage the stack in the following way:
ActivityA(id: 1) → ActivityB
ActivityA(id: 1)
. Note the ActivityB was just
popped from the stack.I know I can achieve this behaviour simply using android:launchMode="singleTask"
. But my problem is when the user opens the ActivityA with a different ID. So here is the desired second behaviour:
ActivityA(id: 1) → ActivityB
ActivityA(id: 1) → ActivityB → ActivityA(id: 2)
. Note that instead of popping out ActivityB, it keeps in the stack and add another instance of ActivityA but now with a different ID.Giving these two scenarios, I want two different behaviors depending on the ID from the ActivityA. And one limitation that I have is that I got no control of ActivityB, so I can't do any changes in the ActivityB.
PS: ActivityB opens ActivityA via deeplink.
I could achieve similar behaviour using
android:documentLaunchMode="intoExisting"
android:launchMode="singleTask"
but it creates a new task every time the ActivityA is open and it would be a bad user experince.
Using taskAffinity
will also create a new task and I can't afford that.
My current attempt is:
AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<activity
android:name=".ActivityA"
android:exported="false"
android:theme="@style/Theme.Light.NoActionBar"
android:windowSoftInputMode="stateAlwaysHidden"
tools:ignore="AppLinkUrlError">
<intent-filter>
<data android:scheme="myscheme" />
<data android:host="myhost" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
ActivityA code
private val idsInStack = mutableSetOf<String>()
class ActivityA : AppCompatActivity(R.layout.activity_a) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val id = intent.data?.getQueryParameter("QUERY_ID")
if (idsInStack.contains(id)) {
val intent = Intent(this, ActivityA::class.java)
intent.data = this.intent.data
intent.addFlags(
Intent.FLAG_ACTIVITY_CLEAR_TOP or
Intent.FLAG_ACTIVITY_SINGLE_TOP or
Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP
)
startActivity(intent)
finish()
}
id?.let { idsInStack.add(it) }
diInjection()
}
}
PS: ActivityB
launches ActivityA
via deeplink and add no flags to the intent.
You can probably accomplish want you want like this:
When ActivityA
is launched, in its onCreate()
it can store the ID into a static
variable, that is available to all instances of ActivityA
(in Kotlin you would use a property in a companion object
)
When ActivityB
launches ActivityA
, it will always create a new instance of ActivityA
.
When ActivityA
is launched, the first thing it does in onCreate()
(after calling super.onCreate()
is to check if the id
it was launched with is the same as the id
stored in the static
variable. If it is, it should remove all activities from the stack that are on top of the original instance of ActivityA
(including itself), like this:
Intent intent = new Intent(this, ActivityA.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP |
Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
startActivity(intent);
finish();
Of course, because the static
variable will be there forever, you'll need to think about cleaning it up so that it doesn't break further executions of your app.
Try this out and see if it works for you.
NOTE: I don't know what should happen if ActivityA
is first launched with id=5, then it launches ActivityB
, which then launches ActivityA
with id=3, which then launches ActivityB
, which then launches ActivityA
. Is this a real scenario? What should happen in this case if ActivityB
launches ActivityA
with id = 3? What should happen in this case if ActivityB
launches ActivityA
with id = 5?