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.
Related
I have two applications installed on my device, application A and application B.
When a user launches application A, they press a button to launch application B.
I use the following code:
Intent intent = Global.CurrentContext.getPackageManager().getLaunchIntentForPackage("some.random.app");
Global.CurrentContext.startActivity(intent);
The first time the user does this, it works fine and works as expected, however, if the user then launches the phones task manager and swipes away application B. When the user then presses the button in application A. The activity is not created.
I believe this is something to do with the way that the task and the activity are being destroyed by android. My thought process is that when the user swipes to kill application B that the activity is destroyed, but the task remains. Then the second time they press the button in application A, that it's attaching to the old task and then not showing for the user.
Application A's activity manifest for the activity:
<activity
android:name="a.a.a.LogonActivity"
android:label="#string/app_name"
android:noHistory="true"
android:screenOrientation="portrait"
android:theme="#android:style/Theme.Light.NoTitleBar"
android:windowSoftInputMode="stateHidden"/>
Application B's activity manifest for the activity:
<activity
android:name=".activity.HomeActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
When I set android:noHistory="false", it works. I think it is android:noHistory="true" killed applicationA's top activity, makes it doesn't work.
My application contains multiple activities, the main activity (A) is in launchMode singleTop, every over activities are in singleInstance mode.
I've choose singleInstance for preventing illimited navigation
if i navigate like this : A -> B -> C -> B -> C -> B -> C
The back action will do: C -> B -> A
It works as expected.
Problem : If i navigate A -> B then i open app recents screen, click on my application, activity b is displayed (ok) but if I go back the application exits and returns to android home (the activity is not destroyed, I can always open B since recent apps screen)
Why does task history not retain if I open the application from the recent applications menu?
<activity
android:name="A"
android:label="#string/app_name"
android:launchMode="singleTop"
android:configChanges="orientation|screenSize"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.default_searchable"
android:value=".SearchResultsActivity" />
</activity>
<activity
android:configChanges="orientation|screenSize"
android:name="B"
android:label="B"
android:launchMode="singleInstance"
android:theme="#style/AppTheme.NoActionBar">
</activity>
<activity
android:configChanges="orientation|screenSize"
android:name="C"
android:label="C"
android:launchMode="singleInstance"
android:theme="#style/AppTheme.NoActionBar">
</activity>
Thanks for your help
If a singleInstance Activity is called, a new task would be created. Through dumpsys, we could find two tasks, but only one task shows in Task Manager. This could be a system bug. And if Activity B set taskAffinity, there could be two tasks in Task Manager.
Since you start singleInstance B from A, there would be two tasks existed:
Task 1 contains Activity A, while task 2 contains Activity B.
You open the task 2 from the Task Manager which only contains a singleIntance Activity B, so you can not back to Activity A.
It works well when using back button, because
Regardless of whether an activity starts in a new task or in the same task as the activity that started it, the Back button always takes the user to the previous activity.
You can get this from Developer Document: Tasks and Back Stack.
I suggest you not to use singleInstace mode if it is really really necessary.
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
when I start activity with following code.
intent = new Intent(MainActivity.this, New1Activity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(intent, StaticValue.REQUEST_CODE_MENU_01);
after New1Activity activity showed up, open another package app such as Facebook app and then go back (by multitasking switch or back button) to my app.
In this scenario, I expected New1 activity will be given but MainActivity is shown.
In addition, AndroidManifest.xml is as below.
<activity
android:name="mypackage.MainActivity"
android:screenOrientation="landscape"
android:launchMode="singleTop"
android:noHistory="true"
android:label="#string/app_name"
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".New1Activity"
android:launchMode="singleTop"
android:noHistory="true"
android:screenOrientation="landscape"
/>
I noted noHistory and singleTop to each activity because of my app performance management.
How can I persist my activity status whenever I go back from other app?
Oh, my stupid.
I removed noHistory="true" and it works. (Let say RTFM. LOL)
below is quoted from android documentation,
android:noHistory
Whether or not the activity should be removed from the activity stack and finished (its finish() method called) when the user navigates away from it and it's no longer visible on screen — "true" if it should be finished, and "false" if not. The default value is "false".
A value of "true" means that the activity will not leave a historical trace. It will not remain in the activity stack for the task, so the user will not be able to return to it.
This attribute was introduced in API Level 3.
In my application there is a behavior that I don't understand. I have the MainActivity A as SingleTask.
It calls an Activity B that is SingleTask too.
When I press the Home button in the second activity to open another application, and after that I try to go to my application mantaining Home button pressed i always go to Main Activity, and I want second activity to be opened mantaining the state that had when i press Home button.
I've tried setting then second activity to singleTop and it doesn't work.
Any help?
The behaviour of activity back stack becomes quit weird when define main activity with singleTask at the same time:
<activity android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
What even worse is there is no clear explanation in the official dev guide regarding to this special use case. Some sections related to this topic are even self-contradictory.
Try using launchMode="standard" on your MainActivity A, and launchMode="singleTask" on your Activity B, which will give the expect behaviour you described.