Activity in launchMode singleTask gets destroyed when started via TaskStackBuilder - android

I have an activity A, declared in Manifest with android:launchMode="singleTask"
and another activity B that has no further attributes.
As far as I know, if there is already some task running activity A,
instead of creating a new instance, the current task is brought to front,
if I attempt to start activity A.
Now, I want to use the TaskStackBuilder to make a call which includes both activities.
Let's suppose I am on a Fragment within Activity A and call:
TaskStackBuilder.create(context)
.addNextIntent(new Intent(context, A.class))
.addNextIntent(new Intent(context, B.class))
.startActivities();
I assume the current stack to become: A > B with A getting a call to onNewIntent and B with a new instance. Furthermore I expect Activity A to hold it's state.
But what happens is: I do not get a call to onNewIntent, but to onDestroy for Activity A. And if I go back from my newly created Activity B, onCreate is called for a new instance of Activity A. (They are not equal!).
Can anybody explain to me, why my Activity A is thrown away instead of doing nothing to it, but adding B to the stack? It is a shame, that I can't get my head around this problem by myself, but I can't see the obstacle here.
Edit/Ps: The context which I use for the StackBuilder can either be a views context or a Service context.

So, as nobody answered so far, I dived into the code from android in the meanwhile and discovered that the StackBuilder itself is the issue.
In detail..this part of code, which is present in the methods startActivities() and also in getPendingIntent(), causes my Activity A to get destroyed:
public PendingIntent getPendingIntent(int requestCode, int flags, Bundle options) {
if (mIntents.isEmpty()) {
throw new IllegalStateException(
"No intents added to TaskStackBuilder; cannot getPendingIntent");
}
Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]);
intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
// Appropriate flags will be added by the call below.
return IMPL.getPendingIntent(mSourceContext, intents, requestCode, flags, options);
}
It is this part:
Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]);
intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
My Activity A was in an existing Task. Although it has the launchMode = "singleTask" and should get reordered to front normally, the FLAG_ACTIVITY_CLEAR_TASK is set to my first activity (which is A) and causes the existing task that would be associated with the A to be cleared before the activity is started.
That said, I won't use the StackBuilder in my special case now.
I see several approaches to this: either use an empty Activity before
my first target Activity or building the stack myself inside of
my Activity A's onCreate / onNewIntent, by providing a Bundle
with a List of parcelables (intents) to start.

Related

How to set no History for all Activities

