Intent 和 Intentfilter

来自:https://developer.android.com/guide/components/intents-filters.html

Intent是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:

  • 启动 Activity

    Activity表示应用中的一个屏幕。通过将Intent传递给startActivity(),您可以启动新的Activity实例。Intent描述了要启动的Activity,并携带了任何必要的数据。

    如果您希望在 Activity 完成后收到结果,请调用startActivityForResult()。在 Activity的onActivityResult()回调中,您的 Activity 将结果作为单独的Intent对象接收。如需了解详细信息,请参阅Activity指南。

  • 启动服务

    Service是一个不使用用户界面而在后台执行操作的组件。通过将Intent传递给startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent描述了要启动的服务,并携带了任何必要的数据。

    如果服务旨在使用客户端-服务器接口,则通过将Intent传递给bindService(),您可以从其他组件绑定到此服务。如需了解详细信息,请参阅服务南。

  • 传递广播

    广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将Intent传递给sendBroadcast()sendOrderedBroadcast()sendStickyBroadcast(),您可以将广播传递给其他应用。

Intent 类型

Intent 分为两种类型:

  • 显式Intent:按名称(完全限定类名)指定要启动的组件。通常,您会在自己的应用中使用显式Intent来启动组件,这是因为您知道要启动的 Activity 或服务的类名。例如,启动新Activity以响应用户操作,或者启动服务以在后台下载文件。
  • 隐式Intent:不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。

创建显式 Intent 启动 Activity 或服务时,系统将立即启动Intent对象中指定的应用组件。

图 1. 隐式 Intent 如何通过系统传递以启动其他 Activity 的图解:[1] Activity A 创建包含操作描述的 Intent,并将其传递给 startActivity()[2] Android 系统搜索所有应用中与 Intent 匹配的 Intent 过滤器。找到匹配项之后,[3] 该系统通过调用匹配 Activity(Activity B)的 onCreate() 方法并将其传递给Intent,以此启动匹配 Activity。

创建隐式 Intent 时,Android 系统通过将Intent的内容与在设备上其他应用的清单文件中声明的Intent过滤器进行比较,从而找到要启动的相应组件。Intent如果 Intent 与 Intent 过滤器匹配,则系统将启动该组件,并将其传递给对象。如果多个 Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。

Intent 过滤器是应用清单文件中的一个表达式,它指定该组件要接收的 Intent 类型。例如,通过为 Activity 声明 Intent 过滤器,您可以使其他应用能够直接使用某一特定类型的 Intent 启动 Activity。同样,如果您没有为 Activity 声明任何 Intent 过滤器,则 Activity 只能通过显式 Intent 启动。

警告:为了确保应用的安全性,启动Service时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪些服务将响应Intent,且用户无法看到哪些服务已启动。从Android 5.0(API 级别 21)开始,如果使用隐式Intent调用bindService(),系统会抛出异常。

构建Intent


Intent对象携带了Android系统用来确定要启动哪个组件的信息(例如,准确的组件名称或应当接收该 Intent 的组件类别),以及收件人组件为了正确执行操作而使用的信息(例如,要采取的操作以及要处理的数据)。

Intent中包含的主要信息如下:

组件名称 要启动的组件名称。

这是可选项,但也是构建显式Intent 的一项重要信息,这意味着 Intent 应当仅传递给由组件名称定义的应用组件。如果没有组件名称,则 Intent 是隐式的,且系统将根据其他Intent信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收Intent。因此,如需在应用中启动特定的组件,则应指定该组件的名称。

注意:启动Service时,您应始终指定组件名称。否则,您无法确定哪项服务会响应Intent,且用户无法看到哪项服务已启动。

Intent的这一字段是ComponentName对象,您可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。例如,com.example.ExampleActivity。您可以使用setComponent()setClass()setClassName()Intent构造函数设置组件名称。

操作

指定要执行的通用操作(例如,“查看”或“选取”)的字符串。

对于广播 Intent,这是指已发生且正在报告的操作。操作在很大程度上决定了其余 Intent 的构成,特别是数据和extra中包含的内容。

您可以指定自己的操作,供Intent在您的应用内使用(或者供其他应用在您的应用中调用组件)。但是,您通常应该使用由Intent类或其他框架类定义的操作常量。以下是一些用于启动 Activity 的常见操作:

ACTION_VIEW
如果您拥有一些某项 Activity 可向用户显示的信息(例如,要使用图库应用查看的照片;或者要使用地图应用查找的地址),请使用 Intent 将此操作与 startActivity()结合使用。
ACTION_SEND
这也称为“共享”Intent。如果您拥有一些用户可通过其他应用(例如,电子邮件应用或社交共享应用)共享的数据,则应使用 Intent 中将此操作与 startActivity()结合使用。

有关更多定义通用操作的常量,请参阅Intent类引用。 其他操作在Android框架中的其他位置定义。例如,对于在系统的设置应用中打开特定屏幕的操作,将在Settings中定义。

您可以使用setAction()Intent构造函数为Intent指定操作。

如果定义自己的操作,请确保将应用的软件包名称作为前缀。 例如:

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
数据 引用待操作数据和/或该数据MIME类型的URI( Uri 对象)。提供的数据类型通常由Intent的操作决定。例如,如果操作是 ACTION_EDIT ,则数据应包含待编辑文档的URI。

创建 Intent 时,除了指定URI 以外,指定数据类型(其 MIME 类型)往往也很重要。例如,能够显示图像的Activity可能无法播放音频文件,即便 URI 格式十分类似时也是如此。因此,指定数据的 MIME 类型有助于 Android 系统找到接收 Intent 的最佳组件。但有时,MIME 类型可以从URI 中推断得出,特别当数据是content:URI 时尤其如此。这表明数据位于设备中,且由ContentProvider控制,这使得数据 MIME 类型对系统可见。

要仅设置数据 URI,请调用setData()。要仅设置 MIME 类型,请调用setType()。如有必要,您可以使用setDataAndType() 同时显式设置二者。

警告:若要同时设置 URI 和 MIME 类型,请勿调用setData()setType(),因为它们会互相抵消彼此的值。请始终使用setDataAndType()同时设置 URI 和 MIME 类型。

类别
一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个Intent中,但大多数Intent均不需要类别。以下是一些常见类别:
CATEGORY_BROWSABLE
目标 Activity 允许本身通过 Web 浏览器启动,以显示链接引用的数据,如图像或电子邮件。
CATEGORY_LAUNCHER
该 Activity 是任务的初始 Activity,在系统的应用启动器中列出。

有关类别的完整列表,请参阅 Intent 类描述。

您可以使用addCategory()指定类别。

以上列出的这些属性(组件名称、操作、数据和类别)表示 Intent 的既定特征。通过读取这些属性,Android 系统能够解析应当启动哪个应用组件。

但是,Intent 也有可能会一些携带不影响其如何解析为应用组件的信息。Intent 还可以提供:

Extra
携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的附加数据。

您可以使用各种putExtra()方法添加附加数据,每种方法均接受两个参数:键名和值。您还可以创建一个包含所有附加数据的Bundle对象,然后使用putExtras()Bundle插入Intent中。

例如,使用ACTION_SEND创建用于发送电子邮件的 Intent 时,可以使用EXTRA_EMAIL键指定“目标”收件人,并使用EXTRA_SUBJECT键指定“主题”。

Intent类将为标准化的数据类型指定多个 EXTRA_* 常量。如需声明自己的附加数据 键(对于应用接收的 Intent ),请确保将应用的软件包名称作为前缀。例如:

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
标志
Intent类中定义的、充当Intent元数据的标志。标志可以指 Android系统如何启动Activity(例如,Activity应属于哪个 任务),以及启动之后如何处理(例如,它是否属于最近的Activity列表)。

如需了解详细信息,请参阅setFlags()方法。

显式Intent示例

显式Intent是指用于启动某个特定应用组件(例如,应用中的某个特定 Activity 或服务)的 Intent。要创建显式 Intent,请为Intent对象定义组件名称。Intent 的所有其他属性均为可选属性。

例如,如果在应用中构建了一个名为DownloadService、旨在从 Web 中下载文件的服务,则可使用以下代码启动该服务:

// 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);

Intent(Context, Class)构造函数分别为应用和组件提供ContextClass对象。因此,此Intent将显式启动该应用中的DownloadService类。

如需了解有关构建和启动服务的详细信息,请参阅服务指南。

隐式Intent示例

隐式Intent指定能够在可以执行相应操作的设备上调用任何应用的操作。如果您的应用无法执行该操作而其他应用可以,且您希望用户选取要使用的应用,则使用隐式Intent非常有用。

