我所理解的Intent 和Intent-filter

时间:2022-04-28
本文章向大家介绍我所理解的Intent 和Intent-filter,主要内容包括2.Intent 类型、3.Intent对象包含的信息、4.Intent-filter、5.Intent和Intentfilter有什么联系、7.注意事项、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

1.Intent

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

  • 启动 Activity: Activity 表示应用中的一个屏幕。通过将 Intent 传递给 startActivity(),可以启动新的 Activity 实例。Intent 描述了要启动的 Activity,并携带了任何必要的数据。 如果希望在 Activity 完成后收到结果,可以调用 startActivityForResult()。在 Activity 的 onActivityResult() 回调中, Activity 将结果作为单独的 Intent 对象接收。
  • 启动服务: Service 是一个不使用用户界面而在后台执行操作的组件。通过将 Intent 传递给 startService(),可以启动服务执行一次性操作(例如,下载文件)。Intent 描述了要启动的服务,并携带了任何必要的数据。 如果服务旨在使用客户端-服务器接口,则通过将 Intent 传递给 bindService(),可以从其他组件绑定到此服务。
  • 传递广播: 广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 Intent 传递给 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast(),可以将广播传递给其他应用。

2.Intent 类型

  • 显式 Intent: >按名称(完全限定类名)指定要启动的组件。 通常在自己的应用中使用显式 Intent 来启动组件,这是因为你知道要启动的 Activity 或服务的类名。例如,启动新 Activity 以响应用户操作,或者启动服务以在后台下载文件。创建显式 Intent 启动 Activity 或服务时,系统将立即启动 Intent 对象中指定的应用组件。
例如:这个Intent由FirstActivity发起,显式地指定SecondActivity响应startActivity(new Intent(FirstActivity.this, SecondActivity.class));
  • 隐式 Intent : >不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。 例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。 >创建隐式 Intent 时,Android 系统通过将 Intent 的内容与在设备上其他应用的清单文件中声明的 Intent 过滤器进行比较,从而找到要启动的相应组件。 如果 Intent 与 Intent 过滤器匹配,则系统将启动该组件,并向其传递 Intent 对象。 如果多个 Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。

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

注意:为了确保应用的安全性,启动 Service 时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪些服务将响应 Intent,且用户无法看到哪些服务已启动。

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

3.Intent对象包含的信息

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

a).Component name

要启动的组件名称。 这是可选项,但也是构建显式 Intent 的一项重要信息,这意味着 Intent 应当仅传递给由组件名称定义的应用组件。 如果没有组件名称,则 Intent 是隐式的,且系统将根据其他 Intent 信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收 Intent。 因此,如需在应用中启动特定的组件,则应指定该组件的名称。 Intent 的这一字段是一个 ComponentName 对象,可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。 例如, com.example.ExampleActivity。 可以使用 setComponent()、setClass()、setClassName() 或 Intent 构造函数设置组件名称。

b)action

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

对于广播 Intent,这是指已发生且正在报告的操作。操作在很大程度上决定了其余 Intent 的构成,特别是数据和 extra 中包含的内容。 可以指定自己的操作,供 Intent 在应用内使用。但是,通常应该使用由 Intent 类或其他框架类定义的操作常量。 以下是一些用于启动 Activity 的常见操作:

  • ACTION_VIEW 如果你拥有一些某项 Activity 可向用户显示的信息(例如,要使用图库应用查看的照片;或者要使用地图应用查看的地址),请使用 Intent 将此操作与 startActivity() 结合使用。
  • ACTION_SEND 这也称为“共享”Intent。如果你拥有一些用户可通过其他应用(例如,电子邮件应用或社交共享应用)共享的数据,则应使用 Intent 将此操作与 startActivity() 结合使用。 可以使用 setAction() 或 Intent 构造函数为 Intent 指定操作。 如果定义自己的操作,请确保将应用的软件包名称作为前缀。 例如:
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";

c)Data

引用待操作数据和/或该数据 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() 同时显式设置二者。

d)Category

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

e)Extra

