Understanding Explicit and Implicit Android Intents (App Component)

Any android application comprises one or more activities. In order to launch another activity from a particular activity (for example launch RegisterActivity from a click action in LoginActivity) we’ve to use a particular app component that android has called Intent. An Intent is basically an intention to do an action. It’s a way to communicate between Android components (not just activities) to request an action from and by different components. It’s like a message that Android listens for and react accordingly by identifying and invoking the appropriate app’s appropriate component (like an Activity, Service, Content Provider, etc.) within that same application or some other app. If multiple apps are capable of responding to the message then Android provides the user with a list of those apps from which a choice can be made.

Using intents you can accomplish a lot of things like navigating from one activity to another, start an external app’s activity so that the user can attain tasks like taking pictures via camera app, picking a contact using the contacts app, pinning location on maps using google maps, sending an email via gmail, sharing status using whatsapp or facebook, etc.

What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.

There are two types of intents:

  • Explicit Intent: While creating an Intent object when we explicitly specify and pass on the target component name directly in the intent, it’s an explicit intent.
  • Implicit Intent: In this case we delegate the task of evaluating the registered components (registration is usually done using intent filters that we’ll cover later) to Android based on the intent data and the intended action (like send an email, capture a photo, pin location on a map, etc.) that we pass. So Android will automatically fire up the component from the same app or some other app that can handle the intent message/job. The idea in this case is that, let’s say we have to pin a location on a map, for that we don’t have to code our own activity to handle that. Instead just pass on the location data to an app like Google maps that can do the job on our app’s behalf.

Typically we use explicit intents to fire components from our own app whereas implicit intents to launch components from other third party apps.

Explicit Intents

An explicit intent is most commonly used when launching an activity (from another one) within the same application. With them the component name is generally specified to which the intent has to be delivered. Without the component name Android decides which component (of which app) should receive the intent based on the intent information (especially action and data) passed, in which case it becomes an implicit intent that we’ll cover in a bit. Let’s see an example of an explicit intent right away:

// Currently in MainActivity

Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);

In the example above we try to launch LoginActivity.java from MainActivity.java which is placed at the top of the activity stack. We instantiate the Intent class by passing the current context and the component’s name (being *explicit*) that needs to be started (launched). Then we call startActivity() and pass it our intent. This will simply launch the LoginActivity from the same application right over MainActivity. Think of how we click links or submit forms on the web to navigate to other web pages (urls). This concept of intents is somewhat analogous to that.

Instead of specifying the component name directly in the Intent constructor, there are several other methods like setComponent(), setClass() or setClassName() on the same class that can be used to do the same:

// Currently in MainActivity

Intent intent = new Intent();

// Use setComponent()
ComponentName componentName = new ComponentName(this, LoginActivity.class);
intent.setComponent(componentName);

// or setClass()

intent.setClass(this, LoginActivity.class);

// or setClassName()

intent.setClassName("com.pycitup.pyc", "com.pycitup.pyc.LoginActivity");

startActivity(intent);

Notice how we used the fully-qualified class/component name with setClassName(). That’s important!

We can add data to our intent in key-value pairs that can be retrieved by the receiving activity.

// Currently in LoginActivity

Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("name", "rishabh");
intent.putExtra("age", 21);

startActivity(intent);

We just transfered two pieces of data, first one is string the other one is an integer. The key has to be of the type String. As values we can use the primitive data types (int, double, etc.) as well as objects of type CharSequence, String, Bundle, Parceable and Serializable. Let’s see how we retrieve this data when the new Activity is launched (inside onCreate() for instance).

// Currently in MainActivity

Bundle extras = getIntent().getExtras();

if (extras != null) {
  String name = extras.getString("name");
  int age = extras.getInt("age");
}

