launchMode="singleTask" does not create a new task - android

I have 2 activities: Activity A and Activity B. Activity A's launch mode is standard, and Activity B's launch mode is singleTask.
<activity
android:name=".AActivity"
android:label="#string/app_name"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="dd"></data>
<data android:host="a"></data>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
<activity
android:name=".BActivity"
android:label="#string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="dd"></data>
<data android:host="b"></data>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
I launch the app, and the launcher Activity A starts. Then i press Home Button and return to home screen of the phone. Then i launch browser app and type the following:
dd://b
to open Activity B. The system navigates to my App and starts activity B on top of activity A. At this point if i press back button, activity B is popped and i see activity A.
This is not what i expected because the android documentation states:
For singleTask activities, the system creates a new task and instantiates the activity at the root of the new task. However, if an instance of the activity already exists in a separate task, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Only one instance of the activity can exist at a time.
What i understand from these sentences is, in my case since an instance of Activity B does not exist, a new task should have been launched and it should have had only Activity B at its stack(Another instance of my app should still exist in a separate task and it should have Activity A in its stack). Then if i press back while i was at Activity B, since it is the only activity in the backstack, it is popped out and the system returns to the browser.
Why this is not the case? How does android system know that my app is open and navigates to it and starts activity B on top of existing app stack instead of launching another instance of my app and letting two instances of my app have their own stacks? What does it mean to instantiate activity in a new task? Can anyone explain?
Thanks.

In addition to the accepted answer, I found an explanation to this problem. As android documentation states:
The affinity indicates which task an activity prefers to belong to. By default, all the activities from the same application have an affinity for each other. So, by default, all activities in the same application prefer to be in the same task which belongs to that app(The app that has the Activity B). However, you can modify the default affinity for an activity.
So, if your app is running, and you start an activity, which has launchMode:singleTask attribute, unless you explicitly state taskAffinity attribute, it starts the activity in the same task. But if you explicitly state an affinity in your manifest:
<activity
android:name=".BActivity"
android:label="#string/app_name"
android:taskAffinity=""
android:launchMode="singleTask">
</activity>
then it starts the activity in a new task.
You can see which activity belongs to which task by the following adb command:
adb shell dumpsys activity
and also, to better understand launchMode concept, you can see the following app in the Google Play: https://play.google.com/store/apps/details?id=com.novoda.demos.activitylaunchmode

You said that your app navigate to B in passing by A.
So, i think you have two stack. In the first, you have only A, in the second you have only B. If you press back, then B is popped and disapeared but A exists and is showing because A in behind B even if A and B are in separate stack.
If you want that B pop and then you return to home, try to call "finish()" after call B from A

Related

SingleTask activity is created multiple times within the same process