My Android Application is based on four Activities. The last activity starts an Intent Service and set itself to background.
Before I sent the last Activity to background I would like to remove all four Activities from backstack history
I tried already to set the no history = true attribute for the activities. This causes errors when I use startActivityForResult, so I need a different solution
I don't really think there is an "good" way to do this, I would try something like this:
First create a SelfClosingActivity which only job is to close itself after opening:
public void onCreate(Bundle savedInstanceState()) {
super.onCreate(savedInstanceState);
finish();
}
When you want to close all the Activities (after you start your IntentService I guess) run the SelfClosingActivity and add flags to clear the current stack.
Intent intent = new Intent(this, SelfClosingActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
This will clear the current stack before opening SelfClosingActivity and SelfClosingActivity will close itself leaving the stack completely empty.
FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK will do what you want.
FLAG_ACTIVITY_CLEAR_TOP
Added in API level 1 int FLAG_ACTIVITY_CLEAR_TOP If set, and the
activity being launched is already running in the current task, then
instead of launching a new instance of that activity, all of the other
activities on top of it will be closed and this Intent will be
delivered to the (now on top) old activity as a new Intent.
For example, consider a task consisting of the activities: A, B, C, D.
If D calls startActivity() with an Intent that resolves to the
component of activity B, then C and D will be finished and B receive
the given Intent, resulting in the stack now being: A, B.
This launch mode can also be used to good effect in conjunction with
FLAG_ACTIVITY_NEW_TASK: if used to start the root activity of a task,
it will bring any currently running instance of that task to the
foreground, and then clear it to its root state. This is especially
useful, for example, when launching an activity from the notification
manager.
Refer to Tasks and Back Stack for more info

Android - Bring an activity to the front from application class

I am trying to bring an activity to the front. I found many questions similar to this but none of them actually works.
I always hold a reference to the current activity in a variable in application class. While the application is running in the background (after onPause fires), if any message arrives, I need to bring the same activity to the front and display the message. The only way I got it worked is..
Intent i = new Intent(mCurrentActivity, JoboffersActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
The issue I have got with this, is that it recreates the activity which I wanted to avoid. I tried singleInstance and singleTask both in the manifest. If I do not add FLAG_ACTIVITY_CLEAR_TOP, on back key press it takes me to the previous instance of the same activity. Even if I add sigleInstance it creates two instances of the same activity. If I do not add FLAG_ACTIVITY_NEW_TASK then it shows an error Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag
Try to add another flag FLAG_ACTIVITY_SINGLE_TOP to your intent.
According to Android documentation, combination of this 2 flags:
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
brings the current instance of Activity to the front.
http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP

Android: when coming from notification, activities are added on top of already open activities and memory increases

Android: when coming from notification, activities are added on top of already open activities and memory increases.
How to clear all the previous activities or even kill the app when notification is tapped?
Here is how I build my notifications:
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)//
.setContentTitle(notificationTitle)//
.setContentText(notificationText)//
.setSmallIcon(R.drawable.app_icon_transparent)//
.setAutoCancel(true)//
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.asd));//
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(context, StarterActivity.class);
resultIntent.putExtra("comingFromNotification", true);
// The stack builder object will contain an artificial back stack for
// the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(StarterActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify((int) now.getTimeInMillis(), mBuilder.build());
Now, when the user receives a notification and clicks it, StarterActivity is started. It initiates all the resources the app needs and then starts the main activity.
If the app had been running up to this moment and had been taking up 50 mb of ram, now ram goes up to 65, which means the previous process is not killed and this one starts on top of it.
Question is, how to kill the app if its running in the moment the user clicks the notification?
EDIT: In some similar question I found this
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_NEW_TASK);
will this help? What do these flags mean?
EDIT2: Nope. those flags didnt help. The application process that has some cached objects in memory still lives and the RAM goes up again.
Your architecture doesn't lend itself to solving this problem. As you mentioned in some comments, StarterActivity is your root activity (ie: the one with ACTION=MAIN and CATEGORY=LAUNCHER), but you don't keep it in the activity stack, so you cannot take advantage of FLAG_ACTIVITY_CLEAR_TOP.
To solve your problem you should not call finish() on StarterActivity when it launches MainActivity. This will allow you to use FLAG_ACTIVITY_CLEAR_TOP (with or without FLAG_ACTIVITY_SINGLE_TOP, depending on whether or not you want to use the existing instance of StarterActivity or create a new instance).
Once you've got that working, you now need to deal with the problem of the user pressing BACK in MainActivity and having it return to StarterActivity, which is obviously not what you want. You can solve that in several ways, here are 2 examples:
Add a boolean member variable to StarterActivity. In StarterActivity.onResume() set that variable to true at the very end of the method. At the start of onResume(), check the variable, and if it is true, you can assume that the user has pressed BACK in MainActivity, so you can just call finish().
In MainActivity override onBackPressed() and instead of calling super.onBackPressed(), call startActivity() with an Intent for StarterActivity with an extra named "exit" and FLAG_ACTIVITY_CLEAR_TOP. This will cause a new instance of StarterActivity to be created. In StarterActivity.onCreate(), check the presence of the extra "exit" in the Intent, and if it is there, it means that the user pressed BACK from MainActivity. In this case, you just want to call finish() to end your application. If you use this mechanism, make sure that StarterActivity has standard launchMode in the manifest, not launchMode="singleTop".
FLAG_ACTIVITY_CLEAR_TOP - Clears intermidiate activities, not every activity e.g:
You have call A > B > C > D >E , then from E you call B. After such call Activities C and D will be removed, activity A will remain.
If you have calls A > B > C > E and from E you call activity F, no activity will be purged.
FLAG_ACTIVITY_SINGLE_TOP ensures the top activity is not recreated if it is called again. eg:
you cal A > B > C , then you call B again. After such call B will not be recreated, but Current existent will be called. If you will call A, it will be recreated as it is not on top of stack.
Activity_new_task - is used for complecated back navigation. You can manage several tasks or in other words several histories of activity calls. Creating new task will not erase previos task. Is an absolute requirenment, when starting app with notification, deeplinks or any other "launcher" behaviour. Purifications inside one task will have no effect on another task history
I assume, a propper reset of history after launch will be achived with those 3 flags together: FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |FLAG_ACTIVITY_CLEAR_TASK
This shoul, clear task history, close it and create new task. If it does not work try to replace Clear_Task with FLAG_ACTIVITY_RESET_TASK_IF_NEEDED.
You don't need to kill the app and start it again. Use android:launchMode="singleTop" in your manifest for the activity:
<activity
android:name=".StarterActivity"
android:launchMode="singleTop"/>
This will open the same activity rather than creating a new one.

