一个Intent是一个消息对象,可以用来从另一个组件请求一个功能(action)。虽然Intents促进组件之间的交流有多重方式,有三种基本的:
•
To start an activity:
一个活动在一个app中代表一个单独的屏幕。可以通过传递一个Intent给startActivity启动一个活动的实例。这个Intent描述了活动如何启动并且携带了必要的数据
如果想在活动结束的时候接收一个结果,调用startActivityForResult.活动接收返回的结果作为一个独立的Intent对象在活动的onActivityResult回调方法中。这个在活动的文档有demo,不过是用的ContentProvide的示例,还是能用到的。
•
To start a service:
服务是一个组件,在后台执行动作,没有用户界面。(和活动相比,就是没有界面,那是不是比活动简单?)。可以传递一个Intent给startService启动一个服务执行一次性动作(比如下载文件)。Intent描述活动如何启动,携带任何需要的数据。
如果服务被设计为一个客户端服务界面,可以通过传递Intent给bindService从另一个组件绑定到服务(是服务绑定到组件上,还是组件绑定到服务上,service中学习)
•
To deliver a broadcast:
广播是一个任何app能接收的消息。系统根据系统事件发布不同的广播,比如当系统启动后者设备充电。可以发布一个广播给其他app通过传递一个Intent给sendBroadcast, sendOrderedBroadcast(), or sendStickyBroadcast().(这么多,广播的文档时再说)
Intent 类型
两种类型
•
Explicit intents
显式意图,通过名字(全路径类名)指定启动的组件。通过在自己的程序使用显式意图指定启动的组件,因为我们知道我们想启动的活动或服务的名词。举例,启动一个活动响应用户的动作或者启动一个服务在后台下载文件。
•
Implicit intents
隐式意图不为一个特定的组件命名,而是声明一个要执行的一般任务,允许其他应用的组件处理他。举例,我们想在地图展示用户的位置,可以使用一个隐式意图请求另一个可用的app在地图上展示一个特定的位置(调用第三方呗)。
当我们指定一个现实意图启动一个活动或服务,系统马上启动在intent对象指定的组件。
当创建一个隐式意图,系统通过通过把intent的内容和设备上其他程序的manifest文件中定义的intent filters对比找到合适组件去启动。如果意图匹配一个intent filter,系统启动这个组件把intent对象传递给他。如果多个intent filters可用,系统展示一个对话框,让用户选择使用哪个app。
一个intent filter是一个在app manifest文件中的表达式,指定组件会接收的intent的类型。举例,通过为一个活动声明一个intent filter,可以让其他app直接以特定的intent启动我们的活动。类似的,如果不为活动生命任何intent filter,它只能以一个显式意图启动(不声明,其他人就没法用,只有自己的显式用了)。
Figure 1. Illustration of how an implicit intent is delivered through the system to start another activity: [1] Activity A creates an Intent with an action description and passes it to startActivity(). [2] The Android System searches all apps for an intent filter that matches the intent. When a match is found, [3] the system starts the matching activity (Activity B) by invoking its onCreate() method and passing it the Intent.
小心:保证app是安全的,总是使用一个显式意图启动一个服务,不要为服务定义intent filters。使用隐式意图启动一个服务是一个安全隐患,因为我们不确定哪个service响应这个intent,并且用户看不到服务启动。从5.0(21)开始,系统抛出异常如果调用bindService用一个隐式意图。
Building an Intent
一个intent对象携带系统使用来决定启动哪个组件(比如应该接收这个intent的精确的组件名称或者组件类型)的信息,加上这个接收组件用来执行合适的行为的信息。(比如要执行的动作和按照执行的数据such as the action to take and the data to act upon).
一个intent包含的基本细信息如下:
Component name
要启动组件的名字
这是可选的,但是他是显式意图的关键信息,意味着这个intent应该被传递给由组件名称定义的app组件。没有组件名称,这个intent的隐式的,系统决定哪个组件应该接收这个意图,通过其他意图信息(比如动作、数据、类型,下面会讲)。
所以如果需要启动一个特定 组件,指定名称。
Note:
当启动服务的时候,应该总是指定名称。否则,不确定按个服务会响应这个意图,用户看不到那个服务启动了。
When starting a Service, you should always specify the component name.
设置的实际是一个ComponentName对象。
This field of the Intent is a ComponentName object, which you can specify using a fully qualified class name of the target component, including the package name of the app. For example, com.example.ExampleActivity. You can set the component name with setComponent(), setClass(), setClassName(), or with the Intent constructor.
Action
一个字符串,指定要执行的通用动作(比如查看和选择)
在广播的intent中,这是发和将要报告的动作。这个动作主要决定了意图其他的部分如何结构,尤其是包含在data和extras中的。
In the case of a broadcast intent, this is the action that took place and is being reported. The action largely determines how the rest of the intent is structured—particularly what is contained in the data and extras.
可以指定自己的动作在自己的app中用(或者其他应用??应该是自己可以定义,但是尽量不要这么做),但是你通常应该使用Intent 类定义的动作常量或者其他架构类,下面是一些通用动作来启动一个活动:
You can specify your own actions for use by intents within your app (or for use by other apps to invoke components in your app), but you should usually use action constants defined by the Intent class or other framework classes. Here are some common actions for starting an activity:
ACTION_VIEW
当活动有一些信息展示给用户,比如,图片在一个galler app展示,或者map app的地址信息。
Use this action in an intent with startActivity() when you have some information that an activity can show to the user, such as a photo to view in a gallery app, or an address to view in a map app.
ACTION_SEND
也称分享Intent。当用户有一些信息要通过另一个app分享,比如邮件和社交分享app。
Intent类参考有其他的通用动作。其他动作定义在安卓框架,比如设置打开一个系统的设置app。(这两个就差不多了,其他的要自己多学啊)。
可以用setAction或者Intent构造器为intent指定动作。
如果自己定义:
If you define your own actions, be sure to include your app's package name as a prefix. For example:
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Data
一个URI对象指向要起作用的data,或者data的MINE类型。数据的类型通常由intent的action决定,比如,action是ACTION_EDIT,那么这个数据应该包含要编辑的文档的URI(content Provider?)
当创建一个intent,通常很重要去指定数据的类型(他的MIME类型)除了他的URI之外。举例,一个展示图片的活动可能不会能够打开音频文件,及时URI的格式可能类似。指定data的MIME类型帮助安卓系统找到最合适的组件接收意图。然而,MIME类型又是可以从URI推断出,尤其是当数据的一个内容:URI(指定类型在设置的位置),有ContentProvider(使数据的MIME类型系统可见)控制
只设置data URI,调用setData,只设置MIME类型,调用setType。如果有需要,可以明确的设置使用setDataAndType.
小心:如果同时设置URI和MIME,不要调用setData和setType,他们会使另一个无效,总是使用setDataAndType同时设置这两个。
Category
一个字符串包含额外的信息关于应该处理这个意图的组件的种类。任何类型描述的数字可以被放置到一个intent中,但是大多数的intents不要求一个category。下面是一些通用的类型:
CATEGORY_BROWSABLE
目标活动允许自己由一个web浏览器启动展示一个连接的数据,比如一个图片或者email信息。
The target activity allows itself to be started by a web browser to display data referenced by a link—such as an image or an e-mail message.
CATEGORY_LAUNCHER
活动是任务的初始活动
The activity is the initial activity of a task and is listed in the system's application launcher.
See the Intent class description for the full list of categories.
可以使用 addCategory()指定一个category.
上面讨论的componet name,action ,data和category,代表了一个意图的定义特征,通过读取这些属性,系统可以决定启动哪个组件。
然而,一个意图可以携带额外的信息,不影响组件如何解析他,比如:
However, an intent can carry additional information that does not affect how it is resolved to an app component.
Extras
键值对携带额外的信息要求如何完成这个请求动作。
Key-value pairs that carry additional information required to accomplish the requested action. Just as some actions use particular kinds of data URIs, some actions also use particular extras.
You can add extra data with various putExtra() methods, each accepting two parameters: the key name and the value. You can also create a Bundle object with all the extra data, then insert the Bundle in the Intent with putExtras().
For example, when creating an intent to send an email with ACTION_SEND, you can specify the "to" recipient with the EXTRA_EMAIL key, and specify the "subject" with the EXTRA_SUBJECT key.
The Intent class specifies many EXTRA_* constants for standardized data types. If you need to declare your own extra keys (for intents that your app receives), be sure to include your app's package name as a prefix. For example:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
Flags
Flags defined in the Intent class that function as metadata for the intent. The flags may instruct the Android system how to launch an activity (for example, which task the activity should belong to) and how to treat it after it's launched (for example, whether it belongs in the list of recent activities).
For more information, see the setFlags() method.
Example explicit intent
一个显式意图是用来启动一个特定的app 组件,比如app中的一个指定的活动或服务。为了创建显式的intent,为Intent对象定义component name,其他intent属性是可选的。
举例,如果在ap中定义了一个叫DownloadService的服务,设计为从网络下载文件,可以这样启动他:
// 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);
The Intent(Context, Class) constructor supplies the app Context and the component a Class object. As such, this intent explicitly starts the DownloadService class in the app.
Example implicit intent
隐式意图指定一个动作可以激活设备上的任意app来执行这个动作。使用一个隐式的意图当我们的app不能执行这个动作,但是其他app可能可以,而且想让用户选择使用哪个app。
举例,如果有内容想让用户和其他人分享,通过ACTION_SEND创建一个intent,并且添加extras指定分享的内容。当调用startActivity使用那个intent,用户可以选择一个app分享这个内容。
小心:可能用户没有任何app处理传递给startActivity的intent。如果这个发生的话,调用会失败,我们的app会崩溃。为了保证一个活动能接收这个意图,调用resolveActivity在Intent对象上,这样默认会有个可用,调用startActivity就安全了。
但是不要使用这样的Intent。避免
To verify that an activity will receive the intent, call resolveActivity() on your Intent object. If the result is non-null, then there is at least one app that can handle the intent and it's safe to call startActivity(). If the result is null, you should not use the intent and, if possible, you should disable the feature that issues the intent.
// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
Note: In this case, a URI is not used, but the intent's data type is declared to specify the content carried by the extras.
当startActivity调用,系统检查所有的app决定哪个处理这个意图(一个意图有ACTION_SENT动作,携带"text/plain" data)。如果只有一个立马启动,多个供用户选择。
Forcing an app chooser
当有多个应用可以响应intent,用户可以选择使用哪个,并且让这个app为默认的选择。当用户想从现在开始使用同一个app,比如打开网页,这是非常友好的。
如果多个可以使用,用户每次都想使用不同的app,我们应该显示的展示选择框。选择框每次都请求用户选择哪个(用户不能选择默认的app)。比如使用ACTION_SEND分享,用户根据当前的情况选择使用一个不同的app。所以每次都要能选择
展示选择器,使用createChooser创建Intent,传递给startActivity,比如:
Intent sendIntent = 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 the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);
// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
这个展示了一个对话框有可以响应的app,使用提供的text作为对话框的标题。
This displays a dialog with a list of apps that respond to the intent passed to the createChooser() method and uses the supplied text as the dialog title.
Receiving an Implicit Intent
为了通知app能接收哪个隐式意图,在manifest文件中定义一个或多个inent filter为每一个app组件使用<intent-filter>元素。每个intent filter指定他接收的intent类型,基于intent的action,data和category。只有intent能通过我们一个intent filter,系统才会把隐式意图传送给我们的app组件。
Note: 显示意图总是传递给目标,不管定义的intent filter。
一个app组件应该为每一个唯一的工作定义分离的fliters。举例:在image gallery app中 ,一个活动可能有两个filters:一个查看,一个编辑。当活动启动,检查Intent,根据Intent的信息决定如果执行(比如查看或者编辑)
每个intent filter由<intent-filter>元素指定,集成进对应的组件(比如<activitt> 或者<service>吧)。在<intent-filter>内部可以使用下面三个元素指定。
<action>
在name属性声明意图接收的动作。值必须是一个动作的string,不是类常量
Declares the intent action accepted, in the name attribute. The value must be the literal string value of an action, not the class constant.
<data>
定义接收的data类型,使用一个或多个属性指定不同方面的data URI
Declares the type of data accepted, using one or more attributes that specify various aspects of the data URI (scheme, host, port, path, etc.) and MIME type.
<category>
在name属性中声明意图接收的类型。值必须是string,而不是类常量。
Declares the intent category accepted, in the name attribute. The value must be the literal string value of an action, not the class constant.
Note: 为了接收隐式意图,必须在intent filter包含CATEGORY_DEFAULT。StartActivity和stratActivityForResult处理所以的意图好像他们声明了CATEGORY_DEFAULT category。如果不声明,没有隐式的意图能被活动接收(????)
In order 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.
下面是一个活动接收一个ACTION_SEND意图,当data type是text。
<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>
Intent filter可以包含多个<action>, <data>, or <category>. 如果这样做,需要确认这个组件可以处理任何或者所有的filter元素。
When you want to handle multiple kinds of intents, but only in specific combinations of action, data, and category type, then you need to create multiple intent filters.
一个隐式意图要通过这三个元素的对比才能传送给这个组件。如果其中一个匹配失败,系统不会把这个意图传送给这个组件,然而,因为一个组件可能有多个inten filter,一个意图不通过这个,可能通过另一个。查看更多Intent Resolution
Restricting access to components
使用一个intent filter并不是一个安全的方法阻止其他app启动我们组件。虽然,intent filter限制了一个组件响应特定的隐式意图,另一个app可以使用显式意图启动app组件如果开发人员发现了我们组件的名字。如果只有自己能启动我们的组件很重要,诶组件设置exported属性为false。
Caution: 服务必须是显式的。
To avoid inadvertently running a different app's Service, always use an explicit intent to start your own service and do not declare intent filters for your service.
Note:所有的活动,必须在manife中声明intent filter。然后广播可以动态注册使用registerReceiver().取消注册使用 unregisterReceiver().这样可以让app运行的时候,只在一段时间监听特殊的广播。 Doing so allows your app to listen for specific broadcasts during only a specified period of time while your app is running.
Manifest的例子片段:
<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>
第一个活动,MainActivity,是app的入口,使用app图标启动时打开的。
•
The ACTION_MAIN action indicates this is the main entry point and does not expect any intent data.主入口
•
The CATEGORY_LAUNCHER指示活动icon作为app的launcher图标<activity>元素不指定,系统会使用<application>的icon,那么这个Application元素必须要有图标了??
These two must be paired together in order for the activity to appear in the app launcher.这两个是一个活动作为启动活动的必要条件。
第二个活动,用户分析文本或媒体信息。虽然用户可以从MainActivity进入这个活动,他们也可以通过另一个app指定符合过滤器的隐式意图进入。
Note: The MIME type, application/vnd.google.panorama360+jpg, is a special data type that specifies panoramic photos, which you can handle with the Google panorama APIs.
Using a Pending Intent
A PendingIntent object is a wrapper around an Intent object. The primary purpose of a PendingIntent is to grant permission to a foreign application to use the contained Intent as if it were executed from your app's own process.
Major use cases for a pending intent include:
•
Declare an intent to be executed when the user performs an action with your Notification (the Android system's NotificationManager executes the Intent).
•
Declare an intent to be executed when the user performs an action with your App Widget (the Home screen app executes the Intent).
•
Declare an intent to be executed at a specified time in the future (the Android system's AlarmManager executes the Intent).
Because each Intent object is designed to be handled by a specific type of app component (either an Activity, a Service, or a BroadcastReceiver), so too must a PendingIntent be created with the same consideration. When using a pending intent, your app will not execute the intent with a call such as startActivity(). You must instead declare the intended component type when you create the PendingIntent by calling the respective creator method:
•
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.
Unless your app is receiving pending intents from other apps, the above methods to create a PendingIntent are the only PendingIntent methods you'll probably ever need.
Each method takes the current app Context, the Intent you want to wrap, and one or more flags that specify how the intent should be used (such as whether the intent can be used more than once).
More information about using pending intents is provided with the documentation for each of the respective use cases, such as in the Notifications and App Widgets API guides.
Action test
指定intent可以接收的动作,一个过滤器可以指定0或多个<actin>元素。举例
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.VIEW" />
...
</intent-filter>
要想通过这个过滤器,Intent(一个intent只能指定一个action)指定的action必须和其中一个匹配
如果过滤器没有任何actions,那么没有任何东西一个intent可以匹配,所有的intents匹配失败。然后,如果一个Intent不指定一个动作,那么他可以通过测试(只要过滤器包含至少一个actiong)
If the filter does not list any actions, there is nothing for an intent to match, so all intents fail the test. However, if an Intent does not specify an action, it will pass the test (as long as the filter contains at least one action).
Category test
指定inent可以接收的类型,过滤器可以设置0或多个<category>元素。举例:
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
...
</intent-filter>
一个intent要通过category测试,intent中的每一个category必须和过滤器中的一个匹配。相反是没有必要的,过滤器可能定义比intent指定的category要多,intent仍然可以通过。因此,一个没有category的intent应该总是能通过这个测试,不管过滤器定义的category。
For an intent to pass the category test, every category in the Intent must match a category in the filter. The reverse is not necessary—the intent filter may declare more categories than are specified in the Intent and the Intent will still pass. Therefore, an intent with no categories should always pass this test, regardless of what categories are declared in the filter.
安卓系统会给隐式意图一个CATEGORY_DEFAULT 类型。所以隐式的intent必须指定一个android.intent.category.DEFAULT
Note: Android automatically applies the the CATEGORY_DEFAULT category to all implicit intents passed to startActivity() and startActivityForResult(). So if you want your activity to receive implicit intents, it must include a category for "android.intent.category.DEFAULT" in its intent filters (as shown in the previous <intent-filter> example.
Intent matching
Intent和过滤器匹配不仅仅去找到一个目标组件去激活,也可以发现设备上一系列的组件。举例,
Intents are matched against intent filters not only to discover a target component to activate, but also to discover something about the set of components on the device. For example, the Home app populates the app launcher by finding all the activities with intent filters that specify the ACTION_MAIN action and CATEGORY_LAUNCHER category.
Your application can use intent matching in a similar way. The PackageManager has a set of query...() methods that return all components that can accept a particular intent, and a similar series of resolve...() methods that determine the best component to respond to an intent. For example, queryIntentActivities() returns a list of all activities that can perform the intent passed as an argument, and queryIntentServices() returns a similar list of services. Neither method activates the components; they just list the ones that can respond. There's a similar method, queryBroadcastReceivers(), for broadcast receivers.