Extra是携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的 extra。 可以使用各种 putExtra() 方法添加 extra 数据,每种方法均接受两个参数:键名和值。还可以创建一个包含所有 extra 数据的 Bundle 对象,然后使用 putExtras() 将Bundle 插入 Intent 中。

f)Flags

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

4.Intent-filter

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

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

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

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

5.Intent和Intentfilter有什么联系

当然只有在使用隐式intent的时候,Intent才会和Intentfilter有关系。

a).当intent的使用者,发起一个意图之前会在intent中添加用于响应者用于匹配的信息,包括:

<action>
<data>
<category>

这三个中的一个或者更多;

b).假设有个应用的组件(可能是与Intent使用者在同一个应用内,也可能不再同一应用内),刚好声明了和intent使用者一样的

<action>
<data>
<category>

c).当Intent使用者发起意图的时候,这个应用中声明有相同信息的组件将对这个意图做出响应。

下面是一个例子: FirstActivity发起一个意图,意图中包括的信息是:

Intent intent = new Intent();intent.setAction("android.intent.action.ACTION_START");intent.addCategory("com.example.geekp.intent.My");

FirstActivity.java代码:

public class FirstActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);
        findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {//                显式Intent//                startActivity(new Intent(FirstActivity.this, SecondActivity.class));//                隐式Intent
                Intent intent = new Intent();
                intent.setAction("android.intent.action.ACTION_START");
            
                if(null!=intent.resolveActivity(getPackageManager()))
                startActivity(intent);
                else
                    Toast.makeText(v.getContext(),"intent没有响应对象",Toast.LENGTH_SHORT).show();

            }
        });
    }}

xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_first"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.geekp.intent.FirstActivity">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="跳转 " />
</RelativeLayout>

SecondActivity在Activity的声明的时候指明了intent-filter

<activity
    android:name=".SecondActivity"
    android:label="@string/title_activity_second"
    android:theme="@style/AppTheme.NoActionBar">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_START" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

SecondActivity .java

public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
}

对应的xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.example.geekp.intent.SecondActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="399dp"
        android:layout_height="wrap_content"
        android:text="这是第二个Activity" />
</LinearLayout>

当按钮被点击的时候FirstActivity就会发起意图,系统会匹配到SecondActivity(当然是使用intent-filter中的信息、、),看一看效果:

7.注意事项

a).在使用隐式intent的时候,一定要在需要响应的组件声明的时候给intent-filter加上属性:

<category android:name="android.intent.category.DEFAULT" />

方法 startActivity() 和 startActivityForResult() 将按照已申明 CATEGORY_DEFAULT 类别的方式处理所有 Intent。 如果未在 Intent 过滤器中声明此类别,则隐式 Intent 不会被任何被系统匹配到,所以永远不会有组件响应你的意图。

b).intent-filter中的<action>、<category><data>属性可以有多个,例如:

<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>

c).如果有两个组件的都被匹配到,也就是说Android系统根据意图的<action>、<category>、<data>匹配到多个复合的组件,那么将会出现选择框让你来选择。

例如:如果此时我们创建一个与SecondActivity一样的ThreeActivity(除了名称不一样),在ThreeActivity的声明也和SecondActivity的一样:

<activity android:name=".ThreeActivity">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_START" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

验证一下:

d).必须是Intent的属性和组件过滤器的信息完全匹配上,组件才会做出相应,否则没有组件响应。如我们的SecondActivity的过滤器中

<intent-filter>
    <action android:name="android.intent.action.ACTION_START" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

我们在FirstActivity实例化Intent的时候,按理来说一定也要添加上<category android:name="android.intent.category.DEFAULT" /> 但是为什么我们在这里没有添加呢?细心的人应该会发现,答案就在a)“方法 startActivity() 和 startActivityForResult() 将按照已申明 CATEGORY_DEFAULT 类别的方式处理所有 Intent。 "也就是这两个方法会为我们添加上这个属性,所以在FirstActivity的intent只需要添加这个属性:

 intent.setAction("android.intent.action.ACTION_START");