I have a launcher app which has a singleTask activity as the main entry point. When the user navigates away to another activity or to a 3rd party app and then hits the home button, then this activity should be brought to the front. However what I experience is that for the first home button press only, another instance is created instead (a new task is created, onCreate() is called). In the meantime the old task is still alive, containing the original instance of this activity, but it is impossible to navigate back to that task/activity or to bring it to the foreground.
After the first home button press, the next home button press brings the 2nd instance of this activity to the foreground. Not sure why not the very first instance's onNewIntent() method is called for the first time... So this only happens once, after that always the 2nd instance's onNewIntent() method is called. This means that the original activity will be not accessible..
I tried to bring the task to the foreground, nothing was happening... Like if it never existed (but the task is there with the activity, it is not killed at any point). I can find the task from code and also using a shell script. It contains the original activity
This is happening on Android TV (Os: Pie). Any idea what can be the reason for this? I do not really understand how this is happening... BTW the result is the same if I set the activity to singleInstance.
The activity looks like this:
<activity
android:name=".activities.MainActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:theme="#style/AppTheme">
<intent-filter android:priority="2">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.ALL_APPS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
I tried alternating the above attributes (priority, excludeFromRecents, taskAffinity), also removed them completely, they had no effect...
The home button press sends the following intent:
action: "android.intent.action.MAIN"
category:
"android.intent.category.HOME"
component: the above activity
it has also some extras but I do not think it is relevant
You are experiencing this nasty long-standing Android bug:
Re-launch of Activity on Home button, but...only the first time
You say in a comment that it doesn't happen with real users. That is not actually true. If a real user would install your app from the Play store and then launch it immediately (click the OPEN APP button after installation) then the problem would be reproduced exactly as you describe it.
I was struggling with the exactly same issue for the last couple of hours and I've read many similar Q&A about this topic/bug on StackOverflow. No solution really worked for me until, out of mere curiosity, I did the following.
If you're building a launcher and using onNewIntent() in your MainActivity (although OP doesn't mention that he's using this method), then simply comment out this line:
super.onNewIntent(intent);
Uninstall your app and install it again.
I don't know how this works, but EVEN when you then uncomment the very same line, it still behaves in a proper manner, that is: the app has really only single instance of its main activity all the time. And how I know that? My MainActivity is doing some database operations, and previously I saw in my Logcat that these operations were done twice every time, but now they are done only once.
And BTW my MainActivity tag in AndroidManifest.xml is nothing special:
<activity
android:name=".activities.MainActivity"
android:exported="true"
android:excludeFromRecents="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

Launcher development - Home button not going back to initial activity

I have an app that acts as a Launcher. This app has 3 activities:
SplashActivity: shows a splash screen while loading, then launches LauncherActivity and finishes. This is the Activity marked as launcher in the manifest.
startActivity(Intent(this, LauncherActivity::class.java))
finish()
<activity
android:name=".SplashActivity"
android:label="#string/app_name"
android:screenOrientation="landscape"
android:theme="#style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
LauncherActivity: main activity for Launcher. Has a menu button that launches DashboardActivity.
startActivity(Intent(this#LauncherActivity, DashboardActivity::class.java))
<activity
android:name=".LauncherActivity"
android:launchMode="singleTask"
android:screenOrientation="landscape" />
DashboardActivity: shows a list of apps and launches them through their launch intent.
private val DEFAULT_FLAGS_APP_LAUNCH = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(packageManager.getLaunchIntentForPackage(packageInfo.packageName).apply {
flags = DEFAULT_FLAGS_APP_LAUNCH
})
<activity
android:label="#string/apps"
android:theme="#style/TNA"
android:name=".DashboardActivity"
android:launchMode="singleTask"
android:screenOrientation="landscape" />
All activities are launched through startActivity, including the apps.
I want the standard Android Launcher behavior, that is: when entering an app through DashboardActivity, if I click home button, go to the main Launcher activity (LauncherActivity), and when clicking back, go to the dashboard (DashboardActivity).
The problem I have is that when clicking home, it goes back to DashboardActivity, not to LauncherActivity. If I finish DashboardActivity, then when clicking back on an app, it goes back to LauncherActivity.
Any ideas on how to solve this?
This is definitely back/task stack related. See this link for more information about the task stack.
When you go from LauncherActivity to DashboardActivity, the dashboard is placed on to the task stack. When the LauncherActivity is requested again via the HOME button, the task stack is restored back to the last Activity which was in use after launching the LauncherActivity, which was DashboardActivity.
You have several different options to resolve this:
Don't use a separate Activity for the "dashboard". Consider a drawer or even a Fragment which shows the content and can be popped back to the main LauncherActivity when it is done calling startActivity to launch another app.
After your DashboardActivity calls startActivity, it should call finish() so it will get popped off the current task stack.
Usually launchers are setup to be launched in singleInstance mode, preventing multiple instances of the launcher Activity to run at the same time. Note that you'll need to support onNewIntent in your LauncherActivity.
To prevent odd interactions with the task manager, consider setting FLAG_ACTIVITY_NO_HISTORY when launcher your DashboardActivity.

Start Activity with NFC in a specific task

My app contains a number of activities. One of these activities responds to an NFC intent filter, as well as standard intents, however, this activity is launching in it's own task, and not in the same task as the app. The app is not necessarily running when the NFC intent is initiated but if it is, I want the activity to launch in the same task to ensure a seamless user experience. At the moment, the app is behaving as though there are 2 of them running.
Here is the manifest for my NFC activity:
<activity
android:name="name.subname.app.activity.ItemSummaryActivity"
android:label="#string/title_activity_item_summary" >
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<data android:mimeType="application/vnd.name.nfcdemo" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Is it possible to launch the activity in the existing task, if it exists?
I see two options here:
1) Add android:launchMode="singleTask" to activity tag in the manifest:
<activity
android:name="name.subname.app.activity.ItemSummaryActivity"
android:label="#string/title_activity_item_summary"
android:launchMode="singleTask" >
"singleTask":
The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.
2) Supply Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT flag to startActivity() intent. But, if the activity is triggered by NFC (and using this option is not feasible) consider what #NFC guy has to say here.

