android singleinstance activity not single if back button pressed - android

I encountered an interesting issue, where an Activity is created multiple times, even it is defined as a singleTask or a singelInstance Activity in the manifest. Here is how this can be reproduced. Say, in the main activity:
#Override
protected void onResume() {
Intent i = new Intent(MainActivity.class, SingleActivity.class);
startActivity(i);
}
in my SingleActivity, I have:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
Log.i("SingleActivity", "onCreate " + System.identityHashCode(this));
...
}
and in the manifest, I have:
<activity android:name=".SingleActivity"
android:launchMode="singleInstance"
/>
now, if I start the application, things seem OK, expect in one case: if I press the 'back' button while SingleActivity is in front, it navigates back to MainActivity, where MainActivity.onResume() will create another SingleActivity instance, instead of bringing forward the one that already exists. this is something I know because on the log, a different identity hash code is displayed.
the same seems to be true if the launch mode is singleTask.
the only workaround seems to be to override onBackPressed(), but that seems like an ugly solution.
I wonder what I'm doing wrong

This is a problem of taskAffinity. Because you haven't specified taskAffinity in the manifest on either your MainActivity or your SingleActivity, these 2 activities have the same (default) taskAffinity. When you start an activity, Android checks the taskAffinity of the activity that you want to start. If it is the same as the taskAffinity of the root activity in your task, then it will ignore launchMode="singleInstance" or launchMode="singleTask" (because those launch modes would require Android to create a new task to launch the activity in) and start the activity in the current task.
Unfortunately, this isn't well documented, but taskAffinity takes precedence over launchMode.
If you really want a singleTask or singleInstance activity (which is usually not the right thing to do because it brings with it a whole mess of other nasty things that you are likely to get wrong), then you need to make sure that your singleInstance or singleTask activity has the following in the manifest in its <activity> definition:
android:taskAffinity=""
If you need more information, search StackOverflow or Google for "launchmode taskaffinity"

By default, pressing the BACK key finishes (destroys) the current activity and displays the previous activity to the user.
So, this is impossible to
instead of bringing forward the one that already exists
because no activity exists.

Related

Using singleTask in launchMode

I added singleTask attribute to MainActivity(A) to avoid loading the activity multiple times. After the other activity(B) is on MainActivity(A->B), if I go back to home screen by pressing home button and re-launch the application, there is no B (A->B->HOME->A)
There IS an answer in here, saying to add FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP every time calling A Activity.
BUT, I have a Intent data scheme in Manifest file, and the application should be launched by the scheme.
Please, help me...
It sounds to me like Android is launching your MainActivity(A) again after you return to the HOME screen. This shouldn't happen, but is a long-standing nasty Android bug. See my answer to this question for more information.

How to prevent stacked activity from showing when app is re-launched?

I have an annoying issue with the activity stack that I haven't found a solution for.
Basically I have an activity that acts as a "starter" activity (the main activity in my manifest, this is started from the launcher etc). It is translucent, set using:
android:theme="#android:style/Theme.Translucent.NoTitleBar"
What it does is check the Intent that is fed to it. If the intent data is empty, it starts a new activity which is the main activity for the app.
If the intent data contains certain commands the starter activity should perform certain tasks and then exit, not even starting the main activity. So this should happen without any UI (except for a popup message when done).
My problem is that if the main activity has been started, if the user uses the home button to leave it, the next time the starter activity is started with a command, the main activity also shows up briefly.
I'm assuming this is because of the activity stack since I'm not restarting the main activity from the starter activity in this case.
I've tried various solutions to no avail. I can't use finish() in the main activity in onPause or onStop since that also exits the activity if the user for example enters the settings activity and that is not wanted behavior. I also tried variations of re-launching the starter activity with
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
but that doesn't seem to work either.
The thing that is weird is that when this unwanted behavior happens, onCreate/onResume/onStart is not called on the main activity. Still it shows! I'm guessing this is because it is stacked and since the startup activity is translucent, the main activity is shown through it.
Enable the android:noHistory attribute on your activity within your manifest:
<activity
...
android:noHistory="true">
...
</activity>
This will set the activity to be removed from the activity stack when it starts the next activity. The user will not be able to return to an activity that has android:noHistory="true".
See:
Activity Attribute: android:noHistory

launch external activity w/o it appearing in the back stack