例如,如果您希望用户与他人共享您的内容,请使用ACTION_SEND操作创建 Intent,并添加指定共享内容的Extra。使用该Intent 调用startActivity()时,用户可以选取共享内容所使用的应用。

警告:用户可能没有任何应用处理您发送到startActivity()的隐式 Intent。如果出现这种情况,则调用将会失败,且应用会崩溃。要验证 Activity是否会接收 Intent,请对Intent对象调用resolveActivity()。如果结果为非空,则至少有一个应用能够处理该 Intent,且可以安全调用startActivity()。如果结果为空,则不应使用该Intent。如有可能,您应禁用发出该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);
}

注意:在这种情况下,系统并没有使用 URI,但已声明Intent的数据类型,用于指定Extra携带的内容。

调用startActivity()时,系统将检查已安装的所有应用,确定哪些应用能够处理这种 Intent(即:含ACTION_SEND操作并携带“文本/纯”数据的 Intent )。如果只有一个应用能够处理,则该应用将立即打开并提供给 Intent。如果多个 Activity 接受 Intent,则系统将显示一个对话框,使用户能够选取要使用的应用。

强制使用应用选择器

如果有多个应用响应隐式Intent,则用户可以选择要使用的应用,并将其设置为该操作的默认选项。如果用户可能希望今后一直使用相同的应用执行某项操作(例如,打开网页时,用户往往倾向于仅使用一种 Web 浏览器),则这一点十分有用。

但是,如果多个应用可以响应Intent,且用户可能希望每次使用不同的应用,则应采用显式方式显示选择器对话框。选择器对话框要求用户选择每次操作要使用的应用(用户无法为该操作选择默认应用)。 例如,当应用使用ACTION_SEND操作执行“共享”时,用户根据目前的状况可能需要使用另一不同的应用,因此应当始终使用选择器对话框,如图 2 中所示。

图 2. 选择器对话框。

要显示选择器,请使用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);
}

这将显示一个对话框,其中包含响应传递给createChooser()方法的Intent的应用列表,并使用提供的文本作为对话框标题。

接收隐式 Intent


要公布应用可以接收哪些隐式 Intent,请在清单文件中使用 <intent-filter>; 元素为每个应用组件声明一个或多个Intent 过滤器。每个 Intent 过滤器均根据 Intent 的操作、数据和类别指定自身接受的 Intent 类型。仅当隐式 Intent 可以通过 Intent 过滤器之一传递时,系统才会将该 Intent 传递给应用组件。

注意:显式 Intent始终会传递给其目标,无论组件声明的 Intent 过滤器如何均是如此。

应用组件应当为自身可执行的每个独特作业声明单独的过滤器。例如,图像库应用中的一个 Activity 可能会有两个过滤器,分别用于查看图像和编辑图像。 当 Activity 启动时,它将检查Intent并根据Intent中的信息决定具体的行为(例如,是否显示编辑器控件)。

每个 Intent 过滤器均由应用清单文件中的<intent-filter>元素定义,并嵌套在相应的应用组件(例如,元素)中。在内部,您可以使用以下三个元素中的一个或多个指定要接受的 Intent 类型:

name属性中,声明接受的 Intent 操作。该值必须是操作的文本字符串值,而不是类常量。
使用一个或多个指定数据 URI( schemehostportpath 等)各个方面和 MIME 类型的属性,声明接受的数据类型。
name 属性中,声明接受的 Intent 类别。该值必须是操作的文本字符串值,而不是类常量。

注意:为了接收隐式Intent,必须CATEGORY_DEFAULT类别包括在 Intent 过滤器中。方法startActivity()startActivityForResult()将按照已申明CATEGORY_DEFAULT类别的方式处理所有 Intent。 如果未在 Intent 过滤器中声明此类别,则隐式Intent不会解析为您的 Activity。

例如,以下是一个使用Intent过滤器进行的Activity声明,当数据类型为文本时,系统将接收ACTION_SENDIntent :

 android:name="ShareActivity">
    
         android:name="android.intent.action.SEND"/>
         android:name="android.intent.category.DEFAULT"/>
         android:mimeType="text/plain"/>
    

您可以创建一个包括多个<action>、<data>或 实例的过滤器。创建时,仅需确定组件能够处理这些过滤器元素的任何及所有组合即可。

如需仅以操作、数据和类别类型的特定组合来处理多种 Intent,则需创建多个 Intent 过滤器。

