Understanding which Activity starts first in an Android app - android

It isn't clear to me how Android determines which Activity starts first when an app starts. The Android documentation states the following concerning the AndroidManifest.xml file about Activities:
"Only one activity should have the "main" action and "launcher" category..."
So in the AndroidManifest.xml file, you should essentially have only one:
action android:name="android.intent.action.MAIN"
category android:name="android.intent.category.DEFAULT"
However, while looking at sample code from the Android SDK, the application called "APIDemos" contains a manifest file with tons of
"android.intent.action.MAIN" and
"android.intent.category.DEFAULT"
I am totally confused. This seems to go contrary to what Google is stating about there only suppose to be one. Does Android simply grab whichever one appears first in the manifest and ignores all the others? If not, why are there multiple MAINs and DEFAULTs?

Activities will very often need to support the CATEGORY_DEFAULT so that they can be found by Context.startActivity(). So, CATEGORY_DEFAULT can appear number of times.
Android does not grab whichever one appears first in the manifest but it starts with activity having CATEGORY_LAUNCHER.
CATEGORY_LAUNCHER : The activity can be the initial activity of a task and is listed in the top-level application launcher.
For more details refer:
http://developer.android.com/guide/topics/intents/intents-filters.html

action.MAIN and category.LAUNCHER are the ones that are used to specify what activity gets launched when the user presses your app icon or selects it from the running list of apps.
You can use other combinations of actions and category.DEFAULT to respond to different events but the combination of action.MAIN and category.LAUNCHER should only be defined once.

I am totally confused. This seems to go contrary to what Google is stating about there only suppose to be one.
It isn't contrary. These activities have category CATEGORY_DEFAULT, but not CATEGORY_LAUNCHER.

Related

Why do we get these "invalid" intents?

We have an app with an Activity that can be started in two ways:
From another Activity - always with some extra data filled in
From deep linking
As far as I can see this is always working just fine. We either get the Intent.ACTION_VIEW with a data URI, or we get some string extras.
However, we have a very few instances where action is Intent.ACTION_MAIN and there are no extra data.
The toString() of the Intent is as follows (class name changed):
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10400000 cmp=com.example.OurActivity }
Intent.getExtras() returns null, Intent.getDataString() returns null.
In which cases can this happen? Why is the category for the Activity Intent.CATEGORY_LAUNCHER? How can we get the data needed to show the user the right contents?
launchMode is not specified for the Activity. The only IntentFilter in AndroidManifest.xml is for the deep linking (and not the launcher category).
The issue happens on Android 4-6 on a wide range of devices.
Edit: Forgot to mention the flags:
As the print out suggests the flags for the Intent are FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_BROUGHT_TO_FRONT. I suppose that could be relevant here.
I believe, I nailed it:
There're launchers, like Nova Launcher which allows users to start with any of app's Activities, instead of the normal flow:
For example, you can add a shortcut on your desktop to start Gmail client with Account setup Activity.
And in this case, Activity is being started with empty Extras and technically it becomes a launcher's Activity.
Now that AndroidManifest.xml is manipulated by the build system, it often happens that libraries that you include also add things to the manifest, which I suspect may be happening here.
Although you state that there is only one IntentFilter in the manifest, have you actually checked the installed app to see what its manifest says (rather than relying on what you think you put in your source code)?
Various apps are available in the Play Store to show you the manifest of an installed app - including App Detective (which I wrote).

Android Lollipop behavior change

As per changes in Android Lollipop, reference :
StackOverflow Question
Cheese factory blog
I expect that when I start an activity of other application from my application, it should open in a new task even if the behavior is default (launchmode is standard). So, I made 2 test apps to verify the same behavior. But surpirisingly, the other app always opens up in my app's task, if there's no launchmode specified. I've tested this on Xiaomi Redmi Note 3 (5.1.1), Marshmallow emulator (x86), and the behavior is same for both. I'd appreciate some help on this, and also a link for reference from Android developer's site.
Some code :
Launching app :
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
startActivity(intent);
break;
App to be launched :
<activity android:name="com.android.sample.launchdemo.ActivityB">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
On a button click from launching app, the intent is fired and activity B successfully opens up, but in the same task. Thanks in advance for any help/suggestions.
After looking through the documentation, the feeling I have is that standard mode works the same way as it did before Android 5.0 (Lollipop). The cheese factory blog post was the only one that specified that action and even in my own experience standard launch modes have opened an activity in the same task it was sent in (unless intent flags were passed through). Someone correct me if I am wrong but it is not specified in Android documentation that standard mode would open a new task. From the Android documentation:
"standard" (the default mode): The system creates a new instance of the activity in the task from which it was started and routes the intent to it. The activity can be instantiated multiple times, each instance can belong to different tasks, and one task can have multiple instances. See full documentation
For what you are looking for, when firing off the Intent, the only way to guarantee a new task is use the flag FLAG_ACTIVITY_NEW_TASK. You can set this by calling either intent.setFlag(Intent.FLAG_ACTIVITY_NEW_TASK) or intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) (the latter is for chaining flags together because that method will return the intent).
After doing more research, the only change (relating to this) that appears to be made in Android 5.0 Lollipop is that the recent apps screen can show multiple instances of an activity.
In previous releases, the recents screen could only display only one task for each app that the user interacted with most recently. Now your app can open more tasks as needed for additional concurrent activities for documents. This feature facilitates multitasking by letting users quickly switch between individual activities and documents from the recents screen, with a consistent switching experience across all apps. Examples of such concurrent tasks might include open tabs in a web browser app, documents in a productivity app, concurrent matches in a game, or chats in a messaging app.
To me this seems like the only change relating and the post (cheesefactory and SO) have documentLaunchMode set to create new tasks for each activity (which very well could be the case considering the cheesefactory had a "Gallery" app). Documentation on concurrent documents and documentLaunchMode. documentLaunchMode and the flag FLAG_ACTIVITY_NEW_TASK can be configured to do similar things documentLaunchMode is preferred.
I've found the documentation below.
https://developer.android.com/guide/components/activities/recents.html
=> when the user is using their browser, and they tap Share > Gmail. The Gmail app's Compose screen appears. Tapping the Recents button at that time reveals Chrome and Gmail running as separate tasks. In lower versions of Android, all of the activities appear as a single task, making the Back button the only means of navigation. Figure 2 shows how the Recents screen looks in Android 5.0 and higher, versus lower versions of the platform. The image on the left screen for Android 5.0 and higher, and the image on the right shows how it appears in lower versions of Android.
and refer to the following link.
Lollipop: making my activity stay in the task that launched the share intent
=> By default, launchMode of myfirstapp.MainActivity is "standard" and any intent flags weren't set.
But after myfirstapp.MainActivity is started through share action of Album, the intent flag contains FLAG_ACTIVITY_MULTIPLE_TASK, FLAG_ACTIVITY_NEW_DOCUMENT.
When an activity is started through share, an activity which contains share in its Toolbar just sets Intent to its ShareActionProvider, and then ShareActionProvider starts an activity with this Intent - in this case, myfirstapp.MainActivity.
So I think that from Lollipop, the system begins a new task for the activity from a different app whose launchMode is "standard" only when the activity is started through share action.