FLAG_ACTIVITY_CLEAR_TOP calls onCreate() instead of onResume()

So I have an abstract class extended through the entire app that overrides the back key to reorder Activity A to the front (With the flag).
So, it would be:
A > B > anywhere, and the back key should bring me back to A
I'm using the FLAG_ACTIVITY_CLEAR_TOP, but it is entirely refreshing A for some reason and I don't want that.
So: Flag_activity_clear_top is reloading the onCreate() rather than onResume(). What gives?
If you want the activity to just be brought to the top without restarting it set the launchMode of the activity to singleTop in the manifest. You will receive a call to onNewIntent when the activity is being brought to the top. onNewIntent is called before onResume. If you only want this behavior for the specific intent you can add the FLAG_ACTIVITY_SINGLE_TOP(in addition to FLAG_ACTIVITY_CLEAR_TOP) to the intent with the addFlags call instead of the manifest.
Intent intent = new Intent(CurrentActivity.this, ActivityNeedOnTop.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
CurrentActivity.this.finish();
From the API docs for FLAG_ACTIVITY_CLEAR_TOP
For example, consider a task consisting of the activities:
A, B, C, D. If D calls startActivity() with an Intent that
resolves to the component of activity B, then C and D
will be finished and B receive the given Intent,
resulting in the stack now being: A, B.
**The currently running instance of activity B in the above example
will either receive the new intent you are starting here in its
onNewIntent() method, or be itself finished and restarted with the new intent.**
So I think your activity is itself finished and restarted.

FLAG_ACTIVITY_CLEAR_TOP and android:launchMode="singleInstance"

I think I've just found a really strange bug... But it can just be somekind of feature that I never heard of.
On my application if I have any Activity on the AndroidManifest with android:launchMode="singleInstance" when you try to "clean" the stack to a certain point with the following code:
Intent intent = new Intent(this, Xpto.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
It goes to that activity. But when you press back, it returns to the previous.that should have been finished...
Example:
A -> B -> C
Then from C I call A with Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_CLEAR_TOP having A singleInstance on the Manifest. It goes to A but it only brings it to front. And does not finishes C and B.
Can somebody explain this behaviour?
The Xpto class I'm calling is at the time the root activity of the stack.
from reading this thread:
http://groups.google.com/group/android-developers/browse_thread/thread/5eb400434e2c35f4
it seems that:
"The currently running instance of activity B in the above example will
either receive the new intent you are starting here in its onNewIntent()
method, or be itself finished and restarted with the new intent. If it
has declared its launch mode to be "multiple" (the default) and you have
not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be
finished and re-created; for all other launch modes or if
FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to
the current instance's onNewIntent(). "
which means that you need to set your launchMode to multiple instance, and use only FLAG_ACTIVITY_CLEAR_TOP.
Intent intent = new Intent(this, Xpto.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
In the scenario you described, activity's B and C are not finished when you start activity A (which is the root activity). The documentation describes that with launch mode of singleInstance and the flag FLAG_ACTIVITY_SINGLE_TOP set, activities B and C will NOT be finished. If you want to have activities B and C finished, then you must set the launch mode to multiple instance and NOT set the flag FLAG_ACTIVITY_SINGLE_TOP.

Categories

Resources