系统通过将 Intent 与所有这三个元素进行比较,根据过滤器测试隐式 Intent。隐式 Intent 若要传递给组件,必须通过所有这三项测试。如果 Intent 甚至无法匹配其中任何一项测试,则 Android 系统不会将其传递给组件。但是,由于一个组件可能有多个 Intent 过滤器,因此未能通过某一组件过滤器的 Intent 可能会通过另一过滤器。如需了解有关系统如何解析 Intent 的详细信息,请参阅下文的 Intent 解析部分。

警告:为了避免无意中运行不同应用的 Service,请始终使用显式 Intent 启动您自己的服务,且不必为该服务声明 Intent 过滤器。

注意:对于所有 Activity,您必须在清单文件中声明 Intent 过滤器。但是,广播接收器的过滤器可以通过调用registerReceiver()动态注册。稍后,您可以使用unregisterReceiver()注销该接收器。这样一来,应用便可仅在应用运行时的某一指定时间段内侦听特定的广播。

过滤器示例

为了更好地了解一些 Intent 过滤器的行为,我们一起来看看从社交共享应用的清单文件中截取的以下片段。

 android:name="MainActivity">
    
    
         android:name="android.intent.action.MAIN" />
         android:name="android.intent.category.LAUNCHER" />
    


 android:name="ShareActivity">
    
    
         android:name="android.intent.action.SEND"/>
         android:name="android.intent.category.DEFAULT"/>
         android:mimeType="text/plain"/>
    
    
    
         android:name="android.intent.action.SEND"/>
         android:name="android.intent.action.SEND_MULTIPLE"/>
         android:name="android.intent.category.DEFAULT"/>
         android:mimeType="application/vnd.google.panorama360+jpg"/>
         android:mimeType="image/*"/>
         android:mimeType="video/*"/>
    

第一个 Activity MainActivity是应用的主要入口点。当用户最初使用启动器图标启动应用时,该 Activity 将打开:

  • ACTION_MAIN操作指示这是主要入口点,且不要求输入任何 Intent 数据。
  • CATEGORY_LAUNCHER类别指示此 Activity 的图标应放入系统的应用启动器。如果元素未使用icon指定图标,则系统将使用元素中的图标。

这两个元素必须配对使用,Activity 才会显示在应用启动器中。

第二个 Activity ShareActivity 旨在便于共享文本和媒体内容。尽管用户可以通过从MainActivity导航进入此 Activity,但也可以从发出隐式 Intent(与两个 Intent 过滤器之一匹配)的另一应用中直接进入ShareActivity

注意:MIME 类型application/vnd.google.panorama360+jpg 是一个指定全景照片的特殊 数据类型,您可以使用Google 全景API 对其进行处理。

使用待定 Intent


PendingIntent对象是Intent对象的包装器。PendingIntent的主要目的是授权外部应用使用包含的Intent,就像是它从您应用本身的进程中执行的一样。

待定 Intent 的主要用例包括:

  • 声明用户使用您的通知执行操作时所要执行的 Intent(Android 系统的NotificationManager执行Intent)。
  • 声明用户使用您的 应用小工具执行操作时要执行的 Intent(主屏幕应用执行Intent)。
  • 声明未来某一特定时间要执行的 Intent(Android 系统的AlarmManager执行Intent)。

由于每个Intent对象均设计为由特定类型的应用组件进行处理(ActivityService 或 BroadcastReceiver),因此还必须基于相同的考虑因素创建PendingIntent。使用待定 Intent 时,应用不会使用调用(如startActivity())执行该 Intent。相反,通过调用相应的创建器方法创建 PendingIntent时,您必须声明所需的组件类型:

  • PendingIntent.getActivity(),适用于启动ActivityIntent
  • PendingIntent.getService(),适用于启动ServiceIntent
  • PendingIntent.getBroadcast(),适用于启动BroadcastReceiverIntent

除非您的应用正在从其他应用中接收待定Intent,否则上述用于创建PendingIntent的方法可能是您所需的唯一PendingIntent方法。

每种方法均会提取当前的应用Context、您要包装的Intent以及一个或多个指定应如何使用该Intent的标志(例如,是否可以多次使用该 Intent)。

如需了解有关使用待定Intent的详细信息,请参阅通知和应用小工具API 指南等手册中每个相应用例的相关文档。

你可能感兴趣的:(Android进阶)