open android activity in new task without using android:launchMode="singleTask"

I've created a browser application with main activity which response to the following intents:
<intent-filter>
<data android:scheme="http"/>
<data android:scheme="https"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<action android:name="android.intent.action.VIEW"/>
</intent-filter>
On url click from other task (gmail, sms) if i choose my application, the activity is open in the same task as the calling task.
When I choose different browser (Mozila firefox, chrome, dolphine) they are opening in different task.
Looking on other browsers manifest, I see that no one using android:launchMode="singleTask".
I don't want to use single task flag since it is not recommended by google and also makes me other prolems.
I tried to understand how does other browsers do it but didn't figure it out.
any ideas? is there other way to open my activity in different task without using singleTask flag?
You can check in onCreate if the activity is running in a single task. If not just finish() it and create it again with using FLAG_ACTIVITY_NEW_TASK
This may help you
ActivityManager activityManager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (RunningTaskInfo task : tasks) {
if (ctx.getPackageName().equalsIgnoreCase(task.baseActivity.getPackageName()))
// check if it's the only one activity or whatever
}
I have found the following description on Android Developers site:
As another example, the Android Browser application declares that the
web browser activity should always open in its own task—by specifying
the singleTask launch mode in the element. This means that
if your application issues an intent to open the Android Browser, its
activity is not placed in the same task as your application. Instead,
either a new task starts for the Browser or, if the Browser already
has a task running in the background, that task is brought forward to
handle the new intent.
As you can see android:launchMode=singleTask is the right choice in your case. You already mentioned that you had issues with this property so maybe let's focus on them.
Update 28.05.2014
Note from Google regarding singleTask launchMode:
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.
singleTask and singleInstance use case from Google:
Specialized launches (not recommended for general use)
As you can see singleTask may not be recommended for general use but your case is not general, actually it's one of the cases where singleTask fits perfectly.
In other words, singleTask is not forbidden, it's just need to be used with caution in order to provide end users common experience with your app.
I hope I made it clear now for you. Feel comfortable with this launch mode in your case.
You're trying to "bend the rules" a little, and you're not clear enough on what you're trying to avoid by not using android:launchMode="singleTask".
So I would suggest researching into either:
Creating a Service and having that Service listen to the intent filter. Then having this Service open your Activity, and having the Activity's affinity set correctly to match the Service's. This would allow you to get over the issue where affinity is not binding the Activity correctly.
Having a silent Activity starting a new Activity and quiting. The silent Activity would start in a new stack (not in a singletask mode), and would shut itself down upon starting the Activity you actually need.

Always declare Activities and Services in the AndroidManifest file?

I know you must declare all your Activities and Services in the AndroidManifest.xml file in order to make them accessible to the system (as said in the official docs) but if I have a Service or an Activity that I want to be started only by another Activity in my app, do I need to declare it in the manifest? I.e., I always launch a secondary activity from the primary activity of my app that directly points the secondary activity's class (no intent filter resolution), is still necessary to declare the secondary activity in the manifest? And what if I don't want anyone outside my app to be able to launch my secondary activity? I'm sorry if it is a stupid question, I just want to understand if it is a good practice (if possible) to omit activities and services from the manifest file when you want them to be started only by pointing their respective classes within the same app.
You must declare all of your activities and services (and everything else like BroadcastReceivers) in the AndroidManifest.xml file.
You won't be able to use them otherwise.
EDIT: As per CommonsWare comment, adding android:exported="false" to the AndroidManifest.xml's declaration of the activity would prevent your secondary activity from being launched outside of your application.

Listing an application's activity and intent-filters?

I am interested in activating another application's activity. I know from reading the Android SDK that it's probably better to do this with an implicit intent. However, this activity doesn't reside in an application I own, so I don't know the action and category and data flags on the intent-filter.
How can I examine an Android applications metadata like the activity classes and the intent-filters for those activities (if declared in the manifest)?
Thanks!
I would try this.
Check on openintents
Contact the developer.
Use android-apktool too see the app's manifest.
It might be illegal to use android-apktool. Use it under your own risk.
There is one more solution: you could run this app and look into logcat. All intents are logged, so you could see what is called up. You won't see extra data though.

Categories

Resources