Android
Intents
Overview
Intents
are asynchronous messages
which allow Android components to request functionality from other
components of the Android system. For example an Activity
can send an Intents
to the Android
system which starts another Activity
.
Therefore
Intents
allow to combine
loosely coupled components to perform certain tasks.
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 and will get notified.
Intents
are instances of the
android.content.Intent
class.
Intents
are send to the Android
system. Depending on how the Intent
was
constructed the Android system will run an receiver determination and
determine what to do.
An
Intent
can also contain data. This
data can be used by the receiving component. For example your
application can calls via an Intent
a
browser component. As data is it may send the URL to the browser
component.
Android supports explicit and implicit
Intents
.
Explicit Intents
ExplicitIntents
explicitly names the
component which should be called by the Android system, by using the
Java class as identifier.
The following shows an explicit
Intent
.
If that Intent
is correctly send to the
Android system, it will start the associated class.
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
Intent
contains the Action and optional the URI. The receiving component can
get this information via the getAction()
and getData()
methods.
Explicit and implicit
Intents
can
also contain additional data. This data call be filled by the
component which creates the Intent
. It
can and can get extracted by the component which receives the Intent
.
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,..), String,
Bundle, Parceable and Serializable.
For example you can trigger all components which have been registered to send some data via the
new
Intent(Intent.ACTION_SEND)
This Intent
determines possible receivers via the type. What is send it defined
via the putExtra
method. You can use any
String as key, the following uses the keys which are predefined for
the ACTION_SEND intent.
Intent sharingIntent = new Intent(Intent.ACTION_SEND); sharingIntent.setType("text/plain"); sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, "News for you!"); // createChooser is a convenience method to create // an Chooser Intent with a Title startActivity(Intent.createChooser(sharingIntent,"Share this using"));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 }Three of the core components of an application — activities, services, and broadcast receivers — are activated through messages, called intents. Intent messaging is a facility for late run-time binding between components in the same or different applications. The intent itself, an
Intent
object, is a passive data structure holding an abstract description
of an operation to be performed — or, often in the case of
broadcasts, a description of something that has happened and is being
announced. There are separate mechanisms for delivering intents to
each type of component:
- An Intent object is passed to
Context.startActivity()
orActivity.startActivityForResult()
to launch an activity or get an existing activity to do something new. (It can also be passed toActivity.setResult()
to return information to the activity that calledstartActivityForResult()
.) - An Intent object is passed to
Context.startService()
to initiate a service or deliver new instructions to an ongoing service. Similarly, an intent can be passed toContext.bindService()
to establish a connection between the calling component and a target service. It can optionally initiate the service if it's not already running.
- Intent objects passed to any of the broadcast methods (such
as
Context.sendBroadcast()
,Context.sendOrderedBroadcast()
, orContext.sendStickyBroadcast()
) are delivered to all interested broadcast receivers. Many kinds of broadcasts originate in system code.
startActivity()
is delivered only to an
activity, never to a service or broadcast receiver, and so on.
Defining Intent Filters
The following 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>This is an example how you could define an Intent receiver for the ACTION.SEND Intent.
<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
Intent
filters, it can only be called by explicit Intents
.
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>
Restrictions as of Android 3.1
As of Android 3.1 the Android system will per default exclude allBroadcastReceiver
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).
This is an additional security features as the user can be sure that only the applications he started will receive broadcast
Intents
.
Intents as event triggers
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).
Share Intent and ShareActionProvider
As of Android 4.0 you can also add an Action Provider to your ActionBar which allows to share. For this you have to define a special menu entry and assign anIntent
which contain the sharing data to it in your Activity
.
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/menu_share" android:title="Share" android:showAsAction="ifRoom" android:actionProviderClass="android.widget.ShareActionProvider" /> <item android:id="@+id/item1" android:showAsAction="ifRoom" android:title="More entries..."> </item> </menu> @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.mymenu, menu); // provider is a field in your Activity provider = (ShareActionProvider) menu.findItem(R.id.menu_share) .getActionProvider(); setShareIntent(); return true; } public void setShareIntent() { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TEXT, "Message"); provider.setShareIntent(intent); }Unfortunately this does not seem to work in the Android emulator, see Bug report for details .
Finding out if an Intent is available
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 checking the
PackageManager
.
The following code checks if an Intent
exists. You can check via this method for Intent
and change your application behavior accordingly for example disable
or hide menu items.
public boolean isIntentAvailable(Context context, String action) { final PackageManager packageManager = context.getPackageManager(); final Intent intent = new Intent(action); List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); if (resolveInfo.size() > 0) { return true; } return false; }
Explicit intents and data transfer between activities
The following tutorial demonstrates how to use explicitIntents
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="fill_parent" android:layout_height="fill_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 "second.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
.
Implicit Intents
The following tutorial demonstrates the usage of implicitintents
of the Android System.
Create a new Android application "de.vogella.android.intent.implicit" with a
Activity
called " CallIntentsActivity
".
In this example we will use a
Spinner
to select the Intent
which should get
triggered. For the content of the Spinner
we will 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 "main.xml" to the following.
<?xml version="1.0" encoding="utf-8"?> <GridLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_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 then for your application. Maintain the following "AndroidManifest.xml".
<?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 to the following. We will start the new intent with the method startActivityForResult() which allow us to specify a desired result code. Once the intent is finished the method onActivityResult() is called and you can perform actions based on the result of the activity.
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.de")); 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 should see an list of buttons and if you press the button, different activities should be performed. Note that you didn't specify any receiving application only the thing that should be done.
Registering an IntentFilter
The following example will register aActivity
for the Intent
which is triggered to
view a http webpage.
Our application downloads the HTML source of this page and display this in a
TextView
.
Create the Android project "de.vogella.android.intent. " with the activity "BrowserActivitiy". Register your
Activity
to the Intent
via the Intent.Action_VIEW
action and
the scheme "http" via the following "AndroidManifest.xml".
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 "main.xml" 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="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/textView"/> </LinearLayout>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.
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 != 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(); } } }If you select you component the HTML code should be loaded into your text view.
No comments:
Post a Comment