How to make multiple intent filters start an activity in the same task

I have two intent filters for the root activity of my application
<activity
android:name=".MyActivity"
android:label="#string/app_name"
android:theme="#android:style/Theme.Dialog" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<data android:mimeType="application/com.example.package" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
One filter is for launching the application from the launcher icon, the other is to launch the activity when the user touches an NFC tag.
I want that when user presses the HOME button from any activity in the application, the next time he presses the launcher icon or touches NFC tag, the application must resume (as it is the case with every application).
But this is what is happening:
When user taps the NFC tag, the first activity is launched. Then he navigates onto further activities. Presses HOME. Taps the NFC tag again, the first activity (MyActivity) is launched, and the previous stack is cleared.
On the other hand,
When the user selects the launcher icon, navigates onto further activities, presses HOME, and then presses the the launcher icon again, MyActivity is started as a new activity on top of the stack. (now there are two MyActivities in the stack).
You need to define the way your activity launches.
If you want your activity to always launch on the same task use: android:alwaysRetainTaskState="true" and android:taskAffinity="your.task.name"
Also take a look on android:launchMode="singleTop" and android:launchMode="singleTask" for further control on how the activity is launched.

Android can i avoid onCreate() being called?

I have two applications App-1 & App-2. App-2 has a button which will start App-1.
The need is to behave like the following:-
User launches App-1 (using launcher) & activities A, B & C are started & activity C is at the top of the activity stack.
Please note that entry point of App-1 is activity A.
User presses home key.
User then launches the application App-2. User chooses the button in App-2 to start App-1.
onClick() of the button in App-2 has the following code:-
Intent i = new Intent();
i.setAction("com.x.y.z"); //resolves to activity A of App-1
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("x", "y");
startActivity(i);
After step-4, onCreate() of activity A is called which is quite normal. But I want Android to bring the entire Activity Stack to be brought to the foreground, since App-1 is running & Android hasn't killed it.(Which is the same behavior if I had launched App-1 after step-2).
I want activity C to be shown to the user.
Kindly help me if it is possible to do this.
I have tried making activity A as singleTask & singleInstance. if i do that, only activity A is brought to the foreground which is not what i want.
the snippet of App-1's manifest looks like below:-
<activity android:name=".aa.a"
android:configChanges="orientation|keyboardHidden|locale"
android:label="#string/app_name"
android:theme="#android:style/Theme.NoDisplay"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="com.x.y.z" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
FLAG_ACTIVITY_NEW_TASK informs Android to start a new task; the new task doesn't contain B & C, so don't expect any of them to be shown.
Also, FLAG_ACTIVITY_CLEAR_TOP removes B & C, which is exactly opposite to what you want.
Try removing both flags.

Categories

Resources