Starting Activity from Service Context - android

I want to start an activity from service context. But I am bound to use flag = Intent.FLAG_ACTIVITY_NEW_TASK which is creating multiple instance of the activity since it is throwing Run-Time-Exception with other flags.
How can we launch an activity from service so that multi instances of activity will not be created, if already activity in recent tasks it will launch that only??

You can combine different flags, like 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.
...
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.
and FLAG_ACTIVITY_CLEAR_TASK
If set in an Intent passed to Context.startActivity(), this flag will cause any existing task that would be associated with the activity to be cleared before the activity is started. That is, the activity becomes the new root of an otherwise empty task, and any old activities are finished. This can only be used in conjunction with FLAG_ACTIVITY_NEW_TASK.
with FLAG_ACTIVITY_NEW_TASK. Which one to use depends on what exactly do you want to achieve.
Check the documentation of the flags in the Intent class and Tasks and Back Stack.

Related

finish all activities, start a new one as single instance

I want to finish all activities in my app and start a new single instance activity.
If someone attempts to start that same activity again, then it should stay as top and only onNewIntent() called. I don't want it to be recreated.
How can I do that?
I have tried a combination of different flags for launching but it didn't work.
FLAG_ACTIVITY_CLEAR_TASK, FLAG_ACTIVITY_NEW_TASK,
FLAG_ACTIVITY_SINGLE_TOP, FLAG_ACTIVITY_CLEAR_TOP
These flags didn't help to start the single instance activity.

Clarification on activity launch mode :"single task"

The android documentation defines single task launch mode as :-
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
Now my question is what happens in the case where the instance of activity already exists in a separate task but it is not at the top of the task. Are all activities above this activity destroyed and the new intent is delivered to this activity ? (as in FLAG_ACTIVITY_CLEAR_TOP with FLAG_ACTIVITY_NEW_TASK)
Yes, it will. You can test it by making a simple test app. New intent will be received in onNewIntent()

How can an activity with 'singleTask' launch mode be not root activity in a task?

This is an image from android documentation:
Activity Y has 'singleTask' launch mode but it is not root activity in the task, that is Activity X. How did it happen?
Upd.:
From the documentation:
"singleTask"
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.
Note: Although the activity starts in a new task, the Back button still returns the user to the previous activity.
Single task only means that this activity can only be created once cf to this website: http://inthecheesefactory.com/blog/understand-android-activity-launchmode/en
It doesn't matter if the activity is root or not.
singleTask:
This mode is quite different from standard and singleTop. An Activity
with singleTask launchMode is allowed to have only one instance in the
system (a.k.a. Singleton). If there is an existed Activity instance in
the system, the whole Task hold the instance would be moved to top
while Intent would be delivered through onNewIntent() method.
Otherwise, new Activity would be created and placed in the proper
Task.
I think the answer is that this activity was launched with an intent with FLAG_ACTIVITY_SINGLE_TOP flag, because intents have higher priority than xml tags.

Android default launchMode of LAUNCHER activity?

