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.
当我们要启动该服务的时候,只需要设置对应的Intent即可隐式启动该服务。
对于要打开系统的设置相关的界面的操作,将在 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 过滤器既可以不声明任何 元素,也可以声明多个此类元素。 例如:
<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);
}
6) 标志
在 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中文主页