1. Intents 和Intent Filters
- to start an Activity
startActivity or startActivityForResults
- to start a service
startService or bindService
- to deliever a broadcast
sendBroadcast, sendOrderedBroadcast or sendStickyBroadcast
1.1 Intent Types
- Explicit intents
指定请求组件的全限定名,就包名+类名。 - Implicit intents
申明通用的动作去执行,允许其他App处理该动作。
1.2 Building Intent
- Component name
是决定该Intent是否为Explicit Intent,可通过set Component(), setClass(), setClassName()
, 或者是作为Intent()的构造器的参数。若没指定该参数,则系统使用一下item进行匹配。其中Service必须指定Component Name。 - Action
是一个String字符串,指定通用动作去执行,如View or Pick。该字段通常能够决定Intent的其他字段如何构造,尤其是在data域和extras域。可自定义Action,但更推荐使用系统提供的。以下为常用的Action, ACTION_VIEW(使用地图显示地址或使用相册显示图片),ACTION_SEND(使用其他App共享数据,诸如电子邮件或者社交网络共享).
可以通过setAction(),或者Intent的构造器进行指定。定义自己的ACTION,请加上包名,如下static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
- Data
使用URI指定MIME类型的数据,通常需要指定数据类型。但是使用content: URI
的URI数据,可不指定类型,因为系统可识别出该数据位于设备上,并且受到ContentProvider控制,因此MIME类型就变为可见。
指定方式为setData(), setType() or setDataAndType()
,其中setData和setType不可同时使用,若需要,请使用setDataAndType()。 - Category
一个String,包含可处理该Intent的组件种类。每个Intent可包含任意数量的Category,但大部分的Intent都不需要,通常设置为android.intent.category.DEFAULT
。常见的Category:CATEGORY_BROWSABLE
和CATEGORY_LAUNCHER
。前者表示该数据可为浏览器打开,后者表示该Activity是一个任务的初始Activity,并且将会被显示在Launcher里。可用'addCategory()'来指定该项。
>Tip 以上介绍的component name, action, data, 和category限定了一个intent的特性,Android系统可知道如何决定一个组件来处理或启动。 - Extras
携带键值对信息,KV来完成特定的Action。可通过putExtras(K, V),或者创建一个Bundle携带所有的KVP,然后putExtras到Intent。指定自己的Extras Key,务必加上包名,如static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
- FLAG
定义Intent的元素据metadata,可指导Android系统如何启动一个Activity(task所属),或者启动后如何对待(是否属于其他已经启动的Activity)
1.2 Examples
- Explicit Intent
// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
- Implicit Intent
模糊的Intent,可能会没有App响应,导致App Crash。因此可通过resolveActitvity()来验证是否有Activity响应,然后再决定是否启动Activity。
// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType(HTTP.PLAIN_TEXT_TYPE); // "text/plain" MIME type
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
若有多个App响应,则显示列表对话框以供用户选择,如果只有一个App响应,则直接启动。
- 强制出现App选择器
如分享文件或者数据到各种App,ACTION_SEND。可使用Intent的creatChooser来操作,如下:
Intent intent = new Intent(Intent.ACTION_SEND);
...
// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, title);
// Verify the intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
1.3 Receiving an Implicit Intent
在Manifest文件中申明< intent-filters >标签,通过action, data, and category等字段制定Intent可接受的类型。
一个Explicit Intent无视组件所申明的接受Intent类型,直接传递过去。
不同job应申明不同的< intent-filters >,可包括action, data, and category3个字段。
-action, 指定可接受的intetn action。
-data,可接受的数据类型,URI (scheme, host, port, path, etc.) and MIME type。
-category,必须是一个Action的string值。
Tip: 为了收到Implicit Intents,必须设定
CATEGORY_DEFAULT
。这样, startActivity() and startActivityForResult() 才可调用,否则没有任何Implicit可启动该Activity。
举例:
<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
只有三个item都通过验证,Android才会传递Intent给该Activity。对于Broadcast Receiver,可通过registerReceiver()
or unregisterReceiver()
动态绑定。
- Example Filters
<activity android:name="MainActivity">
<!-- This activity is the main entry, should appear in app launcher -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ShareActivity">
<!-- 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="application/vnd.google.panorama360+jpg"/>
<data android:mimeType="image/*"/>
<data android:mimeType="video/*"/>
</intent-filter>
</activity>
1.4 Using a Pending Intent
它是对Intent的包装器,主要用途是给予其他App权限获取本进程的数据。主要用途如下:
- Notification Manager
- AppWidget
- AlarmManager
由于每个Intent都是为特定的组件设计的,因此PendingIntent也同样。
- PendingIntent.getActivity(),启动Activtiy
- PendingIntent.getService(),启动Service
- PendingIntent.getBroadcast(), 启动BroadcastReceiver()。
1.5 Intent Resolution
Android 系统匹配最优的组件的算法,基于以下三方面的匹配测试:
- The intent action
- The intent data (both URI and data type)
- The intent category
其中data的URI 结构和MIME 类型。
URI:<scheme>://<host>:<port>/<path>
ex:content://com.example.project:200/folder/subfolder/etc
这4个元素是线性依赖的:
>If a scheme is not specified, the host is ignored.
If a host is not specified, the port is ignored.
If both the scheme and host are not specified, the path is ignored.
PackageManager 可以调用以下几个方法列出可接受该Intent的组件
- queryIntentActivities(), 返回所有相似的Activity
- queryIntentServices(),返回相似的所有Service
- queryIntentBroadcastReceivers(), 返回所有
1.6 Common Intents
1 AlarmClock
Action: ACTION_SET_ALARM
Extras:....
ex:
public void createAlarm(String message, int hour, int minutes) {
Intent intent = new Intent(AlarmClock.ACTION_SET_ALARM)
.putExtra(AlarmClock.EXTRA_MESSAGE, message)
.putExtra(AlarmClock.EXTRA_HOUR, hour)
.putExtra(AlarmClock.EXTRA_MINUTES, minutes);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
并其需要使用权限:<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
相应的Intent-Filter
<activity ...>
<intent-filter>
<action android:name="android.intent.action.SET_ALARM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
2 Timer
Action:ACTION_SET_TIMER
Extras:。。。
e.g.
public void startTimer(String message, int seconds) {
Intent intent = new Intent(AlarmClock.ACTION_SET_TIMER)
.putExtra(AlarmClock.EXTRA_MESSAGE, message)
.putExtra(AlarmClock.EXTRA_LENGTH, seconds)
.putExtra(AlarmClock.EXTRA_SKIP_UI, true);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
Permission:<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
Intent-Filter:
<activity ...>
<intent-filter>
<action android:name="android.intent.action.SET_TIMER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
3 显示所有Alarms
Action:ACTION_SHOW_ALARMS
4 添加Calendar Event
Action:ACTION_INSERT
Data URI:Events.CONTENT_URI
MIME Type: "vnd.android.cursor.dir/event"
Extras: 。。。
4 拍摄照片或摄制录像
Action:ACTION_IMAGE_CAPTURE or ACTION_VIDEO_CAPTURE
Extras:EXTRA_OUTPUT
使用onActivityResult()
接收返回数据:
static final int REQUEST_IMAGE_CAPTURE = 1;
static final Uri mLocationForPhotos;
public void capturePhoto(String targetFilename) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.withAppendedPath(mLocationForPhotos, targetFilename);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bitmap thumbnail = data.getParcelable("data");
// Do other work with full size photo saved in mLocationForPhotos
...
}
}
相应的Intent-Filter
<activity ...>
<intent-filter>
<action android:name="android.media.action.IMAGE_CAPTURE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
5 Start a camera app in still image mode
Action: INTENT_ACTION_STILL_IMAGE_CAMERA
6 Start a camera app in video mode
Action: INTENT_ACTION_VIDEO_CAMERA
7 选择一个联系人
Action:ACTION_PICK
MIME Type:Contacts.CONTENT_TYPE
e.g.:
static final int REQUEST_SELECT_CONTACT = 1;
public void selectContact() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_SELECT_CONTACT);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SELECT_CONTACT && resultCode == RESULT_OK) {
Uri contactUri = data.getData();
// Do something with the selected contact at contactUri
...
}
}
8 选择特定的联系人
Action: ACTION_PICK
MIME Type:
CommonDataKinds.Phone.CONTENT_TYPE。Pick from contacts with a phone number.
CommonDataKinds.Email.CONTENT_TYPE,Pick from contacts with an email address.
CommonDataKinds.StructuredPostal.CONTENT_TYPE,Pick from contacts with a postal address.
static final int REQUEST_SELECT_PHONE_NUMBER = 1;
public void selectContact() {
// Start an activity for the user to pick a phone number from contacts
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(CommonDataKinds.Phone.CONTENT_TYPE);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SELECT_PHONE_NUMBER && resultCode == RESULT_OK) {
// Get the URI and query the content provider for the phone number
Uri contactUri = data.getData();
String[] projection = new String[]{CommonDataKinds.Phone.NUMBER};
Cursor cursor = getContentResolver().query(contactUri, projection,
null, null, null);
// If the cursor returned is valid, get the phone number
if (cursor != null && cursor.moveToFirst()) {
int numberIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER);
String number = cursor.getString(numberIndex);
// Do something with the phone number
...
}
}
}
9 显示一个联系人
- ACTION_PICK返回的contactURI,然后使用ACTION_VIEW查看。
- 直接访问联系人列表,需要READ_CONTACTS权限
public void viewContact(Uri contactUri) {
Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
10 编辑一个联系人
Action:ACTION_EDIT
URI: 之前ACTION_PICK返回的contactURI。
Extras:ContactsContract.Intents.Insert中定义的条目都可编辑
public void editContact(Uri contactUri, String email) {
Intent intent = new Intent(Intent.ACTION_EDIT);
intent.setData(contactUri);
intent.putExtra(Intents.Insert.EMAIL, email);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
11 插入一个联系人
Action:ACTION_INSERT
MIME Type:Contacts.CONTENT_TYPE
Extras:One or more of the extras defined in ContactsContract.Intents.Insert.
e.g.:
public void insertContact(String name, String email) {
Intent intent = new Intent(Intent.ACTION_INSERT);
intent.setType(Contacts.CONTENT_TYPE);
intent.putExtra(Intents.Insert.NAME, name);
intent.putExtra(Intents.Insert.EMAIL, email);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
12 Email
13 File Storage
14 Fitness
15 Local Actions
16 Maps, show a location on a map
Action: ACTION_VIEW
URI: geo:latitude,longtitude
e.g.:
public void showMap(Uri geoLocation) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(geoLocation);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
intent filter:
<activity ...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="geo" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
17 MUSIC or VIDEO
Action: ACTION_VIEW
Data URI Scheme:
- file:
- content:
- http:
MIME Type:
- "audio/*"
- "application/ogg"
- "application/x-ogg"
- "application/itunes" Or any other that your app may require.
example intent filter:
<activity ...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:type="audio/*" />
<data android:type="application/ogg" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
18 Phone
Action: ACTION_DIAL , ACTION_CALL
其中ACTION_DIAL,需要传输一个号码,然后跳转到拨打界面,需要点击拨打按钮。
而ACTION_CALL,需要传输一个号码,然后直接开始拨打,但是需要权限<uses-permission android:name="android.permission.CALL_PHONE" />
Data URI Scheme:'tel:< phone-number>,voicemail:< phone-number>'
e.g.:
public void dialPhoneNumber(String phoneNumber) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + phoneNumber));
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
19 Settings
Action:
- ACTION_SETTINGS ACTION_WIRELESS_SETTINGS
- ACTION_AIRPLANE_MODE_SETTINGS ACTION_WIFI_SETTINGS
- ACTION_APN_SETTINGS ACTION_BLUETOOTH_SETTINGS ACTION_DATE_SETTINGS
- ACTION_LOCALE_SETTINGS ACTION_INPUT_METHOD_SETTINGS
- ACTION_DISPLAY_SETTINGS ACTION_SECURITY_SETTINGS
- ACTION_LOCATION_SOURCE_SETTINGS ACTION_INTERNAL_STORAGE_SETTINGS
- ACTION_MEMORY_CARD_SETTINGS
20 Text Messaging
Action: ACTION_SENDTO, ACTION_SEND, ACTION_SEND_MULTIPLE
Data URI Scheme:
sms:<phone_number>
smsto:<phone_number>
mms:<phone_number>
mmsto:<phone_number>
MIME Type:
PLAIN_TEXT_TYPE ("text/plain")
"image/"
"video/"
Extras:
"subject",'sms_body','EXTRA_STREAM'
e.g.:
public void composeMmsMessage(String message, Uri attachment) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setType(HTTP.PLAIN_TEXT_TYPE);
intent.putExtra("sms_body", message);
intent.putExtra(Intent.EXTRA_STREAM, attachment);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
若想要确保intent被短信应用处理,使用 ACTION_SENDTO,并且设置data schmem为‘smsto:’.
相应的Intent-filter
<activity ...>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:type="text/plain" />
<data android:type="image/*" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
21. WebBrowser
加载网址
- Action: ACTION_VIEW
- Data URI Scheme:
http:< URL>
https:< URL>
- MIME Type:
PLAIN_TEXT_TYPE ("text/plain")
"text/html"
"application/xhtml+xml"
"application/vnd.wap.xhtml+xml"
Web查询
Action: ACTION_WEB_SEARCH
Extras:SearchManager.QUERY The search string.
public void searchWeb(String query) {
Intent intent = new Intent(Intent.ACTION_SEARCH);
intent.putExtra(SearchManager.QUERY, query);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
22 Verify Intents with the Android Debug Bridge
- Set up an Android device for development, or use a virtual device.
- Install a version of your app that handles the intents you want to support.
- Fire an intent using adb:
adb shell am start -a <ACTION> -t <MIME_TYPE> -d <DATA> \ -e <EXTRA_NAME> <EXTRA_VALUE> -n <ACTIVITY>
For example:adb shell am start -a android.intent.action.DIAL \ -d tel:555-5555 -n org.example.MyApp/.MyActivity
If you defined the required intent filters, your app should handle the intent.