What happens when you click on an application's launch icon? - android

 What happens when you click on an app's launch icon?
Is a new intent always sent, or is the result sometimes the same as resuming a task from recent tasks?
If an intent is sent, when does it get sent to the onCreate() method of a new activity instance and when does it get routed through onNewIntent() of an existing activity?
Let's suppose the intent gets routed through onNewIntent() of an existing activity in the task. Which activity does it get sent to? The one nearest the top or the one nearest the root? Will it always get sent to an instance of the application's launch activity or can it sometimes get sent to an activity with the same affinity as the root? Can it ever get sent to an activity which does not share the same affinity as the root?
Finally, how is this all affected by the various launch modes (standard, single top, single instance, single task) of the activities in the task?
If there is anyone out there who understands all this, please help me!

What happens when you click on an app's launch icon?
Launcher apps calls startActivity with an intent [action = Intent.ACTION_MAIN, category = Intent.CATEGORY_LAUNCHER and flag = Intent.FLAG_ACTIVITY_NEW_TASK].
Regarding Intent.FLAG_ACTIVITY_NEW_TASK, from docs:
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.
onNewIntent basics:
onNewIntent is delivered only when activity has set either singleTask, singleInstance launch modes. It is also delivered if activity has set singleTop launch mode or the intent to start the activity has set the flag FLAG_ACTIVITY_SINGLE_TOP and the activity instance is already at the top of the target task. It means an attempt was made to launch a new instance of activity, instead the existing instance itself need to handle the intent.
Here is the response to your queries:
Is a new intent always sent, or is the result sometimes the same as
resuming a task from recent tasks?
If the task is already running, it is brought to foreground. In case FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET flag was used to launch a activity and latter the task is brought to foreground, then the activity is killed. From docs:
This is useful for cases where you have a logical break in your
application. For example, an e-mail application may have a command to
view an attachment, which launches an image view activity to display
it. This activity should be part of the e-mail application's task,
since it is a part of the task the user is involved in. However, if
the user leaves that task, and later selects the e-mail app from home,
we may like them to return to the conversation they were viewing, not
the picture attachment, since that is confusing. By setting this flag
when launching the image viewer, that viewer and any activities it
starts will be removed the next time the user returns to mail.
-
If an intent is sent, when does it get sent to the onCreate() method
of a new activity instance and when does it get routed through
onNewIntent() of an existing activity?
onCreate is called while creating a new instance of activity. onNewIntent is called if already an activity instance exists and no new instance need to be created, as in case of singleInstance, singleTask and conditionally singleTop (as described above).
Let's suppose the intent gets routed through onNewIntent() of an
existing activity in the task. Which activity does it get sent to? The
one nearest the top or the one nearest the root? Will it always get
sent to an instance of the application's launch activity or can it
sometimes get sent to an activity with the same affinity as the root?
Can it ever get sent to an activity which does not share the same
affinity as the root?
In case of singleTask and singleInstance it has to be root of the task. In case of singleTop it has to be top activity of the task.
Finally, how is this all affected by the various launch modes
(standard, single top, single instance, single task) of the activities
in the task?
I hope the explanation provided till now, answers it.
Update 1:
Here is the Launcher code which adds the flags to intent:
void processShortcut(Intent intent) {
....
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
....
}
void startActivitySafely(Intent intent) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
...
startActivity(intent);
}

