Version 3.0
Copyright © 2009, 2010, 2011, 2012, 2013 Lars Vogel
28.01.2013
| Revision History | |||
|---|---|---|---|
| Revision 0.1 | 20.07.2010 | Lars Vogel |
Created |
| Revision 0.2 - 3.0 | 19.07.2010 - 28.01.2013 | Lars Vogel |
bug fixes and enhancements |
Table of Contents
Intents are asynchronous messages which allow Android components to request functionality from other components of the Android system. Intents can be used to signal to the Android system that a certain event has occurred. Other components in Android can register to this event via an intent filter.
Intents
are send to the Android system via a method call, e.g. via the
startActivity()
method you can start
activities.
Depending on how
the
Intent
was constructed
the Android system will run an receiver determination
and determine possible components which can be started. If several
components have registered for the same
intents
the user can decide which component should be started.
For example an activity can send an intent to the Android system which starts another activity via the following code.
# Start the activity connect to the # specified class Intent i = new Intent(this, ActivityTwo.class); startActivity(i);
Intents
are instances of the
android.content.Intent
class.
An
Intent
can contain data. This data can be used by the receiving
component.
For example your application can start via an
intents
a browser component. As data it may send the URL to the browser
component which this browser should open and display.
String url = "http://www.vogella.com"; Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(url)); startActivity(i);
Android supports explicit and implicit Intents.
Explicit intents explicitly defines the component which should be called by the Android system, by using the Java class as identifier.
The following shows how to create an
explicit intents
and send it to the Android system.
If that class represents an
activity
Intent
the Android system start it.
Intent i = new Intent(this, ActivityTwo.class); i.putExtra("Value1", "This value one for ActivityTwo "); i.putExtra("Value2", "This value two ActivityTwo");
Explicit intents are typically used within on application as the classes in an application are controlled by the application developer.
Implicit intents specify the action which should be performed and optionally data which provides data for the action.
For example the following tells the Android system to view a
webpage.
Typically the web browser is registered to this
Intent
but other component could also register themself to this event.
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.vogella.com")); startActivity(i);
If these Intents are send to the Android system it searches for all components which are registered for the specific action and the data type.
If only one
component is found, Android starts this component
directly.
If several
components are identifier by the Android system,
the user
will get an
selection dialog and can decide which component
should be
used for
the
Intent.
An implicit
Intent
contains the action and optionally additional data. The receiving
component can
get this
information via the
getAction()
and
getData()
methods on the
Intent
object. This
Intent
object can be retrieved via the
getIntent()
method.
The component which creates the
Intent
can add data to it via the overloaded
putExtra()
method. Extras are key/value pairs; the key is always a String. As
value you can use the primitive data types (int, float,..) plus
objects of type
String,
Bundle, Parceable and Serializable.
Lots of Android applications allow you to share some data with other people, e.g. the Facebook, G+, Gmail and Twitter application. You can send data to one of this components. The following code snippet demonstrates that.
Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(android.content.Intent.EXTRA_TEXT, "News for you!"); startActivity(intent);
The component which receives the
Intent
can
use the
getIntent().getExtras()
method call to get the extra data.
Bundle extras = getIntent().getExtras(); if (extras == null) { return; } // Get data via the key String value1 = extras.getString(Intent.EXTRA_TEXT); if (value1 != null) { // Do something with the data }
A component can register for an intent via an intent filter. These are described in a later chapter.
If you send an
Intent
to the Android system, Android requires that you tell it to which
type of component your
Intent
should be send.
To start an
activity
use the method
startActivity(Intent). This method is defined on the
Context
object and available in every
activity
object.
If you call an
activity
with the
startActivity(Intent)
method the caller requires no result from the called
activity.
You can also start
services
via
intents. Use the
startService(Intent)
method call
for that.
If you start the
intent with
the
startActivityForResult()
method you can specify a result code. Once the called
activity
ends
the
onActivityResult()
method on the calling
activity
is called and you can
perform
actions based on the
result of the
triggered
activity.


The following example code demonstrates how to trigger and
intent
with the
startActivityForResult()
method.
public void onClick(View view) { Intent i = new Intent(this, ActivityTwo.class); i.putExtra("Value1", "This value one for ActivityTwo "); i.putExtra("Value2", "This value two ActivityTwo"); // Set the request code to any code you like, you can identify the // callback via this code startActivityForResult(i, REQUEST_CODE); }
If you use the
startActivityForResult()
method then the started
activity
is called a
Sub-Activity.
If the Sub-Activity is finished it can send data back to its caller
via
Intent. This is done in the
finish()
method.
@Override public void finish() { // Prepare data intent Intent data = new Intent(); data.putExtra("returnKey1", "Swinging on a star. "); data.putExtra("returnKey2", "You could be better then you are. "); // Activity finished ok, return the data setResult(RESULT_OK, data); super.finish(); }
Once the
Sub-Activity
finished, the
onActivityResult()
method in the calling
activity
will be called.
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) { if (data.hasExtra("returnKey1")) { Toast.makeText(this, data.getExtras().getString("returnKey1"), Toast.LENGTH_SHORT).show(); } } }
If an Intents is send to the Android system, it will determine suitable applications for this Intents. If several components have been registered for this type of Intents, Android offers the user the choice to open one of them.
This determination is based on IntentFilters. An IntentFilters specifies the types of Intent that an activity, service, or Broadcast Receiver can respond to. An Intent Filter declares the capabilities of a component. It specifies what an activity or service can do and what types of broadcasts a Receiver can handle. It allows the corresponding component to receive Intents of the declared type.
IntentFilters
are typically defined via the
AndroidManifest.xml
file. For
BroadcastReceiver
it is also possible to define them in coding.
An
IntentFilters
is defined by its category, action
and data filters. It can also
contain additional metadata.
If a component does not define an Intent filter, it can only be called by explicit Intents.
The following code will register an Activity for the Intent which is triggered when someone wants to open a webpage.
<activity android:name=".BrowserActivitiy" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="http"/> </intent-filter> </activity>
The following example will register an Activity for the
ACTION_SEND
Intent
for the
text/plain
mime type.
<activity android:name=".ActivityTest" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity>
If a component does not define an Intent filter, it can only be called by explicit Intents.
Intents
can also be used to send broadcast messages into the Android system.
BroadcastReceivers
can register to event and will get notified if such an event is
triggered.
Your application can register to system events, e.g. a new email has arrived, system boot is complete or a phone call is received and react accordingly.
As said earlier, since Android version 3.1 the Android system will per
default exclude all
BroadcastReceiver
from receiving
Intents
if the corresponding application has never been started by the user
or
if the user explicitly stopped the application via the Android
menu (in
Manage Application).
Sometimes you want to find if an application has registered for a
certain
Intent. For example you want to check if a certain receiver is
available and
if you enable some functionality in your app.
This can be done via the
PackageManager
class.
The
following
code checks
if an someone has registered himself for
a certain
Intent. Construct your
Intent
as you desired to trigger it and pass it to the following method.
public static boolean isIntentAvailable(Context ctx, Intent intent) { final PackageManager mgr = ctx.getPackageManager(); List<ResolveInfo> list = mgr.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); return list.size() > 0; }
Based on the result you can adjust your application for example you could disable or hide certan menu items.
The following assumes that you have already basic knowledge in Android development. Please check the Android development tutorial to learn the basics.
The following tutorial demonstrates how to use explicit Intents and how to transfer data between two activities.
The first
activity
will call the second one via an explicit intent. Once the user select
the "Back" button on his phone the first
activity
will receive some hard-coded data from the
Sub-Activity.
Create a new Android application "de.vogella.android.intent.explicit" with the activity called ActivityOne.
Change the layout
main.xml
to the following.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/TextView01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minHeight="60dip" android:text="First Activity. Press button to call second activity" android:textSize="20sp" > </TextView> <Button android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick" android:text="Calling an intent" > </Button> </LinearLayout>
Create the layout "activity_result.xml".
<?xml version="1.0" encoding="utf-8"?> <GridLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:alignmentMode="alignBounds" android:columnCount="2" android:columnOrderPreserved="false" android:useDefaultMargins="true" > <TextView android:layout_gravity="center_horizontal" android:text="Input 1" android:textSize="32dip" /> <EditText android:id="@+id/input1" android:layout_gravity="fill_horizontal" android:text="Default" /> <TextView android:layout_gravity="center_horizontal" android:text="Input 2" android:textSize="32dip" /> <EditText android:id="@+id/input2" android:layout_gravity="fill_horizontal" android:text="Default" /> <Button android:layout_column="1" android:layout_gravity="right" android:width="80dp" android:onClick="onClick" android:text="Finish" /> </GridLayout>
Declare a new
activity
called
ActivityTwo
via the
AndroidManifest.xml
file.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.intent.explicit" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <application android:icon="@drawable/icon" android:label="@string/app_name" > <activity android:name=".ActivityOne" 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="ActivityTwo" android:label="ActivityTwo" > </activity> </application> </manifest>
Create the following coding for your two activities.
package de.vogella.android.intent.explicit; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class ActivityOne extends Activity { private static final int REQUEST_CODE = 10;/** Called when the activity is first created. */@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void onClick(View view) { Intent i = new Intent(this, ActivityTwo.class); i.putExtra("Value1", "This value one for ActivityTwo "); i.putExtra("Value2", "This value two ActivityTwo"); // Set the request code to any code you like, you can identify the // callback via this code startActivityForResult(i, REQUEST_CODE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) { if (data.hasExtra("returnKey1")) { Toast.makeText(this, data.getExtras().getString("returnKey1"), Toast.LENGTH_SHORT).show(); } } } }
package de.vogella.android.intent.explicit; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class ActivityTwo extends Activity {/** Called when the activity is first created. */@Override public void onCreate(Bundle bundle) { super.onCreate(bundle); setContentView(R.layout.second); Bundle extras = getIntent().getExtras(); if (extras == null) { return; } String value1 = extras.getString("Value1"); String value2 = extras.getString("Value2"); if (value1 != null && value2 != null) { EditText text1 = (EditText) findViewById(R.id.input1); EditText text2 = (EditText) findViewById(R.id.input2); text1.setText(value1); text2.setText(value2); } } public void onClick(View view) { finish(); } @Override public void finish() { Intent data = new Intent(); // Return some hard-coded values data.putExtra("returnKey1", "Swinging on a star. "); data.putExtra("returnKey2", "You could be better then you are. "); setResult(RESULT_OK, data); super.finish(); } }
Run your application. The first will send data to the second
activity
which will be shown on the user interface. If you select back on your
phone, the first
activity
will display a
Toast
with the data from the second
activity.
The following exercise demonstrates the usage of implicit intents to trigger activities in your Android system.
Create a new Android application called
de.vogella.android.intent.implicit
with an
activity
called
CallIntentsActivity.
In this example you use a
Spinner
view
to select which
intent
is triggered. For the content of the
Spinner
you define static values.
Create the following
intents.xml
file in the
res/values
folder.
<resources>
<string-array name="intents">
<item>Open Browser</item>
<item>Call Someone</item>
<item>Dial</item>
<item>Show Map</item>
<item>Search on Map</item>
<item>Take picture</item>
<item>Show contacts</item>
<item>Edit first contact</item>
</string-array>
</resources>
Change the layout file of the Activity to the following.
<?xml version="1.0" encoding="utf-8"?> <GridLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:alignmentMode="alignBounds" android:columnCount="1" > <Spinner android:id="@+id/spinner" android:layout_gravity="fill_horizontal" android:drawSelectorOnTop="true" > </Spinner> <Button android:id="@+id/trigger" android:onClick="onClick" android:text="Trigger Intent"> </Button> </GridLayout>
To be able to use certain intents you need to register for the
required permission in your
AndroidManifest.xml
file.
Ensure that your
AndroidManifest.xml
contain the permissions from the followng listing.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.intent.implicit" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <uses-permission android:name="android.permission.CALL_PRIVILEGED" > </uses-permission> <uses-permission android:name="android.permission.CALL_PHONE" > </uses-permission> <uses-permission android:name="android.permission.CAMERA" > </uses-permission> <uses-permission android:name="android.permission.READ_CONTACTS" > </uses-permission> <uses-permission android:name="android.permission.INTERNET"/> <application android:icon="@drawable/icon" android:label="@string/app_name" > <activity android:name=".CallIntentsActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Change your activity class to the following code.
package de.vogella.android.intent.implicit; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.Toast; public class CallIntentsActivity extends Activity { private Spinner spinner;/** Called when the activity is first created. */@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); spinner = (Spinner) findViewById(R.id.spinner); ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.intents, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); } public void onClick(View view) { int position = spinner.getSelectedItemPosition(); Intent intent = null; switch (position) { case 0: intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.vogella.com")); break; case 1: intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:(+49)12345789")); break; case 2: intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:(+49)12345789")); startActivity(intent); break; case 3: intent = new Intent(Intent.ACTION_VIEW, Uri.parse("geo:50.123,7.1434?z=19")); break; case 4: intent = new Intent(Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=query")); break; case 5: intent = new Intent("android.media.action.IMAGE_CAPTURE"); break; case 6: intent = new Intent(Intent.ACTION_VIEW, Uri.parse("content://contacts/people/")); break; case 7: intent = new Intent(Intent.ACTION_EDIT, Uri.parse("content://contacts/people/1")); break; } if (intent != null) { startActivity(intent); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK && requestCode == 0) { String result = data.toURI(); Toast.makeText(this, result, Toast.LENGTH_LONG); } } }
If you start your application you see an list of buttons and if you press one of the buttons, your defined activities are started.
Note that you didn't specify any receiving application only the thing that should be done. This allows you to define loosely coupled tasks which use components of different applications.
In the following exercise you register an
activity
as browser, i.e.
for the
intent
which is triggered if someone want to view an URL starting with http.
The example activity downloads the HTML source of this page and
display
this
in
a
TextView.
Create the
Android project called
de.vogella.android.intent.browserfilter
with the
activity
called
BrowserActivity.
Register your
activity
to
Intent.Action_VIEW
action and the scheme "http" via the following changes in your
AndroidManifest.xml
file
The manifest also declares the permission to access the
Internet.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.intent.browserfilter" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <uses-permission android:name="android.permission.INTERNET" > </uses-permission> <application android:icon="@drawable/icon" android:label="@string/app_name" > <activity android:name=".BrowserActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="http" /> </intent-filter> </activity> </application> </manifest>
Change corresponding layout file to the following.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/textView"/> </LinearLayout>
Change your activity class to the following code.
package de.vogella.android.intent.browserfilter; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.StrictMode; import android.widget.TextView; public class BrowserActivity extends Activity {/** Called when the activity is first created. */@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // To keep this example simple, we allow network access // in the user interface thread StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder() .permitAll().build(); StrictMode.setThreadPolicy(policy); setContentView(R.layout.main); Intent intent = getIntent(); TextView text = (TextView) findViewById(R.id.textView); // To get the action of the intent use String action = intent.getAction(); if (!action.equals(Intent.ACTION_VIEW)) { throw new RuntimeException("Should not happen"); } // To get the data use Uri data = intent.getData(); URL url; try { url = new URL(data.getScheme(), data.getHost(), data.getPath()); BufferedReader rd = new BufferedReader(new InputStreamReader(url.openStream())); String line = ""; while ((line = rd.readLine()) != null) { text.append(line); } } catch (Exception e) { e.printStackTrace(); } } }
Install your application. If you now trigger an intent to open an URL your should be able to select your own component. You can for example trigger this intent via the example from the implicit tutorials.
If you select you component the HTML code is loaded into your text view.


The following example shows how to pick an image from any registered photo application on Android via an intent.
Create a new Android project called de.vogella.android.imagepick with one activity called ImagePickActivity.
Change the
activity_main.xml
layout file to the following.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="pickImage" android:text="Button" > </Button> <ImageView android:id="@+id/result" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/icon" > </ImageView> </LinearLayout>
Change your activity class according to the following coding.
package de.vogella.android.imagepick; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.View; import android.widget.ImageView; public class ImagePickActivity extends Activity { private static final int REQUEST_CODE = 1; private Bitmap bitmap; private ImageView imageView;/** Called when the activity is first created. */@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); imageView = (ImageView) findViewById(R.id.result); } public void pickImage(View View) { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(intent, REQUEST_CODE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { InputStream stream = null; if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) try { // We need to recyle unused bitmaps if (bitmap != null) { bitmap.recycle(); } stream = getContentResolver().openInputStream(data.getData()); bitmap = BitmapFactory.decodeStream(stream); imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (stream != null) try { stream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
If you run this application you can select an image from your image
library on your Android phone and assign it to your
ImageView.
Before posting questions, please see the vogella FAQ. If you have questions or find an error in this article please use the www.vogella.com Google Group. I have created a short list how to create good questions which might also help you.
vogella Training Android and Eclipse Training from the vogella team
Android Tutorial Introduction to Android Programming
GWT Tutorial Program in Java and compile to JavaScript and HTML
Eclipse RCP Tutorial Create native applications in Java
JUnit Tutorial Test your application
Git Tutorial Put everything you have under distributed version control system