我所理解的Intent 和Intent-filter

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

原文发布于微信公众号 - 非著名程序员(non-famous-coder)

原文发表时间:2017-02-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

我所理解的Android 启动模式

首先,这是从 一个开源网站转载的,觉得写得不错,对我们之前理解的activity的启动模式是一个新的理解方式,并给出实际的应用场景。 任务栈是什么 任务栈Tas...

25170
来自专栏码农笔录

Android N 7.0 上安装apk问题

14530
来自专栏上善若水

028android初级篇之调起Activity Service的方法

我们知道调起另外一个组件一般有两种方式,显式的和隐式的。显式的会直接指定要启动的组件,而隐式的则通过匹配条件,调起匹配的组件。

25950
来自专栏分享达人秀

四大组件的纽带——Intent

前面学习Activity时己经多次使用了 Intent,当一个Activity需要启动另一个Activity时, 程序并没有直接告诉系统要启动哪个Act...

23250
来自专栏编程之旅

iOS开发——数据持久化之归档

在实际的项目开发中,数据持久化是我们必须要考虑的一个事情,如何把我们需要的数据进行持久化处理。

14510
来自专栏小巫技术博客

Activity启动模式解析

11630
来自专栏向治洪

软件更新时候出现和原包名冲突

如果你是一个开发人员,那么出现这个问题可能是因为,较旧的版本你是使用eclipse自动发布到模拟器上的,而eclipse自动发布时使用的是一个测试用签名,这个签...

19960
来自专栏Android干货

Android项目实战(四十九):Andoird 7.0+相机适配

一、在AndroidManifest.xml 文件中添加 四大组件之一的 <provider>

10000
来自专栏封碎

在一个apk中调用另外一个apk中的activity 博客分类: Android小技巧 AndroidCC++C#Web

    其实,这本来是一件很简单的事情,但是我发现很多人问我这个问题,所以写篇小文章供参考。

13520
来自专栏移动开发

Andorid检测支付宝客户端是否安装

22340

扫码关注云+社区

领取腾讯云代金券