[Android][Intent]

1.Intent的作用

Intent 是一个消息传递对象,您可以使用它从其他应用组件请求操作。简单来说Intent就是用于组件(这里的组件自然是四大组件)之间传递消息。
它的基本用途:启动Activity,启动服务,传递广播。

2.Intent类型

显式 Intent:按名称(完全限定类名)指定要启动的组件。 通常,您会在自己的应用中使用显式 Intent 来启动组件,这是因为您知道要启动的 Activity 或服务的类名。例如,启动新 Activity 以响应用户操作,或者启动服务以在后台下载文件。可以看到这里指定了要启动的组件名MyService.class。

//显式启动服务
mIntent = new Intent(this,MyService.class);
startService(mIntent);

隐式 Intent :不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。 例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。可以看出这里没有指定要启动的组件名,而是通过Intent过滤器里的action来启动。

//隐式启动服务
mIntent = new Intent();
mIntent.setAction("com.tan.lgy.testintent.MyService.Action");
//api5.0必须要设置,否则会报错,这里设置的是该服务所在的应用的包名,而不是这个服务类的路径名
mIntent.setPackage("com.tan.lgy.testintent");
startService(mIntent);

3.Intent启动组件的过程

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

隐式启动过程图解

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

4.Intent包含的主要信息

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

1)组件名称

要启动的组件名称。这是可选项,如果要显式启动组件,就必须设置组件名称。如果没有在Intent里设置组件名称,则 Intent 是隐式的,且系统将根据其他 Intent 信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收 Intent。
Intent 的这一字段是一个 ComponentName 对象,您可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。 例如,com.tan.lgy.testintent.MyService。您可以使用 setComponent()、setClass()、setClassName() 或 Intent 构造函数设置组件名称。

/*设置组件名称
* 只要是设置了组件名称,都是显示启动组件
* */
private void setComponentName(int index)
{
    switch(index)
    {
        case 1:
            mIntent = new Intent(this,MyService.class);
        break;
        case 2:
            mIntent = new Intent();
            mIntent.setComponent(new ComponentName("com.tan.lgy.testintent","com.tan.lgy.testintent.MyService"));
            break;
        case 3:
            mIntent = new Intent();
            mIntent.setClass(this,MyService.class);
            break;
        case 4:
            mIntent = new Intent();
            mIntent.setClassName(this,"com.tan.lgy.testintent.MyService");
            break;
        default:
        break;
    }
    startService(mIntent);
}

2)操作

指定要执行的通用操作(例如,“查看”或“选取”)的字符串。可以指定自己的操作,供 Intent 在您的应用内使用(或者供其他应用在您的应用中调用组件)。但是,您通常应该使用由 Intent 类或其他框架类定义的操作常量。这是什么意思呢?也就是可以通过设定action,执行对应的操作。系统提供了一些标准的操作,你只需调用系统定义的常量即可执行对应的动作,例如,下面这个例子会打开手机的拨号界面,并填充要拨打的号码“1800010001”.

/*    设置Action,可执行指定动作*/
    private void aboutAction(int index)
    {
        if (index == 1) {
            //界面跳转到拨号界面并输入了要拨打的号码:1800010001
            Intent intent=new Intent();
            intent.setAction(Intent.ACTION_DIAL);
            intent.setData(Uri.parse("tel:1800010001"));
            startActivity(intent);
        }else if (index == 2)
        {
            //界面跳转到设置蓝牙的界面
            Intent intent=new Intent();
            intent.setAction(Settings.ACTION_BLUETOOTH_SETTINGS);
            startActivity(intent);
        }
    }

当然,也可以设置为自己定义的字符串,执行对应的行为。如果定义自己的操作,请确保将应用的软件包名称作为前缀(这个不是必须的,只是一个良好的习惯,可以在一定程度上避免定义了重复的Action)。如隐式启动服务的例子,我们在清单文件里设置Intent过滤器里设置了action.

image.png

当我们要启动该服务的时候,只需要设置对应的Intent即可隐式启动该服务。

image.png

对于要打开系统的设置相关的界面的操作,将在 Settings 中定义(android.provider.Settings)。至于如何设置Action,你可以使用 setAction() 或 Intent 构造函数为 Intent 指定操作。

3)数据