does the launchMode of the launcher activity in the manifest get ignored?
The android documentation says that the default launchMode is "standard" but this isn't logic for me if this would be applied to the main activity of an app because each time you start the app, another task would be created in the instance of the app.
Well, I delved into Android sources myself and found the following thing.
The launcher starts apps using the method startActivityAsUser in LauncherAppsService. The intent is constructed using these lines:
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIntent.setComponent(component);
launchIntent.setSourceBounds(sourceBounds);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
According to Android documentation, the flag FLAG_ACTIVITY_NEW_TASK means:
When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in.
This effectively and unconditionally overrides launchMode specified (or omitted to default behaviour) in the app, and ignores this attribute.
I think this demonstrates that the documentation is not clear (or complete) enough. Without such deep investigations of the core source codes everyone can get unexpected results now and then.
You are confusing two things. One is launchMode and the other is "what happens when the user selects an app icon from the HOME screen, or selects a task from the list of recent tasks". These are 2 completely different things.
launchMode
Each Activity has a specified launchMode (the default is "standard" or "multiple". This tells Android how to start this Activity, and there are many factors that can contribute to the "interpretation" of the launchMode. It depends on what other flags may have been specified in the Intent used. It depends on which task requested the launch of the Activity (or if the launch was requested from a non-activity context, like from a Service or BroadcastReceiver). It depends on whether or not an existing instance of the Activity is already active in the specified task, etc.
Behaviour on selecting an app icon from the HOME screen or list of installed applications
When the user selects an app icon, startActivity() is called with an Intent containing the following data:
ACTION=MAIN
CATEGORY=LAUNCHER
Component is set to the package name and the class name of the Activity that is defined in the manifest with ACTION=MAIN and CATEGORY=LAUNCHER
Flag FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_RESET_TASK_IF_NEEDED are set.
Regardless of the launchMode definition of the Activity to be launched, calling startActivity() with an Intent like this causes the following behaviour:
If there is already an existing task whose task affinity matches the Activity being started (in simple terms, if the app is already running), Android will simply bring the existing task to the foreground. That's it. It doesn't create an instance of any Activity. It doesn't call onNewIntent() on any Activity. It does nothing other than bringing the existing task to the foreground. This is why, even if you specify launchMode="standard" for your launcher Activity, Android doesn't create a new instance every time you click on your app icon.
If there isn't already an existing task whose task affinity matches the Activity being started (in simple terms, if the app isn't already running), Android will create a new task and launch the Activity into that task. launchMode doesn't play a role here, since there is absolutely no difference between the launch modes when launching a single Activity into a new task. Android always creates a new task and always creates a new instance of the Activity as the root of that task.
This behaviour is also the same when the user selectsa task from the list of recent tasks. If the task is still running, Android just brings the task to the foreground, does not start any new Activity instances and does not call onNewIntent(). If the task is no longer running, Android creates a new task and launches the launcher Activity into that task. The only difference here is that the flag FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY is also set in the Intent if the user selected a task from the list of recent tasks.
I hope this answers your question.
See this answer for a very detailed explanation of FLAG_ACTIVITY_RESET_TASK_IF_NEEDED and task reparenting in general.
Think of everything except the opening activity as an abstract implementation. Declaring an activity as
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Will cause it to open first. Subsequent activities are Overriden at the time an Intent is formed to navigate between activities. The overrides are represented as intent flags.
A list of intent extras:
http://developer.android.com/reference/android/content/Intent.html
With flags being commands you'd otherwise have written in the Manifest.
You are right.The default mode is "standard".
According to android documentation
*In standard mode ,Every time there's a new intent for a "standard" activity, a new instance of the class is created to respond to that intent. Each instance handles a single intent.
*.If the parent activity has launch mode standard (and the up intent does not contain FLAG_ACTIVITY_CLEAR_TOP), the current activity and its parent are both popped off the stack, and a new instance of the parent activity is created to receive the navigation intent.
The behavior of Activity set to standard mode is a new Activity will always be created to work separately with each Intent sent. Imagine, if there are 10 Intents sent to compose an email, there should be 10 Activities launch to serve each Intent separately. As a result, there could be an unlimited number of this kind of Activity launched in a device.
Behavior on Android pre-Lollipop
standard Activity would be created and placed on top of stack in the same task as one that sent an Intent.
For example, when we share an image from gallery to a standard Activity, It will be stacked in the same task as described although they are from the different application.
If we switch the application to the another one and then switch back to Gallery, we will still see that standard launchMode place on top of Gallery's task. As a result, if we need to do anything with Gallery, we have to finish our job in that additional Activity first.
Behavior on Android Lollipop
If the Activities are from the same application, it will work just like on pre-Lollipop, stacked on top of the task.
But in case that an Intent is sent from a different application. New task will be created and the newly created Activity will be placed as a root Activity like below.
Source from here

Resume started activity from Service

As I read in several stackoverflow answeres. To start activity from the service you have to use FLAG_ACTIVITY_NEW_TASK, but it will create new instance of activity in separate task. I want to reuse already started activity. Im trying to do it from IntentService that listen for c2dm notifications.
In the manifest for the Activity, you could set android:launchMode="singleInstance".
Here's what the docs say about singleInstance:
Only allow one instance of this activity to ever be running. This
activity gets a unique task with only itself running in it; if it is
ever launched again with the same Intent, then that task will be
brought forward and its Activity.onNewIntent() method called. If this
activity tries to start a new activity, that new activity will be
launched in a separate task. See the Tasks and Back Stack document for
more details about tasks.
And here is where I got my info.
You must use this flag with your Intent:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
IF you don't want a new instance of your Activity, set launchMode for your Activity in the AndroidManifest file:
android:launchMode="singleTask"

Categories

Resources