onActivityResult do not fire if launch mode of activity is singleInstance - android

I have an Activity which is basically my main activity and its launch mode is single instance. But because of singleInstance, the onActivityResult() callback does not fire. And if I change the launch mode in my manifest file to any other mode it works fine.
Can you explain why this callback is not working?

I believe that the problem is that singleInstance doesn't let the callee activity to exist in the same task as the caller, hence it can't return the value to the caller.
Consider using singleTask instead:
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.
singleInstance
Same as "singleTask", except that the
system doesn't launch any other
activities into the task holding the
instance. The activity is always the
single and only member of its task.
http://developer.android.com/guide/topics/manifest/activity-element.html

If an activity is singleInstance, it will be the only one in the task so it always be the top of the task. So when you call startActivityForResult it will fire the callback method immediately.

A "singleInstance" activity, 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. The activity is always the single and only member of its task.
I think onActivityResult will not work with singleInstance

You can't use singleInstance or singleTask with startActivityForResult method.
Standard mode or singleTop launch mode will fix the problem.

Android Source Code
Check "ActivityStarter.computeLaunchingTaskFlags()" method:
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
// The original activity who is starting us is running as a single
// instance... this new activity it is starting must go on its
// own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
That's the reason why NEW_TASK flag is added when your original activity with single instance launch mode.
More Source Code
Check "ActivityStarter.sendNewTaskResultRequestIfNeeded()" method:
if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
// For whatever reason this activity is being launched into a new task...
// yet the caller has requested a result back. Well, that is pretty messed up,
// so instead immediately send back a cancel and let the new task continue launched
// as normal without a dependency on its originator.
Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
null /* data */);
That's the reason why FLAG_ACTIVITY_NEW_TASK always immediately return RESULT_CANCELED.

An example of my application can benefit someone:
In the manifest file, my settings are:
<application android:launchMode="singleTask"...>
I use one java class, only as a launcher(it does not show Activity). In the manifest, it is set to:
<activity android:launchMode="singleTop" ...>
Also, other Activities are set the same:
<activity android:launchMode="singleTop" ...>
When a user sends an application to the background, by clicking on the application icon or restoring the application from the background, the last started Activity is returned unchanged, even though they were all started with startActivityForResult() from launcher.

Related

LaunchMode "singleTop" is ignored when activity is started with startActivityForResult

I launch an activity with startActivityForResult() and want to prevent multiple instances from being started at the top of activity stack. So I expect android:launchMode="singleTop" to do its work, but for some reason the flag gets ignored.
After some investigations I managed to launch only one instance by adding FLAG_ACTIVITY_REORDER_TO_FRONT to intent, but I would be grateful if someone could explain me the reason why "singleTop" doesn't work in such case. The code is very simple.
// Activity class
Intent intent = new Intent(this, DetailsActivity.class);
// multiple instances can be launched without this line
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityForResult(intent, REQUEST_CODE_DETAILS);
// AndroidManifest.xml
<activity
android:name=".DetailsActivity"
android:launchMode="singleTop"/>
How SingleTop work?
let suppose you have current activity stack like
A->B->C
Now from current activity C, if you start A activity which is a singleTop, so in this case system will create a new instance of A and brings that instance to top. (If specified activity is not on top then new instance will be created)
So stack will look like
A->B->C->A
Now if you try to open A again then in this case as A on top already, so NO new instance will be created. Instead A will receive callback in onNewIntent() method
Flag
FLAG_ACTIVITY_REORDER_TO_FRONT
it scans from the front of stack to back of stack and if it found instance of specified activity then brings that activity to front.
So in your case if DetailsActivity instacne is already present in system then this flag will bring DetailsActivity to front
The flag FLAG_ACTIVITY_SINGLE_TOP must be ignored in this case because when you launch an Activity using startActivityForResult() you are basically saying that you want to launch an Activity and have the Activity return you a result.
If an instance of this Activity already exists in the stack (regardless of where it is in the stack), that instance has not been setup to return a result to your Activity. It is possible that that instance was launched using startActivity() in which case it isn't setup to return a result at all. It could be that it was launched using startActivityForResult() from another Activity, in which case it is set up to return a result to the Activity that launched it, not to your Activity.
These 2 things: FLAG_ACTIVITY_SINGLE_TOP and startActivityForResult() therefore are in conflict.