提供待操作数据或该数据 MIME 类型的 URI(Uri 对象)。提供的数据类型通常由 Intent 的操作(也就是action)决定。例如,如果操作是 ACTION_EDIT,则数据应包含待编辑文档的 URI。下面的例子是打开给定路径下的对应的文件。

/*    根据给定的路径和MIME类型打开文件
    这种方式无需设置读写sd卡权限*/
    private void aboutData(int index)
    {
        if (index == 1) {
            Intent intent = new Intent("android.intent.action.VIEW");
            Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath()+"/testSignature.jpg"));
            intent.setDataAndType (uri, "image/*");
            this.startActivity(intent);
        }else if (index == 2)
        {
            //这种方式会弹出能打开pdf文件的软件选择界面,让你选择使用那个软件打开该pdf文档
            Intent intent = new Intent("android.intent.action.VIEW");
            Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath()+"/移动受理单.pdf"));
            intent.setDataAndType (uri, "application/pdf");
            this.startActivity(intent);
        }
    }

如果仅需要设置数据 URI,请调用 setData()。 如果仅需要设置 MIME 类型,请调用 setType()。如果既要设置data又要设置type,就必须使用 setDataAndType() 。若要同时设置 URI 和 MIME 类型,请勿调用 setData() 和 setType(),因为它们会互相抵消彼此的值。请始终使用 setDataAndType()同时设置 URI 和 MIME 类型。也可以在清单文件中,使用一个或多个指定数据 URI 各个方面(scheme、host、port、path 等)和 MIME 类型的属性,声明接受的数据类型。

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

第二个 Activity ShareActivity 旨在便于共享文本和媒体内容。 尽管用户可以通过从 MainActivity 导航进入此 Activity,但也可以从发出隐式 Intent(与两个 Intent 过滤器之一匹配)的另一应用中直接进入 ShareActivity。
要指定接受的 Intent 数据, Intent 过滤器既可以不声明任何 <data> 元素,也可以声明多个此类元素。 例如:

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>

每个 <data> 元素均可指定 URI 结构和数据类型(MIME 媒体类型)。 URI 的每个部分均包含单独的 scheme、host、port 和 path 属性:
<scheme>://<host>:<port>/<path>
例如:
content://com.example.project:200/folder/subfolder/etc
在此 URI 中,架构是 content,主机是 com.example.project,端口是 200,路径是 folder/subfolder/etc。

这里也是摘抄的详细可以查看https://developer.android.google.cn/guide/components/intents-filters.html#Building一文里的数据测试。

4)类别

包含应处理 Intent 组件类型的附加信息的字符串。 您可以将任意数量的类别描述放入一个 Intent 中,但大多数 Intent 均不需要类别。您可以使用 addCategory() 指定类别。