Your best bet is to read through the Developer docs here: http://developer.android.com/training/basics/activity-lifecycle/index.html
There is a flow chart in the first lesson(http://developer.android.com/images/training/basics/basic-lifecycle.png) which provides an excellent graphical representation of the Android activity life-cycle.

Related

Reuse current app instance when clicking on Notification [duplicate]

This question already has answers here:
Resume application and stack from notification
(8 answers)
Closed 6 years ago.
Right now I'm using a PendingIntent to launch my desired Activity from the Notification. But I want to know if it's possible to reuse the current app instance.
For example:
Launch app, the Launcher Activity is called HomeActivity
Navigate to SecondActivity
Press the home button
Click on the notification from the app and resume SecondActivity
But if the user didn't navigate to the SecondActivity i want to open the HomeActivity when I click on the Notification. I would be glad for any help regarding this problem :)
But I want to know if it's possible to reuse the current app instance.
Yes, you can reuse if the app is in background. In PendingIntent you will pass an Intent, so in that Intent you should set a flag like intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); so if the app is in background it will just bring activity to front.
According to android docs :
FLAG_ACTIVITY_NEW_TASK
Added in API level 1 int FLAG_ACTIVITY_NEW_TASK If set, this activity
will become the start of a new task on this history stack. A task
(from the activity that started it to the next task activity) defines
an atomic group of activities that the user can move to. Tasks can be
moved to the foreground and background; all of the activities inside
of a particular task always remain in the same order. See Tasks and
Back Stack for more information about tasks.
This flag is generally used by activities that want to present a
"launcher" style behavior: they give the user a list of separate
things that can be done, which otherwise run completely independently
of the activity launching them.
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. See FLAG_ACTIVITY_MULTIPLE_TASK
for a flag to disable this behavior.
This flag can not be used when the caller is requesting a result from
the activity being launched.
Check the docs here
But if the user didn't navigate to the SecondActivity i want to open the HomeActivity.
Why do you launch HomeActivity if your notification intent class you are creating have SecondActivity as the launcher. It will create a new SecondActivity and launches it if it is not in background.
EDIT: If you want to relaunch app where you left off try like this :
final Intent resumeIntent = new Intent(context, YourLauncher.class);
resumeIntent.setAction("android.intent.action.MAIN");
resumeIntent.addCategory("android.intent.category.LAUNCHER"‌​);
final PendingIntent resumePendingIntent = PendingIntent.getActivity(context, 0, resumeIntent, 0);
Here YourLauncher.class is the launcher class name.

How to keep a single Android instance of app in current running apps

I have an android application which has two activities let's say A and B where A is the launcher activity. For activity A I registered an intent-filter which opens application for a specific URL. I want to have always a single task for my application in current running apps. To solve this problem I tried different combinations for launchMode attributes:
A singleTop, B standard but when I access app via intent URL I have two applications in current running apps
A singleTask B standard but every time when I open the app, it starts with activity A even the apps was already opened with activity B (in this case I want to resume the app)
A singleTask B singleTask the behavior is like A singleTask B standard
I want that my app to have the same behavior like for example gmail, always to have maximum one instance in current running apps, when I open it and there is already an instance in background, to resume it and if I open the app via intent filter I want to process the intent and provide content accordingly (for example gmail is in background and I receive a new mail notification after I click it, the android keep one instance for gmail and displays the new mail).
I use Samsung Galaxy Note 4 with Android 6.0
Problem solved
I found a solution for my problem, maybe it is not the best but it works. Firstly I added a new Activity to catch the intent filter which has launchMode = "SingleTask" and in its onCreate method I posted an event to kill all existing activities.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if ("android.intent.action.VIEW".equals(getIntent().getAction())) {
// This Activity is started using an external app (eg: Gmail)
// So start the app from the beginning
// (redirecting to Activity A)
Intent mainIntent = getIntent(); // Copy the Intent used to launch me
// Launch the real root Activity (launch Intent)
mainIntent.setClass(this, StartActivity.class);
// Post an event to kill all existing activities
// To do this i use Guava
PubSub.getInstance().post(new KillActivityEvent());
startActivity(mainIntent);
finish();
} else {
// The activity wasn't started by an external app
finish();
}
}

How to recreate recent-apps activity when app is launched from notification

My app and activity is in my list of recent apps when I receive a notification. When I click on the notification, I want the intent of the notification to be honored. In my case I want to restart the activity (brute force) and pass in the new intent: so, finish then re-create. I am reading about such tags as FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_SINGLE_TOP but I don't understand them enough to know how to force a "finish then re-create` of my activity. And, oh, the activity in question is MainActivity.
The snippet inside GcmListenerService uses
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
for sending the notification intent
Alternatively
If I go with onNewIntent things get complicated because there maybe DialogFragments being displayed, etc. And I would have to clear everything. That is why finish then re-create seem like the simplest solution to me.
Intent flag FLAG_ACTIVITY_CLEAR_TOP should produce the desired behavior. The documentation for Tasks and Back Stack says this in the section for Using Intent Flags:
If the launch mode of the designated activity is "standard", it too is
removed from the stack and a new instance is launched in its place to
handle the incoming intent. That's because a new instance is always
created for a new intent when the launch mode is "standard".
The documentation for FLAG_ACTIVITY_CLEAR_TOP describes the same behavior in more detail.

Activity opened twice