So calling getExtras() on our Intent object gives us a [`Bundle`](http://developer.android.com/reference/android/os/Bundle.html) object on which we can call various methods like getString(), getInt(), getChar(), etc. to retrieve various values that were transmitted. Instead of working with bundles we could have also used methods on the Intent object like getStringExtra() and getIntExtra().

For this entire explicit intents concept to work (that launches other components like Activity in our example) we’ll need to make sure that the activities are listed in AndroidManifest.xml (application manifest). Should be something like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.pycitup.pyc" >

    <application
        android:name="com.pycitup.pyc.PycApplication"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.pycitup.pyc.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.pycitup.pyc.LoginActivity"
            android:label="@string/title_activity_login" >
        </activity>
    </application>

</manifest>

Returning Data from Launched Activity

Sometimes you will want to get a result back from the launched activity’s end. For example, you launch an activity that lets the user pick a particular person from his contacts list or a particular image from the image gallery or maybe he just taps on the Android back button (that triggers finish()). When he’s done with choosing you want the reference data back so that you can do further actions or processing with his selection. In order to do this, instead of calling `startActivity()` we can call startActivityForResult(). Let’s see how to do that:

// Currently in MainActivity

Intent intent = new Intent(this, LoginActivity.class);
startActivityForResult(intent, 1);

The call to startActivityForResult() with a positive integer request code starts LoginActivity as the sub-activity of MainActivity. The integer request code passed as the second argument identifies the request. When we receive the result Intent from the sub-activity back, the same request code is also sent back so that we can properly identify the result and decide how to handle it.

Let’s comprehend how to return the data to the parent activity (MainActivity) now. If you want to send some data back to the parent activity then this is what you’ve to do:

Intent resultIntent = new Intent();
resultIntent.putExtra("result", "Excellent!");
setResult(RESULT_OK, resultIntent);
finish();

If you don’t want to return any data back, then you could do this instead:

Intent resultIntent = new Intent();
setResult(RESULT_CANCELED, resultIntent);
finish();

The result codes RESULT_OK and RESULT_CANCELED basically means operation succeeded and canceled respectively. When the sub-activity crashes then also the parent activity receives a RESULT_CANCELED result code. The response code can be some other user defined custom value too, not just these.

Time to see how the data reverted has to be handled in the parent activity. What we’ve to do is, override the onActivityResult() method in our parent activity. Note that this method is called immediately before onResume() when the activity is re-starting.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 1) {
        if (resultCode == RESULT_OK) {
            String result = data.getStringExtra("result");
            Log.d(TAG, result);
        }

        if (resultCode == RESULT_CANCELED) {
            // Operation failed or cancelled. Handle in your own way.
        }
    }
}

The code is pretty straight forward but you’d be wondering what is the requestCode. If you remember we had passed a second argument to the startActivityForResult() call. This requestCode is the same integer value as that to help us identify the correct operation from which the resultCode came from. Hence, this helps when you’re dealing with firing up multiple sub-activites.

Tip: Generally you should store your request code in a constant that can be reused. Something like this – private static final int REQUEST_CODE = 1;.

Implicit Intents

When we work with implicit intents, we generally specify the action that we want to be performed and optionally some data required for that action. Data is typically expressed as a Uri which can represent an image in the gallery or person in the contacts database for instance. An example will make this idea much more clearer.

// From MainActivity.onCreate()

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://google.com"));
startActivity(intent);

So what we did was specify an action to perform which in this case is ACTION_VIEW that leads to the most reasonable thing to occur in this case based on the HTTP URI which is show the webpage in a browser. We sort of implied or suggested that the user wants to view google.com in/through the app. Now instead of showing the webpage in a WebView we decided to use another app (chrome browser for instance) that the system already ships with. So convenient!

During an implicit intent, the Android system searches for all the registered components (through intent filters) for the specific action and data type (in this case an HTTP URI). If only one component is found, that is triggered but when several of them are identified a dialog is presented to the user from which he can make a suitable selection.

In the receiving Activity you can get the action and data passed using getAction() and getData():

Log.d(TAG, "Action: " + getIntent().getAction());

// getDataString() is the same as getData() but returns the URI as an encoded String
// whereas getData() returns a Uri object
Log.d(TAG, "Data: " + getIntent().getDataString());

/*
Should dump:
Action: android.intent.action.VIEW
Data: http://google.com
*/

Let’s see another example of an implicit intent where the user might want to share a piece of data/text/update/message via apps like Facebook, Gmail, WhatsApp, Twitter, Snapchat, etc.

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_TEXT, "Standing on the Moon!");
startActivity(intent); 

The standard action ACTION_SEND is used to send some data to another app. The documentation does an excellent job at explaining this action (which will make the example also crystal clear). Just to keep in mind, `setType()` is used to set the MIME type whereas putExtra() binds the data that has to be passed on for the sake of sharing.

By default, when we pass a URI while creating our Intent object, the system determines the appropriate MIME type required by the intent (first example). When we don’t specify the URI (second example), we should use setType() to specify the type of data (MIME) associated with the intent. This data also helps Android in determining which kinds of activities (or other components) should receive the intent.

With the code example above, what’ll happen is Android will present the user with a list of apps that he can use to share the text (facebook, gmail, whatsapp, etc.) from which he can choose one and infact the next time he can decide whether he wants to use a particular app always or not (if he prefers that most of the time). However, in this case the action is more of a social update (or “share) which probably means he should be given the ability to select a different app everytime, i.e., we should probably force a list everytime so that by mistake if he chooses an app that should be used always then he’ll have trouble getting the entire list again as that app will be used by default always. To do this, we can force the chooser dialog everytime with this piece of code:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_TEXT, "Standing on the Moon!");

Intent chooser = Intent.createChooser(intent, "Chooser Title!");

startActivity(chooser); 

