What are Intents?
An Intent is a message object that represents a request for an action to be performed. It can be used to start activities, services, or broadcast receivers. In simple terms, Intents act as a bridge to pass information and instructions between different components of your application or between different applications.
How Intents Work
When an Intent is created and sent, the Android system uses it to figure out which component should respond to the request. Depending on the type of Intent and how it's used, the system may:
Start an Activity: Move the user to a different screen.
Start a Service: Perform background tasks without user interaction.
Deliver a Broadcast: Send messages to multiple components interested in system-wide or app-wide events.
Types of Intents
Explicit Intents
Implicit Intents
Explicit Intent
An Explicit Intent specifies the exact component to start by name. This is commonly used within an application when you know which activity or service you want to start.
Example 1: Here’s how to launch another activity within the same app using explicit intent.
class IntentSenderActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_intent_sender)
val btnLaunch = findViewById<Button>(R.id.btn_launch)
btnLaunch.setOnClickListener {
// Create an explicit Intent to start IntentReceiverActivity
val intent = Intent(this, IntentReceiverActivity::class.java)
// Start the activity specified by the Intent
startActivity(intent)
}
}
}
Example 2: Launching a Calculator App Using Explicit Intent
Here we will launch the calculator app installed on your phone. To do so, you need the name of the package and the name of the class that implements the component.
Steps to Retrieve Package Name and Main Activity:
- Open a terminal and connect to your Android device:
adb shell
- List all installed packages:
pm list packages
- Filter the list to find the calculator package:
pm list packages | grep calculator
Here you will get the package name of the calculator app, i.e., com.oneplus.calculator
.
- Get the main activity of the calculator app:
//dumpsys package <package_name> | grep -A 1 "MAIN"
dumpsys package com.oneplus.calculator | grep -A 1 "MAIN"
Here we get the package name com.oneplus.calculator
and the name of the main component which is the launcher activity com.android.calculator2.activity.MiniActivity
.
class IntentSenderActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_intent_sender)
val btnLaunch = findViewById<Button>(R.id.btn_launch)
btnLaunch.setOnClickListener {
// Create an implicit Intent with the ACTION_MAIN action
val intent = Intent(Intent.ACTION_MAIN)
// Set the component to target the calculator app
intent.setComponent(ComponentName("com.oneplus.calculator", "com.android.calculator2.Calculator"))
try {
// Attempt to start the calculator activity
startActivity(intent)
} catch (e: Exception) {
// Show a Toast message if the calculator app is not found
Toast.makeText(this, "Calculator app not found", Toast.LENGTH_SHORT).show()
}
}
}
}
Implicit Intent
Used when you declare an action to be performed. The system determines the appropriate component(s) to handle the intent based on the intent filter declarations in the manifest of other apps.
Example: Here’s how to launch the dialer app using an implicit intent.
class IntentReceiverActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_intent_receiver)
val btnLaunch = findViewById<Button>(R.id.btn_launch)
btnLaunch.setOnClickListener {
// Create an implicit Intent with the ACTION_DIAL action
val intent = Intent(Intent.ACTION_DIAL)
// Set the data to a phone number URI
intent.data = Uri.parse("tel:1234567890")
// Create a chooser dialog for the Intent
val chooser = Intent.createChooser(intent, "Choose Dialer")
// Start the activity specified by the chooser
startActivity(chooser)
}
}
}
In this example, a chooser is created to display a chooser dialog, allowing you to select the desired dialer app. If you do not create a chooser as shown in the code below,
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:1234567890")
startActivity(intent)
and directly launch the intent, the system will automatically use the default app set for that functionality (in this case, the dialer app). As a result, the chooser dialog will not appear, and the default app will be launched instead.
However, if no default app is set and if multiple matches are found, the system presents a chooser dialog to the user, allowing them to select the preferred app.
Intent Filters
An Intent Filter is an expression in an app's manifest file that specifies the types of Intents the component can respond to. It allows the system to determine which components can handle a given Intent.
Key Elements of Intent Filters
action
: Specifies the general action to be performed, such asACTION_VIEW
,ACTION_EDIT
, orACTION_SEND
.category
: Provides additional information about the action to be performed, likeCATEGORY_DEFAULT
orCATEGORY_BROWSABLE
.data
: Specifies the type of data the intent is expecting, using schemes, host names, MIME types, etc.
When an app sends an Implicit Intent, the Android system matches it against the Intent Filters of all installed apps. If a match is found, the system launches the corresponding component. If multiple matches are found, the system presents a chooser dialog to the user, allowing them to select the preferred app.
Example: Here's how you can receive data from another app using intent filter.
- Create an activity where we will be receiving an image which will be shared from a browser app.
class IntentReceiverActivity : AppCompatActivity() {
private lateinit var imageView: ImageView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_intent_receiver)
imageView = findViewById(R.id.imageView)
// Handle the initial Intent that started the activity
handleIntent(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// Handle any new Intents received while the activity is already running
handleIntent(intent)
}
// Function to handle the Intent and extract the URI of the image
private fun handleIntent(intent: Intent) {
// Get the URI from the Intent extras
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
} else {
intent.getParcelableExtra(Intent.EXTRA_STREAM)
}
// If the URI is not null, set it as the image source for the ImageView
uri?.let {
imageView.setImageURI(it)
}
}
}
When sharing an image from the browser app, if the activity is not already running, the onCreate()
method is called. Inside onCreate()
, the handleIntent()
method processes the data (image URI). If the activity is already running, onCreate()
is not called; instead, onNewIntent()
is triggered, where we again call the handleIntent()
method to handle the new data.
- Inside the manifest file, declare the
IntentReceiverActivity
and the intent filters.
<manifest>
<application ...>
...
<activity
android:name="._05intent.IntentReceiverActivity"
android:exported="true"
android:label="Intents Receiver"
android:launchMode="singleTop" >
<intent-filter>
<!-- Filter for SEND action -->
<action android:name="android.intent.action.SEND" />
<!-- Category default is required for most implicit intents -->
<category android:name="android.intent.category.DEFAULT" />
<!-- Specify the MIME type for the data the activity can handle -->
<data android:mimeType="image/*" />
</intent-filter>
</activity>
</application>
</manifest>
<activity>
:Declares an activity that is part of the application.
android:name
: Specifies the class name of the activity.android:exported
: Indicates whether the activity can be launched by components of other applications.android:label
: Specifies the user-readable name for the activity, displayed in the UI.android:launchMode
: Determines how the activity is launched, with"singleTop"
preventing multiple instances if one already exists at the top of the stack.v
<intent-filter>
:Describes the types of intents the activity can respond to.
<action android:name="android.intent.action.SEND" />
:- Specifies that the activity can handle the
SEND
action, which is used when an app sends data to another app.
- Specifies that the activity can handle the
<category android:name="android.intent.category.DEFAULT" />
:- Indicates that the activity can handle the default category of intents. This is required for most implicit intents.
<data android:mimeType="image/*" />
:- Specifies the type of data the activity can handle. In this case, the activity is set up to handle image files.
This configuration ensures that the IntentReceiverActivity
can be launched when an image is shared using an Intent
with the SEND
action and the MIME type of image/*
.
Conclusion
Understanding intents and intent filters is essential for developing Android apps that communicate effectively with each other and provide a seamless user experience. Intents allow you to start activities, services, and broadcast receivers, enabling different app components to work together. By using explicit intents, you can target specific components, while implicit intents allow the system to choose the best component to handle the action. Intent filters in the manifest help declare what types of intents an activity can handle. With this knowledge, you can create more dynamic and interactive Android applications.