I need to launch an external activity (which isn't mine) for a split second, and then I have a service that launches my previous activity for the user to see.
This works great, but the problem is when the user then presses the back key, he gets to that external activity.
I have two different activity launches here, one for the external activity, and one for my activity to return to (in which I need to use FLAG_ACTIVITY_NEW_TASK because I'm launching it from a service).
For these two launches I've tried any combination of:
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, FLAG_ACTIVITY_NO_HISTORY, FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_CLEAR_TOP, FLAG_ACTIVITY_CLEAR_TASK, FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS.
Nothing helps, I keep seeing the external activity after pressing back.
I've seen some similar questions here, but they all have the middle activity in their own app, so they can call finish on it, or set noHistory="true" in the manifest.
Any ideas?
UPDATE:
Seems like the issue is that I re-launch my previous activity from a service, which means I need to give it FLAG_ACTIVITY_NEW_TASK, from the documentation of Tasks and Back Stack:
FLAG_ACTIVITY_NEW_TASK Start the activity in a new task. If a task is
already running for the activity you are now starting, that task is
brought to the foreground with its last state restored and the
activity receives the new intent in onNewIntent(). This produces the
same behavior as the "singleTask" launchMode value, discussed in the
previous section.
And from the singleTask paragraph:
"singleTask" The system creates a new task and instantiates the activity at the root of the new task
...
Note: Although the activity starts in a
new task, the Back button still returns the user to the previous
activity.
So I need a flag that overrides this behavior somehow...

How to open already opened activity instead of creating new one?

I have two activities with "navigation menu" which has items for launching Activity1 and Activity2.
For example we starts Activity2 from Activity1 and then we want open Activity1 by tap on "navigation menu", but when we do this we get new instance of Activity1 instead of open еxisting instance.
How can i open instance of Activity1 if it already exists and create new instance if not?
Add FLAG_ACTIVITY_REORDER_TO_FRONT to your Intent you use with startActivity().
add android:launchMode="singleTop" to your activity in the Manifest.xml
<activity android:name=".myActivity" android:label="#string/app_name"
android:launchMode="singleTop" />
Check this out about different launchModes
also mind this:
As shown in the table above, standard is the default mode and is
appropriate for most types of activities. SingleTop is also a common
and useful launch mode for many types of activities. The other modes —
singleTask and singleInstance — are not appropriate for most
applications, since they result in an interaction model that is likely
to be unfamiliar to users and is very different from most other
applications
Set the flag of the activity to singleTask and override the onNewIntent(Intent intent) to catch the new intent.
The most complete answer would be to use android:launchMode="singleTask" and depending on your functionality, override onNewIntent since it will be called if there is already an instance of the Activity with the new Intent passed to it.
<activity
android:name=".MainActivity"
android:launchMode="singleTask"/>
Why?
Based on the question.
There are two Activities, Activity1 & Activity2
We open Activity1 and then from Activity1 we open Activity2. Then, inside Activity2:
How can i open instance of Activity1 if it already exists and create new instance if not?
As stated in AndroidManifestActivity_launchMode for singleTask
If, when starting the activity, there is already a task running that starts with this activity, then instead of starting a new instance the current task is brought to the front. The existing instance will receive a call to Activity.onNewIntent() with the...
Furthermore, under the intent class, if you read about the singleTask launchMode it already uses Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT and manually setting an intent to it is not normally set by application code.
As stated in FLAG_ACTIVITY_BROUGHT_TO_FRONT
int FLAG_ACTIVITY_BROUGHT_TO_FRONT
This flag is not normally set by application code, but set for you by the system as described in the launchMode documentation for the singleTask mode.
Therefore, by using singleTask launchMode you ensure that there is only one instance of your application and you do not have to be adding the FLAG_ACTIVITY_BROUGHT_TO_FRONT flag to your intents in every activity which calls your Activity2 as suggested by CommonsWare.
Now, if we use the android:launchMode="singleTop" as weakwire suggested, the link he provided himself clearly states;
"singleTop"...In other circumstances — for example, if an existing instance of the "singleTop" activity is in the target task, but not at the top of the stack, or if it's at the top of a stack, but not in the target task — a new instance would be created and pushed on the stack.
In other words, we would end up with multiple instances of Activity1 in some scenarios which is what we do not want.
As final say, if you are one of those who love editing answers that contribute nothing to the answer itself, go answer some real questions if you really want to increase your Stack Overflow Reputation.

How to start an activity from a singleInstance activity?

I have a widget which can pop up small dialogs when clicked. These dialogs are displayed by an activity called RemoteActivity in singleInstance launchMode. In one of these dialogs, there is a button to launch the main app MainActivity, which has the standard launchMode.
However, when this button is clicked, and startActivity() called, MainActivity isn't launched, although I can see the corresponding "Starting activity: Intent { ... }" in logcat.
If I set the launchMode of RemoteActivity to standard then MainActivity gets launched, but this isn't what I want, RemoteActivity is merely an extension of the widget, I don't want it to stack with any other activity.
I also tried with FLAG_ACTIVITY_NEW_TASK but it didn't help, and it shouldn't be necessary anyway according to the docs:
A "singleInstance" activity, on the
other hand, permits no other
activities to be part of its task.
It's the only activity in the task. If
it starts another activity, that
activity is assigned to a different
task — as if FLAG_ACTIVITY_NEW_TASK
was in the intent.
How can I launch my main activity?
UPDATE / ERRATA:
The MainActivity is actually launched but only if it isn't already part of a task. If I launch MainActivity normally through the launcher, and press Back to exit, then RemoteActivity does launch MainActivity.
But if, instead of pressing Back, I press Home to leave MainActivity, then RemoteActivity can't launch MainActivity, although the intent appears in logcat.
I'm testing this on Froyo.
Any idea of what's happening?
Maybe the noHistory flag will work for what you are looking for?
I found the problem: this behavior only occurs when calling finish() before startActivity() in RemoteActivity. If I call startActivity() before finish() then it works fine whether MainActivity is already part of an existing task or not.
Go figure...

Categories

Resources