/*
* 可以总结,在显示启动的情况下设置不设置Category对于启动组件都没什么影响,都能打开组件;
* 但是在隐式启动的情况下,如果设置的Category和清单文件下的不匹配,是会报错的。因为可以设置多个Category,
* 所以只要有一个Category能匹配清单文件下的Category,那么就能执行指定操作。
* */
    private void aboutCategory(int index)
    {
        if (index == 1) {
            Intent intent = new Intent("android.intent.action.VIEW");
            Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath()+"/testSignature.jpg"));
            intent.setDataAndType (uri, "image/*");
            //报错No Activity found to handle Intent
            // { act=android.intent.action.VIEW cat=[lgy] dat=file:///storage/emulated/0/testSignature.jpg typ=image/* }
            //startActivity默认匹配的Category是android.intent.category.DEFAULT
            //如果我改成intent.addCategory("lgy"),那么就匹配不到了,所以不仅无法打开该图片,甚至报错了
            intent.addCategory("android.intent.category.DEFAULT");
            this.startActivity(intent);
        }else if (index == 2)
        {
            Intent intent = new Intent(this,Main2Activity.class);
            //已经在清单文件里的Main2Activity里的过滤器下设置了名为com.lgy.test的category
            //1.仅匹配android.intent.category.DEFAULT,能启动Main2Activity
            //intent.addCategory("android.intent.category.DEFAULT");
            //2.匹配com.lgy.test和android.intent.category.DEFAULT,能启动Main2Activity
            //intent.addCategory("com.lgy.test");
            //intent.addCategory("android.intent.category.DEFAULT");
            //3.仅匹配com.lgy.test,能启动Main2Activity
            //intent.addCategory("com.lgy.test");
            //4.匹配一个不存在的category,"com.lgy.test2",也能打开Main2Activity,居然能匹配成功,我在这里猜测是因为我们是显式启动的原因,
            //所以即使Category不匹配也能打开,下面测试一下隐式启动的情况下,我觉得不匹配应该是会保存或者无法启动Main2Activity
            intent.addCategory("com.lgy.test2");
            this.startActivity(intent);
        }else if (index == 3)
        {
            //测试Category对隐式启动的影响
            //先在清单文件里给Main2Activity设置一个action,"com.tan.lgy.main2Acitivity.testCategory"用于隐式启动
            Intent intent = new Intent();
            intent.setAction("com.tan.lgy.main2Acitivity.testCategory");
            //已经在清单文件里的Main2Activity里的过滤器下设置了名为com.lgy.test的category
            //1.仅匹配android.intent.category.DEFAULT,不能能启动Main2Activity
            //这是因为没有在清单文件里设置名为"android.intent.category.DEFAULT"的Category,设置之后就能启动
            //intent.addCategory("android.intent.category.DEFAULT");
            //2.匹配com.lgy.test和android.intent.category.DEFAULT,能启动Main2Activity
            //intent.addCategory("com.lgy.test");
            //intent.addCategory("android.intent.category.DEFAULT");
            //3.仅匹配com.lgy.test,能启动Main2Activity
            intent.addCategory("com.lgy.test");
            //4.匹配一个不存在的category,"com.lgy.test2",和猜想的一样,不能打开Main2Activity且报错
            intent.addCategory("com.lgy.test2");
            this.startActivity(intent);
        }
    }

5)Extra

携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的 extra。这个应该比较容易理解,这里举个例子:例如要启动一个类名为Main2Activity的Activity,这时候可以在intent里附件数据供被启动的Main2Activity使用。
您可以使用各种 putExtra() 方法添加 extra 数据,每种方法均接受两个参数:键名和值。您还可以创建一个包含所有 extra 数据的 Bundle 对象,然后使用 putExtras() 将Bundle 插入 Intent 中。

/*
* 携带数据,携带完成请求操作所需的附加信息的键值对
* */
    private void aboutExtra()
    {
        Intent intent = new Intent(this,Main2Activity.class);
        intent.putExtra("data","Extra携带数据供Main2Activity使用");
        this.startActivity(intent);
    }
  1. 标志
    在 Intent 类中定义的、充当 Intent 元数据的标志。 标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务),以及启动之后如何处理(例如,它是否属于最近的 Activity 列表)。这里就不再赘述,下面的内容是摘抄网上的内容。
    Flag是一些常用的标志,不同的Flag有不同的用途。例如:
    FLAG_ACTIVITY_BROUGHT_TO_FRONT
      这个标志一般不是由程序代码设置的,如在launchMode中设置singleTask模式时系统帮你设定。

FLAG_ACTIVITY_CLEAR_TOP
  如果设置,并且这个Activity已经在当前的Task中运行,因此,不再是重新启动一个这个Activity的实例,而是在这个Activity上方的所有Activity都将关闭,然后这个Intent会作为一个新的Intent投递到老的Activity(现在位于顶端)中。 例如,假设一个Task中包含这些Activity:A,B,C,D。如果D调用了startActivity(),并且包含一个指向Activity B的Intent,那么,C和D都将结束,然后B接收到这个Intent,因此,目前stack的状况是:A,B。 上例中正在运行的Activity B既可以在onNewIntent()中接收到这个新的Intent,也可以把自己关闭然后重新启动来接收这个Intent。如果它的启动模式声明为“multiple”(默认值),并且你没有在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,那么它将关闭然后重新创建;对于其它的启动模式,或者在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,都将把这个Intent投递到当前这个实例的onNewIntent()中。 这个启动模式还可以与FLAG_ACTIVITY_NEW_TASK结合起来使用:用于启动一个Task中的根Activity,它会把那个Task中任何运行的实例带入前台,然后清除它直到根Activity。这非常有用,例如,当从Notification Manager处启动一个Activity。

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
  如果设置,这将在Task的Activity stack中设置一个还原点,当Task恢复时,需要清理Activity。也就是说,下一次Task带着FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。 这在你的程序有分割点的时候很有用。例如,一个e-mail应用程序可能有一个操作是查看一个附件,需要启动图片浏览Activity来显示。这个Activity应该作为e-mail应用程序Task的一部分,因为这是用户在这个Task中触发的操作。然而,当用户离开这个Task,然后从主画面选择e-mail app,我们可能希望回到查看的会话中,但不是查看图片附件,因为这让人困惑。通过在启动图片浏览时设定这个标志,浏览及其它启动的Activity在下次用户返回到mail程序时都将全部清除。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
  如果设置,新的Activity不会在最近启动的Activity的列表中保存。