How to launch activity from notification such that it can't be navigated back to?

I need to launch an activity when a notification is clicked.
The Activity launched is a standalone activity and can not be opened from any other flow from the app itself. I need to ensure that no one can navigate back to this activity once it is destroyed.
Currently i am using the following configuration:
<activity android:name=".Activities.SingleRestaurantOfferActivity"
android:taskAffinity=".Activities.SingleRestaurantOfferActivity"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
android:resizeableActivity="false"/>
And I am launching this activity with the intents
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK
Now when the acitivity is created (first notification click), it creates a new task and as per my understanding, if another notification is clicked, since the task will be existing, it will clear the task and re launch the new activity with the new intent. Is my understanding correct?
If the app is open. Then the user clicks on the notification and launches the activity. Now the home button is pressed. What will happen to the notification activity? The recents screen will only show the actual app task and not the notification activity task, will the notification activity be destroyed eventually or will it leak memory?
Please guide me as to how i should approach this. The official android guide also uses launchMode:singleTask, do i need to use that as well?
I am answering my own question here so that anyone who faces a similar issue gets an idea as to what they can do.
Steps to follow are:
1) In the manifest of the required activity, add the following:
android:taskAffinity = "com.yourpackage.youractivity"
This ensures that this activity has a seperate task affinity as compared to the default affinity. This will come into picture when excludeFromRecents is added.
android:excludeFromRecents = "true"
This flag tells android that the task associated with the given activity should not be shown in recents screen. If we had not added the taskAffinity explicitly, this would have meant that the whole application would not show in the recents screen which would be irritating. adding the task affinity will only hide the task of the required activity from recents
android:noHistory = "true"
I am not sure if this is necessarily needed. I added it to ensure that as soon as the user navigates away from the activity in any way ... eg home button, it will kill the activity using onDestroy. Also exclude from recents will prevent it from showing in the recents screen.
2) Now comes the part of intent flags to launch the activity:
I have used the flags:
FLAG_ACTIVITY_NEW_TASK :
This flag along with the taskAffinity will create a new task for the activity if the task is not already created with the same affinity. Then it will place the activity at the root of this new task. If the task already exists, it will be brought to front and the new intent will be delivered to the activity in onNewIntent() method. I wanted the activity to get fully recreated so i added the other flag below.
FLAG_ACTIVITY_CLEAR_TASK:
This flag clears the task and kills all the activities in it. Then it adds the new intended activity at the root of the task. This will ensure in my case that the activity gets fully destroyed and recreated from scratch. You might not need this in your case. CLEAR_TOP with standard launch mode will also almost do the same thing but that's a whole different scenario.
Hope this helps people creating standalone activities which don't merge directly into app flows.

First activity is called after coming in foreground from background

In my application, I am finishing my first activity when I move to the next activity like this:
if(className.equals("com.tritonhk.android.LoginActivity"))
startActivityForResult(in, 1);
//loadingrelative.setVisibility(View.GONE);
displayVal = 0;
Helper.IsFullSync = false;
LoginActivity.this.finish();
So that when I go in background from any other activity and come back in foreground then that activity must be called by which we went in background.
It is happening in some cases but If I remain in background for more then 10 minutes then my first activity's oncreate method is called.
It seems that dalvik is killing my application process that is why when I click on my application icon then its new instance is created and hence its onCreate is called.
Please suggest me what should be the better approach for this.
EDIT Problem solved partially. Now with android:launchMode = "standard" behaves normally but not in first attempt. I mean when I install the app and run it and went to background and come back to foreground then it does not work but from the second time it works properly.
Since you are finishing your current activity which launches subactivity means you don't want the task to retain root activity on re launch ,
you want to start from where you left ,you may use android:alwaysRetainTaskState which will retain state of task since it is useful for root activity only so you have to start your subactivity in a new task using FLAG_ACTIVITY_NEW_TASK intentfilter flag.
Do u have android:launchMode="standard" for the activity which is called while launching the app? If not add this line.

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.

Categories

Resources