I have an application that uses Urban Airship for push notification. When a notification arrives and the user clicks on it, activity A in my application should open and do something.
I've installed the BroadcastReceiver as is shown in the docs, and it's almost working.
When my app is in the foreground I don't let the user see the notification at all, and just handle it automatically.
When my app is not running at all, the activity opens up just fine.
When my app is in the background (which always happens when A is the top activity), a second instance of Activity A is created.
This is, of course, a problem. I don't want two A activities, I just want one of them. Here's the relevant BroadcastReceiver code:
#Override
public void onReceive(Context ctx, Intent intent)
{
Log.i(tag, "Push notification received: " + intent.toString());
String action = intent.getAction();
int notificationId = intent.getIntExtra(PushManager.EXTRA_NOTIFICATION_ID, -1);
if(action.equals(PushManager.ACTION_NOTIFICATION_OPENED))
{
Intent intentActivity = new Intent(ctx, ActivityA.class);
intentActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
UAirship.shared().getApplicationContext().startActivity((intentActivity);
}
}
UPDATE:
I tried to bypass this bug by calling System.exit(0) when the user presses Back on Activity A. The process ended, but then it was restarted immediately! My BroadcastReceiver is not called again in the second instance. What's happening?
UPDATE 2:
#codeMagic asked for more information about the app and activity A.
This app lets its user review certain items and comment on them. Activity A is started when the app is launched. If the user's session isn't valid any more, a Login activity is started. Once the user logs in, activity A becomes active again. A only has a "No items to review" message and a "Try now" button.
When the user logs in, the server starts sending push notifications whenever a new item is available for review. When the app gets the notification, activity A accesses the server and gets the next item to review. The item is shown in activity B. Once the review is submitted to the server, activity B finishes and activity A is again the top activity.
The server knows when a user is reviewing an item (because activity A fetched it), and doesn't send push notifications until the review is submitted - meaning a notification can't come if the user isn't logged in or if the user is viewing activity B.
While I agree there is a subtle race condition here, it is not causing the problem I'm seeing - in testing I am 100% positive there's no race condition - the push notification is only sent after Activity A becomes active again.
The solution was to add a launchMode='singleTask' to the activity in AndroidManifest.xml . As a result, instead of a new activity, onNewIntent of the same activity instance is called.
You can use one of several Intent Flags. FLAG_ACTIVITY_REORDER_TO_FRONT being one of them. This will bring the Activity to the front of the stack if it is already in the stack and if not then it will create a new instance. I believe you will still need FLAG_ACTIVITY_NEW_TASK if you aren't calling it from an Activity
Intent.FLAG_ACTIVITY_CLEAR_TOP should also work. But this will clear any other Activities on the stack. It just depends on what other functionality you need. Look through the Intent Flags and see which of these will work best for you
There are multiple scenarios when this could happen. One of them can be handled this way. Please see my answer here: https://stackoverflow.com/a/44117025/2959575
Ok, two notes on this :
You can register a broadcast receiver via the manifest so it is independent of any parts of your app. and use a Singleton pattern (keep a static reference to your activity somewhere in your app) that way you can check if their is an activity viewing or not and process accordingly.
// your activity A
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
myActivityReference = this;
}
public void onPause() {
super.onPause();
if (isFinishing()) {
myActivityReference = null;
}
}
or you can keep everything as it is and use activity lunching modes flags in your manifest such as singleTop, singleInstance ... etc. take a look here android activity lunch modes

Multiple Instances of an Activity

I am trying to show the user information on incoming-call screen, whenever there is an incoming-call. So I have a broadcast receiver listening to incoming calls, which starts the intent service, which subsequently starts an activity (with Theme Dialog).
Now, whenever there is an incoming-call, my activity dialog pops up and shows as intended.
Problem: When the activity dialog is already on the screen and incoming-call happens, there is no new activity dialog with new information. I guess that whenever there is an instance, Android does not creates the new one. So it seems like my problem is "creating multiple instances of an activity".
Please note that I am starting an activity from an intent service using FLAG_NEW_TASK.
Google Doc says :
FLAG_ACTIVITY_NEW_TASK
"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."
So, if you want to start a new fresh activity then simply not use this flag only, you should use it with FLAG_ACTIVITY_CLEAR_TASK for the desired result.
For Example:
// Sets the Activity to start in a new, empty task
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
If the above solution is not what you needed, then have a look at
android:launchMode attribute, declare this attribute with the desired options (i.e. as per your need) in activity tag of manifest file.
Hope this will solve the problem.
Use flag FLAG_ACTIVITY_MULTIPLE_TASK which according to the documentation :
Used in conjunction with FLAG_ACTIVITY_NEW_TASK to disable the behavior of bringing an existing task to the foreground. When set, a new task is always started to host the Activity for the Intent, regardless of whether there is already an existing task running the same thing.
Using this flag along with FLAG_ACTIVITY_NEW_TASK will cause each activity instance to be created as a separate task and thus you can have different dialog pop ups.
Simply add following flags to your Intent .
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_MULTIPLE_TASK);

Categories

Resources