FLAG_ACTIVITY_FORWARD_RESULT
  如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的Activity。

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
  这个标志一般不由应用程序代码设置,如果这个Activity是从历史记录里启动的(常按HOME键),那么,系统会帮你设定。

FLAG_ACTIVITY_MULTIPLE_TASK
  不要使用这个标志,除非你自己实现了应用程序启动器。与FLAG_ACTIVITY_NEW_TASK结合起来使用,可以禁用把已存的Task送入前台的行为。当设置时,新的Task总是会启动来处理Intent,而不管这是是否已经有一个Task可以处理相同的事情。 由于默认的系统不包含图形Task管理功能,因此,你不应该使用这个标志,除非你提供给用户一种方式可以返回到已经启动的Task。 如果FLAG_ACTIVITY_NEW_TASK标志没有设置,这个标志被忽略。

FLAG_ACTIVITY_NEW_TASK
   如果设置,这个Activity会成为历史stack中一个新Task的开始。一个Task(从启动它的Activity到下一个Task中的Activity)定义了用户可以迁移的Activity原子组。Task可以移动到前台和后台;在某个特定Task中的所有Activity总是保持相同的次序。 这个标志一般用于呈现“启动”类型的行为:它们提供用户一系列可以单独完成的事情,与启动它们的Activity完全无关。 使用这个标志,如果正在启动的Activity的Task已经在运行的话,那么,新的Activity将不会启动;代替的,当前Task会简单的移入前台。参考FLAG_ACTIVITY_MULTIPLE_TASK标志,可以禁用这一行为。 这个标志不能用于调用方对已经启动的Activity请求结果。

FLAG_ACTIVITY_NO_ANIMATION
  如果在Intent中设置,并传递给Context.startActivity()的话,这个标志将阻止系统进入下一个Activity时应用Acitivity迁移动画。这并不意味着动画将永不运行——如果另一个Activity在启动显示之前,没有指定这个标志,那么,动画将被应用。这个标志可以很好的用于执行一连串的操作,而动画被看作是更高一级的事件的驱动。

FLAG_ACTIVITY_NO_HISTORY
  如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。这也可以通过设置noHistory特性。

FLAG_ACTIVITY_NO_USER_ACTION
  如果设置,作为新启动的Activity进入前台时,这个标志将在Activity暂停之前阻止从最前方的Activity回调的onUserLeaveHint()。 典型的,一个Activity可以依赖这个回调指明显式的用户动作引起的Activity移出后台。这个回调在Activity的生命周期中标记一个合适的点,并关闭一些Notification。 如果一个Activity通过非用户驱动的事件,如来电或闹钟,启动的,这个标志也应该传递给Context.startActivity,保证暂停的Activity不认为用户已经知晓其Notification。

FLAG_ACTIVITY_REORDER_TO_FRONT
  如果在Intent中设置,并传递给Context.startActivity(),这个标志将引发已经运行的Activity移动到历史stack的顶端。 例如,假设一个Task由四个Activity组成:A,B,C,D。如果D调用startActivity()来启动Activity B,那么,B会移动到历史stack的顶端,现在的次序变成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP标志也设置的话,那么这个标志将被忽略。

FLAG_ACTIVITY_SINGLE_TOP
如果设置,当这个Activity位于历史stack的顶端运行时,不再启动一个新的。

5.源码地址

http://download.csdn.net/download/lgywsdy/10105485

6.参考文章

Intent 和 Intent 过滤器官网介绍
Google Developers的中文官网
Android API中文主页

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容