We create a new intent using Intent.createChooser() and pass that to startActivity(). Also if you notice you can set the title of the dialog that’ll show a list of different apps that can handle the intent action by passing it to createChooser() as the second argument.

Determine If There is an App to Handle the Intent

Although most of the times certain intents have registered components in other built-in apps (like gmail, facebook, phone, email, calendar, contact, etc.), it is still important that we verify whether there’s an app that can handle the intent or not before invoking it. This is because for some reason if there’s no matching app then the application will crash that could lead to unhappy users leaving negative feedbacks on the play store. Let’s see how to do this.

// This snippet can obviously be wrapped in a method call for easy reuse

// Get the package manager
PackageManager packageManager = getPackageManager();
// Get activities that can handle the intent
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
// Check if 1 or more were returned
boolean isIntentSafe = activities.size() > 0;

if (isIntentSafe) {
  startActivity(intent);
}

We get the PackageManager object on which we call the queryIntentActivities() method that gives us a list of matching activities. If the size of the list is more than 0 then we should go ahead else we should disable the feature that generates the intent or link the user to an app on the play store that can handle it.

Feel free to go through the activities list. Here’s a snippet that’ll let you do that:

for (int i = 0; i < activities.size(); i++) {
    String activity = activities.get(i).toString();
    Log.d(TAG, activity);
}

This is what it dumped for me:

D/MainActivity﹕ ResolveInfo{38a6e506 com.android.mms/.ui.ComposeMessageActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{217b87c7 com.android.bluetooth/.opp.BluetoothOppLauncherActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{2fe1a1f4 com.google.android.talk/com.google.android.apps.hangouts.phone.ShareIntentActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{19227d1d com.google.android.keep/.activities.EditorActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{173c4b92 com.google.android.apps.docs/.app.SendTextToClipboardActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{89ed963 com.google.android.apps.plus/com.google.android.libraries.social.gateway.GatewayActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{1770e960 com.android.nfc/.BeamShareActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{26ece19 com.google.android.gm/.ComposeActivityGmail m=0x608000}
D/MainActivity﹕ ResolveInfo{c4f8ede com.google.android.apps.docs/.shareitem.UploadSharedItemActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{191ba8bf flipboard.app/flipboard.activities.ShareActivityAlias m=0x608000}
D/MainActivity﹕ ResolveInfo{20eb3b8c com.pinterest/.activity.create.PinItActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{a9532d5 com.facebook.orca/com.facebook.messenger.activity.ShareLauncherActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{1cc1baea com.path.paperboy/com.path.activities.TranslucentPaperboyEntryPointActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{6c11db com.google.zxing.client.android/.encode.EncodeActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{5d68478 com.whatsapp/.ContactPicker m=0x608000}
D/MainActivity﹕ ResolveInfo{5832751 com.minsh.bjs/.BangaloreJS m=0x608000}
D/MainActivity﹕ ResolveInfo{8789bb6 com.facebook.katana/com.facebook.composer.shareintent.ImplicitShareIntentHandler m=0x608000}
D/MainActivity﹕ ResolveInfo{3f8ff0b7 com.devhd.feedly/.Main m=0x608000}
D/MainActivity﹕ ResolveInfo{102b7024 com.closerlabs.spiral/com.spiral.ui.activity.NewPostFragmentAct m=0x608000}
D/MainActivity﹕ ResolveInfo{3e81e78d com.kakao.talk/.activity.SplashActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{15eabd42 com.dropbox.android/.activity.DropboxSendTo m=0x608000}
D/MainActivity﹕ ResolveInfo{3da8e153 com.google.android.apps.inbox/com.google.android.apps.bigtop.activities.ComposeMessageActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{ec16a90 com.skype.raider/com.skype.android.app.main.SplashActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{32326f89 com.hipchat/.activities.SignedOutActivity_ m=0x608000}
D/MainActivity﹕ ResolveInfo{a6b6b8e com.bsb.hike/.ui.ComposeChatActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{28563faf com.estrongs.android.pop/.app.ESFileSharingActivity m=0x608000}
D/MainActivity﹕ ResolveInfo{247a9fbc com.estrongs.android.pop/.app.SaveToESActivity m=0x608000}

There’s another shorter way to accomplish this same verification task for activities. We can call the resolveActivity() method on the intent object that returns null if there’s no app activity that can handle the intent.

// Verify that the intent will resolve to an activity
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
}

Conclusion

So we covered what exactly Intents are in Android development and what is the difference between explicit and implicit intents. We also covered a couple of usages. You should definitely check out the API reference to go through all sorts of action, data and category types and definitely other comprehensive information. Hopefully that gives you a good understanding and if that’s true then the next step is to understand intent filters properly.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Author: Rishabh

Rishabh is a full stack web and mobile developer from India. Follow me on Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *