您的位置:首页 > 其它

Intents and Intent Filters

2017-02-16 15:27 211 查看

Intents and Intent Filters

https://developer.android.com/guide/components/intents-filters.html#Types

Intent types

  Explicit intents:直接指定完整类名,系统接收到请求后会立即启动组件;

  Implicit intents:不直接指定类名,而是声明一个要执行的action,当系统接收到请求后,会将Intent与系统中所有manifest.xml声明的intent-filters进行比较,找到合适的组件,如果有多个组件匹配,系统会弹出dialog提示用户选择。

  为了保证app的安全性,不要使用隐式Intent启动Service,也不要在App中给Service定义intent-filters,因为无法确定系统中哪些Service会响应启动Intent。

Building an intent

  Intent包含的信息描述了系统应该启动哪个组件,同时包含接收组件可以使用的数据。主要包含的信息如下:

  Component name:manifest.xml中显式指定组件类名,Intent中使用setComponent(), setClass(), setClassName()或者构造器指定;

  Action:定义组件可以执行的动作,为系统中的其他组件提供服务,系统已经定义了一些标准的ACTION名称,如展示数据的ACTION_VIEW(android.intent.action.VIEW)、组件间共享数据的ACTION_SEND(android.intent.action.SEND)等,应用声明组件时可以直接使用这些定义。如果要定义自己的action,需要使用包名作为前缀,例如:static final String ACTION_TIMETRAVEL = “com.example.action.TIMETRAVEL”;

  Data:设置目标组件需要的数据URI,可以从ContentProvider中读取。setData()和setType会将对方设置为null,需要互斥使用,同时设置data和type需要使用setDataAndType()这个API。Intent匹配组件时会也会比较
<data/>


  Category:指定组件的类型,比如CATEGORY_BROWSABLE表示组件可以被浏览器启动,CATEGORY_LAUNCHER表示组件是应用的启动入口,所以会被添加在系统的应用启动列表中。组件可以定义任意多个Category,调用者通过addCategory() API增加category。调用者构造Intent的时候,系统会附加检查android.intent.category.DEFAULT这个category,所以允许使用隐式intent启动的activity必须添加这个category。

官方原文:Note: To receive implicit intents, you must include the CATEGORY_DEFAULT category in the intent filter. The methods startActivity() and startActivityForResult() treat all intents as if they declared the CATEGORY_DEFAULT category. If you do not declare this category in your intent filter, no implicit intents will resolve to your activity.


  Extras:保存附加的K-V对数据,putExtra()/putExtras();

  Flags:作为intent的元数据,标识系统应该怎样启动activity、启动后怎么管理等。

  隐式Intent需要匹配Action、Data、Category三个
<intent-filters/>
,其中Category可以设置>=0个,实际测试是这样的,和官方文档的说法一致。Action可以设置>=0个,Data必须匹配。


Example explicit intent

  显式的Intent只需要指定目标组件的类名,其他的参数都是可选的,不存在找不到组件的问题。

Example implicit intent

  隐式的Intent通过action和category指定目标组件,调用startActivity()时,系统会查找所有已安装应用的组件,如果能找到一个唯一匹配的组件就直接启动;如果有多个满足条件就会通过弹框提示用户选择;如果找不到任何目标组件,应用会直接crash掉,为了避免这个问题,startActivity()使用隐式intent启动组件前,需要调用Intent#resolveActivity(PackageManager)检查组件是否存在,返回非空表示存在可用组件,否则不存在,应该放弃启动操作。

Forcing an app chooser

  如果有多个应用能响应Intent请求,使用Intent.createChooser()启动选择页面,如果只有一个应用能响应,系统会直接启动这个应用。

Intent sendIntent = new Intent(Intent.ACTION_SEND);
Intent choose = Intent.createChooser(sendIntent, "Choose Application");
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(choose);
}


Receiving an implicit intent

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>
<!-- 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>
<intent-filter>
<action>...</action>
</intent-filter>
</activity>


  The
ACTION_MAIN
action indicates this is the main entry point and does not expect any intent data. The
CATEGORY_LAUNCHER
category indicates that this activity’s icon should be placed in the
system's app launcher
. If the element does not specify an icon with icon, then the system uses the icon from the element. These two must be paired together in order for the activity to appear in the app launcher.

  Intent需要匹配一组完整的
<intent-filter/>
才能启动对应的组件。

Using a pending intent

  PendingIntent是对Intent对象的包装,主要目的是授权外部应用使用PendingIntent内包含的Intent,主要用于NotificationManager执行通知事件、桌面的App Widget执行Intent、系统的AlarmManager执行Intent。主程序向系统服务发送PendingIntent等待,事件被触发时会执行PendingIntent中包含的真正Intent。

  Intent和PendingIntent的区别

Intent是立即使用的,而PendingIntent可以等到事件发生后触发,PendingIntent可以cancel

Intent在程序结束后即终止,而PendingIntent在程序结束后依然有效

PendingIntent自带Context,而Intent需要在某个Context内运行

Intent在原task中运行,PendingIntent在新的task中运行

  创建PendingIntent主要使用下面3个API,用于不同的目的:

PendingIntent.getActivity() for an Intent that starts an Activity.

PendingIntent.getService() for an Intent that starts a Service.

PendingIntent.getBroadcast() for a Intent that starts an BroadcastReceiver.

  通知栏使用PendingIntent实例:

// 向通知栏发送消息,执行Intent
Intent openIntent = new Intent(MainActivity.this, IntentActivity2.class);
PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0, openIntent, PendingIntent.FLAG_CANCEL_CURRENT);

NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
int icon = android.R.drawable.stat_notify_chat;
long when = System.currentTimeMillis() + 2000;
Notification n = new Notification.Builder(MainActivity.this)
.setContentIntent(pi)
.setDefaults(Notification.DEFAULT_SOUND)
.setTicker("Ticker")
.setContentTitle("ContentTitle")
.setContentText("ContentText")
.setSubText("SubText")
.setWhen(when)
.setSmallIcon(icon)
.build();

nm.notify(0, n);


Intent resolution

  当系统接收到一个隐式Intent的时候,会从系统中找到最佳匹配的组件,匹配项包括Action、Data(URI+Type)和Category。

  Action Test规则:组件可以声明[0, n]个
<action/>
,如果组件没有声明action,所有的Intent都将匹配失败,如果是Intent没有设置action,只要组件至少声明了一个action,Action匹配都将通过;

  Category Test规则:组件可以声明[0, n]个
<category/>
,只要Intent中设置的category都包含在组件的category列表中,就可以匹配通过,不需要全部都设置,基于这个规则如果Intent中不包含任何category,匹配也可以通过;

  调用startActivity()和startActivityForResult()启动Activity时,如果入参是隐式Intent,系统默认会添加CATEGORY_DEFAULT的category,如果目标Activity必须包含
android.intent.category.DEFAULT
才能匹配启动。

  Data Test规则:组件可以声明[0, n]个
<data/>
,完整的格式
<scheme>://<host>:<port>/<path>
,这部分匹配规则比较复杂,可以直接看文档原文:

URI线性依赖规则:
Each of these attributes is optional in a <data> element, but there are linear dependencies:
1. If a scheme is not specified, the host is ignored.
2. If a host is not specified, the port is ignored.
3. If both the scheme and host are not specified, the path is ignored.

URI最小匹配规则:
When the URI in an intent is compared to a URI specification in a filter, it's compared only to the parts of the URI included in the filter. For example:
1. If a filter specifies only a scheme, all URIs with that scheme match the filter.
2. If a filter specifies a scheme and an authority but no path, all URIs with the same scheme and authority pass the filter, regardless of their paths.
3. If a filter specifies a scheme, an authority, and a path, only URIs with the same scheme, authority, and path pass the filter.

Note: A path specification can contain a wildcard asterisk (*) to require only a partial match of the path name.

URI和MIME匹配规则:
The data test compares both the URI and the MIME type in the intent to a URI and MIME type specified in the filter. The rules are as follows:

An intent that contains neither a URI nor a MIME type passes the test only if the filter does not specify any URIs or MIME types.
An intent that contains a URI but no MIME type (neither explicit nor inferable from the URI) passes the test only if its URI matches the filter's URI format and the filter likewise does not specify a MIME type.
An intent that contains a MIME type but not a URI passes the test only if the filter lists the same MIME type and does not specify a URI format.
An intent that contains both a URI and a MIME type (either explicit or inferable from the URI) passes the MIME type part of the test only if that type matches a type listed in the filter. It passes the URI part of the test either if its URI matches a URI in the filter or if it has a content: or file: URI and the filter does not specify a URI. In other words, a component is presumed to support content: and file: data if its filter lists only a MIME type.

This last rule, rule (d), reflects the expectation that components are able to get local data from a file or content provider. Therefore, their filters can list just a data type and don't need to explicitly name the content: and file: schemes. The following example shows a typical case in which a <data> element tells Android that the component can get image data from a content provider and display it:

<intent-filter>
<data android:mimeType="image/*" />
...
</intent-filter>

**Filters that specify a data type but not a URI are perhaps the most common because most available data is dispensed by content providers.**

Another common configuration is a filter with a scheme and a data type. For example, a <data> element like the following tells Android that the component can retrieve video data from the network in order to perform the action:

<intent-filter>
<data android:scheme="http" android:type="video/*" />
...
</intent-filter>


Intent matching

  Intent匹配不仅用于找到并激活某个组件,还可以用于查询系统中的组件集合,桌面程序通过给Intent指定ACTION_MAIN和CATEGORY_LAUNCHER,找到所有应用的启动入口,任意一个程序都可以做类似的查询。

  PackageManager这个代理类提供了一组查询方法query…(),返回满足Intent描述的目标组件列表,包括Activity、Service、BroadcastReceiver:

queryIntentActivities(Intent, flag)

queryIntentService(Intent, flag)

queryBroadcastReceivers(Intent, flag)



  PackageManager这个代理类还提供了一组解析Intent的方法resolve…(),返回一个最符合条件的组件,匹配规则查看方法的Doc注释。

  应用程序查询系统组件实例:

PackageManager pm = getPackageManager();
Intent actIntent = new Intent("android.intent.action.MAIN");
actIntent.addCategory("android.intent.category.LAUNCHER");
List<ResolveInfo> acts = pm.queryIntentActivities(actIntent, 0);
for (ResolveInfo act : acts) {
Log.i(TAG, "Activity: " + act.activityInfo.name);
}

Intent srvIntent = new Intent("com.codesky.aidlgo.action.aidl.SERVER");
List<ResolveInfo> srvs = pm.queryIntentServices(srvIntent, 0);
for (ResolveInfo srv : srvs) {
Log.i(TAG, "Service: " + srv.serviceInfo.name);
}


通用的Intent

Alaram Clock

Create an alarm: ACTION_SET_ALARM 这是闹钟

<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />


Create a timer: ACTION_SET_TIMER 设置定时器

<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />


Show all alarms: ACTION_SHOW_ALARMS 系统时钟应用显示所有闹钟

Calendar

Add a calendar event: ACTION_INSERT

Camera

Capture a picture or video and return it: ACTION_IMAGE_CAPTURE/ACTION_VIDEO_CAPTUR

Start a camera app in still image mode: INTENT_ACTION_STILL_IMAGE_CAMERA

Start a camera app in video mode: INTENT_ACTION_VIDEO_CAMERA

Contacts/People App

Select a contact: ACTION_PICK 选择联系人

Select specific contact data: ACTION_PICK 选择联系人号码、邮件等

View a contact: ACTION_VIEW 查看某个联系人的详细页面

Edit an existing contact: ACTION_EDIT 编辑联系人

Insert a contact: ACTION_INSERT

Email

Compose an email with optional attachments: ACTION_SENDTO/ACTION_SEND/ACTION_SEND_MULTIPLE

File Storage

Retrieve a specific type of file: ACTION_GET_CONTENT

Open a specific type of file: ACTION_OPEN_DOCUMENT/ACTION_CREATE_DOCUMENT

Local Actions

Call a car: ACTION_RESERVE_TAXI_RESERVATION

Maps

Show a location on a map: ACTION_VIEW

Music or Video

Play a media file: ACTION_VIEW

Play music based on a search query: INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH

New Note

Create a note: ACTION_CREATE_NOTE

Phone

Initiate a phone call: ACTION_DIAL/ACTION_CALL

Search

Search using a specific app: “com.google.android.gms.actions.SEARCH_ACTION”

Perform a web search: ACTION_WEB_SEARCH

Settings

Open a specific section of Settings: 设置的具体子页面 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

Text Messaging

Compose an SMS/MMS message with attachment: ACTION_SENDTO/ACTION_SEND/ACTION_SEND_MULTIPLE

Web Browser

Load a web URL: ACTION_VIEW

Verify Intents with the Android Debug Bridge

使用ADB测试自己的应用能够响应指定的Intent

adb shell am start -a <ACTION> -t <MIME_TYPE> -d <DATA> \
-e <EXTRA_NAME> <EXTRA_VALUE> -n <ACTIVITY>

例如:
adb shell am start -a android.intent.action.DIAL \
-d tel:555-5555 -n org.example.MyApp/.MyActivity
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: