Intents sends signals to the Android system telling it that some action needs to be performed by another component (activity, services, broadcast receivers) in the same app or a different app. The system starts resolving which component in which app is responsible to handle this event that just got triggered. Let’s see a simple example of an implicit intent that opens a webpage URL in the browser.
String url = "http://google.com"; Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); startActivity(intent);
Pretty simple piece of code where we create an intent with the
ACTION_VIEW intent action and set the data to
startActivity() is triggered Android will start looking for all the apps that can handle this and most probably find just one which is the browser (chrome) that can receive it. It’ll launch chrome and open google.com in it.
Note: I highly recommend you to read my article on Android intents to completely understand the concept of Intents.
Now imagine if we wanted to advertise that our app can also handle such an implicit intent where we take an HTTP URL and do some useful jazz with it. In such a case Android will give two choices to the user – single browser installed (chrome) and our app – from which he can make a selection. To register a component of our app that can receive an implicit intent for a specific action and data, we can declare an
<intent-filter> for that particular component (activity, service or a broadcast receiver) in our manifest file.
Note: An explicit intent has its target specified (while creation) hence there’s no scene of making choices from a list or some such thing. Hence intent filters for them doesn’t make sense inherently.
Let’s see an example now where we’ll register an Activity component in our app as a browser (based on the aforementioned description) in our
<activity android:name="com.pycitup.pyc.WebViewActivity" android:label="@string/title_activity_web_view" > <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="http" /> </intent-filter> </activity>
Make sure that both the sending and receiving activities have requested permission for the type of action (
ACTION_VIEW) to be performed. In this case all that means is we need the
INTERNET permission in our manifest:
<uses-permission android:name="android.permission.INTERNET" />
Now when the intent code that you see above is fired, the user will be given multiple choices among which one of them will be our own app that’ll trigger
WebViewActivity. Inside the Activity you could then do things like show up the webpage in a WebView or some other sort of funky stuff.
Similar to this, from my previous article on Android intents, we could have an intent filter for the “share” example too (
ACTION_SEND) where once the user wants to share a piece of message, our app shows up along with facebook, whatsapp, gmail, etc.:
<activity android:name="com.pycitup.pyc.ShareActivity" android:label="@string/title_activity_share" > <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity>
Given that now we understand the basics of intent filters and have seen a few examples, let’s start understanding a bit more about the subelements.
Each activity in the manifest can contain multiple intent filters blocks. Each intent filter specifies the type of intents it’ll accept based on the
<data> subelements. So as soon as an implicit intent passes through (or conforms to) one of the intent filters it is delivered to that activity component or at least considered in the list of initial dialog with choices.
Let’s understand what each subelement is for:
<action>– Declares the intent action that you want to be accepted by the intent filter, specified by the
<intent-filter>element must contain one or more
<action>elements, else no Intent objects will get through the filter. Also note that the value must be the string value of the intent action, not the class constant (for example
<category>– Specifies the accepted intent category in the
android:nameattribute. Should contain the string value rather than the class constant (
<data>– Declares the type of data accepted using various attributes that you can find here. Examples are there in the code samples above which makes this element simple to understand.
Action which is the most reasonable action to perform and Data that contains the basic information that drives our action were pretty easy to comprehend but Category is something that might need a bit of an explanation. Using the category attribute, we give/mention additional information about the action to execute (that’ll be matched for intent resolution). Examples:
CATEGORY_LAUNCHER– Show up in the Launcher (Google Experience Launcher or Google Now Launcher or Nova or some other that you installed and use) as a top-level application. In other words the activity is the initial activity of a task (will launch when app starts) and is listed in the system’s application launcher (app drawer).
CATEGORY_BROWSABLE– Activities that can be safely invoked from a browser when a user clicks on a hyperlink.
CATEGORY_ALTERNATIVE– Activity should be included in a list of alternative actions the user can perform on a piece of data. So for instance if your code doesn’t specifies the intent action but sets this category and there’s an intent filter with this category too (but some other intent action name) then without even knowing the action name that intent filter will match with the implicit intent and deliver it to the component. Here’s an example (not sure if a good one):
String url = "http://google.com"; Intent intent = new Intent(); intent.addCategory(Intent.CATEGORY_ALTERNATIVE); intent.setData(Uri.parse(url)); startActivity(intent);
<intent-filter> <!-- can also try android.intent.action.SEND instead of VIEW --> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.ALTERNATIVE" /> <data android:scheme="http" /> </intent-filter>
CATEGORY_DEFAULT– The docs is a little hard to understand compared to this much simpler explanation. The
CATEGORY_DEFAULTcategory is applied to all implicit intents (automatically) passed to startActivity() and startActivityForResult(). So if you want your activity to receive implicit intents, it must include a category for
"android.intent.category.DEFAULT"in its intent filters.
You can go through the entire list here.
Moving on, an intent filter could have multiple instances of the action, category and data subelements. For example look at this:
<intent-filter> <action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND_MULTIPLE"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="image/*"/> <data android:mimeType="video/*"/> </intent-filter>
Multiple action tags that says the intent action can be
ACTION_SEND_MULTIPLE (same as
ACTION_SEND but for multiple data). Let’s take an example of selecting multiple photos in the gallery app and sharing them. So if you had only
android.intent.action.SEND action tag then in the list of choices dialog, Android would show your app only when you tried to share one photo, but not multiple. But now since we have
android.intent.action.SEND_MULTIPLE too we’ll get to select it also when we’re trying to share multiple photos. So this is more like an
OR operation (launch the app component or show up in the multi-list either when it’s an
ACTION_SEND_MULTIPLE), so your app component must be able to handle both of them – performing action on single image or multiple. You can apply the same logic with the data tag.
It is very important to remember that the implicit intent is tested against a filter by comparing it’s data with each element (action, data, category). When all those three tests are passed the intent is delivered to that particular component. If a match with any of them fails then the delivery won’t happen. Also if you fail to mention any of them in the manifest then also the match won’t happen.
Although remember you can have multiple intent filters like this:
<activity android:name="com.pycitup.pyc.ShareActivity" android:label="@string/title_activity_share" > <!-- This activity handles "SEND" actions with text data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND_MULTIPLE"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="image/*"/> <data android:mimeType="video/*"/> </intent-filter> </activity>
So in this case if the implicit intent fails to conform to the first intent-filter, then the check will be done on the second one and if that matches then woohoo! delivery to that app component (activity in this case) will happen.
Although users could get into
ShareActivity from some other activity like
MainActivity but when some other app is trying to trigger a share action your app will show up in the list of choices which could be selected. Once selected based on the incoming data that you get from various Intent methods like
getType(), etc. you can execute appropriate actions.
There are certain additional rules of the entire Intent Resolution process that are listed in the documentation here which I highly recommend you to go through (should be fairly quick).
So in the previous article, we’d learnt how to use Android Intents to trigger actions in your own apps (explicit) or some other third party app (implicit where Android resolves matching intents behind the scenes). In this tutorial we went through the concept of intent filters that’ll help us make our application components ready to be triggered by other apps, when they need to.