startActivity()
或startActivityForResult()
方法启动Activity
,Intent
对象包含了要启动的Activity
的信息及其他必要的数据。startService()
或bindService()
方法启动Service
,Intent
对象包含了要启动的Service
的信息及其他必要的数据。sendBroadcast()
或sendOrderedBroadcast()
或sendStickyBroadcast()
方法发送广播。如果Intent
中明确包含了要启动的组件的完整类名(包名及类名),那么这个Intent就是显式的。
反之就是隐式的, 一般,隐式intent
不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。
例如,你想在地图上给用户显示一个位置,但是你的App
又不支持地图展示,这时候你可以将位置信息放入到一个Intent
中,然后给它指定相应的action
,通过这样隐式的Intent
请求其他地图类型的App
(例如Google Map、百度地图等)来在地图中展示一个指定的位置。
模块解藕示例:https://www.cnblogs.com/xjx22/p/5551034.html
当创建了一个隐式Intent
时,Android
系统会将该隐式Intent
所包含的信息与设备上其他所有App
中AndroidManifest.xml
中的
进行对比过滤,从中找出满足能够接收处理该隐式Intent
的App
和对应的组件。如果有多个App
中的某个组件都符合条件,那么Android
会弹出一个对话框让用户选择需要启动哪个App
。
其实同样可以理解为监听者模式, 而隐式Intent
的调用就是一个广播查找调用。
Android可以根据Intent
所携带的信息去查找要启动的组件,启动的组件根据Intent
携带的一些数据信息做相应的处理。
Intent
由6部分信息组成:Component Name
、Action
、Data
、Category
、Extras
、Flags
。
Component Name
、Action
、Data
、Category
、决定了Android会启动哪个组件,其中Component Name
用于显式Intent
。Intent
,Action
、Data
、Category
、Extras
、Flags
用于在隐式Intent
。Extras
里面包含了具体的用于组件实际处理的数据信息。Flags
是Intent
的元数据,决定了Android对其操作的一些行为。要启动的组件名称。如果你想使用显式的Intent
,那么你就必须指定该参数,一旦设置了component name
,Android会直接将Intent
传递给组件名所指定的组件去启动它。
需要注意的是,当启动
Service
的时候,你应该总是指定Component Name
。否则,你不确定最终哪个App
的哪个组件被启动了,并且用户也看不到哪个Service
启动了。
component name
在Intent
中对应的field
是ComponentName
对象,你可以通过要启动的组件的完整类名(包括应用的包名)指定该值,例如com.example.ExampleActivity
。你可以通过Intent
的setComponent()
方法、setClass()
方法、setClassName()
方法或Intent
的构造函数指定component name
。
表示了要执行操作的字符串。比如查看
或选择
,其对应着
中的
标签.
预定义的具有通用意义的action
:
Intent.ACTION_VIEW
值为 “android.intent.action.VIEW
”,当你有一些信息想通过其他Activity
展示给用户的时候,你就可以将Intent
的action
指定为ACTION_VIEW
,比如在一个图片应用中查看一张图片,或者在一个地图应用中展现一个位置。Intent.ACTION_SEND
值为”android.intent.action.SEND
”,该action
常用来做“分享”使用,当你有一些数据想通过其他的App(例如QQ、微信、百度云等)分享出去的时候,就可以使用此action
构建Intent
对象,并将其传递给startActivity()
方法,由于手机上可能有多个App的Activity
均支持ACTION_SEND
这一action
,所以可能出现多个APP供你选择。自定义action
:
请确保将应用的软件包名称作为前缀,例如:
。
你可以指定你独有的action
以便于你的App中的Intent
使用或其他App中通过Intent
调用你的App中的组件。
你可以通过调用intent
的setAction()
方法或在Intent
的构造函数中指定intent
的action
。
此处所说的Intent
中的data
指的是Uri对象和数据的MIME类型,对应着
中的标签。
一个完整的Uri由scheme
、host
、port
、path
组成,格式是
。例如content://com.example.project:200/folder/subfolder/etc
。
Uri就像一个数据链接,组件可以根据此Uri获得最终的数据来源。通常将Uri和action
结合使用,比如我们将action
设置为ACTION_VIEW
,我们应该提供将要被编辑修改的文档的Uri。
当创建了一个Intent
对象的时候,除了指定Uri之外,指定数据的MIME
类型也很重要。
例如,一个Activity
能够显示图片,但是不能够播放视频,显示图片的Uri和播放视频的Uri可能很类似,为了不让Android误将一个含有视频Uri的Intent
对象传递给一个只能显示图片的Activity
,我们需要在该Activity
的
中指定MIME
类型为图片(例如)并且还要给
Intent
对象设置对应的图片类型的MIME
,这样Android就会基于Uri和MIME
类型将Intent
传递给符合条件的组件。
有个特例,如果Uri使用的是content:
协议,那么这就说明Uri所提供的数据将来自于本地设备,即数据由ContentProvider
提供,这种情况下Android会根据Uri自动推断出MIME
类型,此种情况我们无需再自己指定MIME
类型。
需要注意的是,如果你想要同时设置数据的Uri和MIME
类型,不要先后调用Intent
对象的setData()
方法和setType()
方法,因为setData()
方法和setType()
是互斥的,即如果调用了setData()
方法,会将Intent
中已经通过setType()
方法设置的MIME
类型重置为空。如果调用了setType()
方法,会将Intent
中已经通过setData()
方法设置的Uri重置为空。所以在需要同时设置数据的Uri和MIME
类型的时候,一定要调用Intent
对象的setDataAndType()
方法,而不是分别调用setData()
方法和setType()
方法。
category
包含了关于组件如何处理Intent
的一些其他信息,虽然可以在Intent
中加入任意数量的category
,但是大多数的Intent
其实不需要category
。
以下是一些常见的category
:
CATEGORY_BROWSABLE
目标 Activity
允许本身通过网络浏览器启动,以显示链接引用的数据,如图像或电子邮件。CATEGORY_LAUNCHER
用于标识Activity
是某个App的入口Activity
。extras
,顾名思义,就是额外的数据信息,Intent
中有一个Bundle
对象存储着各种键值对,接收该Intent
的组件可以从中读取出所需要的信息以便完成相应的工作。有的Intent
需要靠Uri携带数据,有的Intent
是靠extras
携带数据信息。
你可以通过调用Intent
对象的各种重载的putExtra(key, value)
方法向Intent
中加入各种键值对形式的额外数据。你也可以直接创建一个Bundle
对象,向该Bundle
对象传入很多键值对,然后通过调用Intent
对象的putExtras(Bundle)
方法将其设置给Intent
对象中去。
例如,你创建了一个action
为ACTION_SEND
用于发送电子邮件的 Intent
时,
用Intent.EXTRA_EMAIL
指定“目标”收件人,用Intent.EXTRA_SUBJECT
指定“主题”。
Intent
类将为标准化的数据类型指定多个 EXTRA_*
常量。如需声明自己的 extra
键(对于应用接收的 Intent
),请确保将应用的软件包名称作为前缀。 例如::static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
在 Intent
类中定义的、充当 Intent
元数据的标志。 标志可以指示 Android 系统如何启动 Activity
(例如,Activity
应属于哪个任务),以及启动之后如何处理(例如,它是否属于最近的 Activity
列表)。。更多信息可参见Intent
的setFlags()
方法。
例如,你有一段文本信息,想通过其他App分享出去,示例代码如下:
Intent sendIntent = new Intent();
// 设置action, action对隐式Intent来说是非常重要的
sendIntent.setAction(Intent.ACTION_SEND);
// 设置数据的MIME类型为纯文本类型
sendIntent.setType("text/plain");
// 设置额外的数据
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
// 获取包管理器
PackageManager pm = getPackageManager();
// 先判断系统中有没有潜在的App的Activity支持对该sendIntent的接收与处理
if (pm.resolveActivity(sendIntent, 0) != null) {
startActivity(sendIntent);
}
上面的代码中,我们构建了一个隐式的Intent
对象。我们设置了action
的值为Intent.ACTION_SEND
,action
对隐式Intent
来说是非常重要的。然后我们将intent
的数据的MIME
类型设置为纯文本类型(“text/plain”),告知Android我们的Intent
持有的是文本类型的数据。最后我们将实际的文本数据通过putExtra()
方法作为额外数据设置进去。
需要注意的是,在构建好了Intent
对象之后,我们没有立即执行startActivity(sendIntent)
方法,而是将sendIntent
作为参数传递给了PackageManager
的resolveActivity()
方法中,该方法会让Android
根据该sendIntent
找到潜在的适合启动的组件的信息,并以ResolveInfo
类的对象的形式返回结果,如果返回null
,表示当前系统中没有任何组件可以接收并处理该sendIntent
。如果返回不是null,就表明系统中至少存在一个组件可以接收并处理该sendIntent
,只有在这种情况下,我们才会执行代码startActivity(sendIntent)
,在通过intent
启动组件之前先判断要启动的组件存不存在是个良好的编程习惯,因为如果系统中不存在支持你的intent
的组件,那么当你调用startActivity()
、startService()
、bindService()
等方法的时候,Android就会抛出异常。
如果有多个应用响应隐式 Intent,则用户可以选择要使用的应用,并将其设置为该操作的默认选项。 如果用户可能希望今后一直使用相同的应用执行某项操作(例如,打开网页时,用户往往倾向于仅使用一种网络浏览器),则这一点十分有用。
但是如果用户不想每次都用同一个默认App处理这样的情形怎么办呢?这时候我们可以在代码中明确地使用App选择对话框,比如我们的App执行一个action为ACTION_SEND
的分享功能时,我们想让用户分享自己数据的代码,但是我们不确定用户想通过哪个App去分享,我们想每次都弹出App选择对话框让用户决定想通过哪个App分享,示例代码如下所示:
Intent sendIntent = new Intent(Intent.ACTION_SEND);
...
String title = "请选择想通过哪个App分享数据";
// 验证是否有App能够接收并处理sendIntent
if (sendIntent.resolveActivity(getPackageManager()) != null) {
// 根据sendIntent创建一个需要显示App选择对话框的intent对象
Intent chooserIntent = Intent.createChooser(sendIntent, title);
// 我们使用chooserIntent作为startActivity()方法的参数,而非sendIntent
startActivity(chooserIntent);
}
首先我们创建了我们原始的sendIntent
,并对其设置action
等相关信息,然后我们将sendIntent
传递给了Intent.createChooser()
方法中,创建了另一个chooserIntent
。后面我们通过调用Intent.resolveActivity(PackageManager)
方法判断系统中是否有App能够接收并处理sendIntent
,该方法与上面之前提到过的PackageManager
的resolveActivity()
方法是等价的。最后我们使用chooserIntent
作为startActivity()
方法的参数,而非sendIntent
,chooserIntent
会让Android系统强制显示用户选择App处理Intent
的界面。
当Android系统接收到一个隐式Intent
要启动一个Activity
(或其他组件)时,Android会根据以下三个信息比较Intent
的信息与注册的组件的
的信息,从而为该Intent
选择出最匹配的Activity
(或其他组件):
如果隐式intent
对象同时通过了某个组件的中
的action
测试、category
测试以及data
测试,那么该组件就可以被intent
对象所启动。反之无法启动。下面我们依次看一下如何才能通过这三个测试。
为了指定能够接收并处理的Intent
的类型,组件可以在
中声明其支持0个或多个action
,例如:
intent
对象可以通过setAction()
方法设置唯一的一个action
值。对于action
测试,需要分两种情况:
如果intent
对象通过调用setAction()
方法设置了action
的值,那么只有当组件的
中包含了intent
对象中的action
值的时候,action
测试才通过,否则无法通过。
举个例子,假设我们的
如下所示:
1.下面的intent
对象可以通过上面
里面的action
测试:
Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun");
intent.setData(uri);
该intent
之所以能通过action
测试是因为
中包含该intent
的action
值com.ispring.action.ACTION_TEST1
。
2.下面的intent
对象无法通过上面
里面的action
测试:
Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST3");
Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun");
intent.setData(uri);
该intent
之所以无法通过action
测试是因为
中不包含该intent
的action
值com.ispring.action.ACTION_TEST3
。
如果intent
对象没有调用setAction()
方法设置action
的值,那么如果
至少有一个任意的action
的值,该intent
对象就可以通过该
的action测试,反之,如果
中没有定义任何的action
,那么该intent
无法通过该
的action
测试。
举个例子,假设我们的intent
对象如下所示:
Intent intent = new Intent();
//不设置action值
//intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun");
intent.setData(uri);
1.上面的intent
对象可以通过如下的
:
2.上面的intent
对象无法通过如下的
:
总结起来有两点结论:
intent
对象通过action
测试,那么
中声明的action
不能为空且要包含intent
对象中的action
值(如果intent
的action
值不为空的话)。
没有声明任何action
,那么所有的intent
的对象(即无论intent
如何配置)都无法通过
的action
测试。为了指定能够接收并处理的Intent
的类型,组件可以在
中声明其支持0个或多个category
,例如:
...
intent
对象有addCategory()
方法,也就是说一个intent
对象也可以关联多个category
。为了能让intent
对象通过
的category
测试,intent
对象中的所有category
都要在
中找到对应项。
这种情况下,假设intent
对象有N个category
(N >=1),那么
中必须要包含这N个category
,intent
对象才能通过category
测试,否则无法通过测试。如果用intent
对象启动Activity
,还有其他限制条件,会在后面详细说明。
举个例子,假设我们的
如下所示:
1.以下intent
对象能够通过category
测试
Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
intent.addCategory("com.ispring.category.TEST1");
intent.addCategory("com.ispring.category.TEST2");
该intent
对象之所以可以通过category
测试是因为
包含了该intent
中所有的category
值:com.ispring.category.TEST1
和com.ispring.category.TEST2
。
2.以下intent
对象无法通过category
测试
Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
intent.addCategory("com.ispring.category.TEST1");
intent.addCategory("com.ispring.category.TEST3");
该intent
之所以无法通过上面的
的category
测试是因为
只包含了该intent
中值为com.ispring.category.TEST1
的category
,而并未包含值为com.ispring.category.TEST3
的category
。
如果intent
对象没有调用过addCategory()
方法,那么intent对象就不包含任何的category
。这种情形下,如果该intent
不是用来启动Activity
的话,那么无论
中category
中如何配置,intent
对象总是能通过
的category
测试,即便
中没有声明任何的category
,intent
都能通过category
测试。此处强调了该intent
不是用来启动Activity
这种条件,会在下面详细解释。
此处需要特别说明的是,我们在上面所有的示例中,都给Activity
的
添加了值为android.intent.category.DEFAULT
的category
,这是因为当我们把一个隐式的intent
传递给startActivity()
或startActivityForResult()
方法时,Android会自动给该隐式intent
添加值为android.intent.category.DEFAULT
的category
,所以为了能让
包含intent
中全部的category
,我们就需要在Activity的
中添加该category
,在使用时需要特别注意。
根据上面我们的几个示例,我们总结如下:
intent
对象不包含任何category
,并且该intent
不是用来启动Activity
的,那么该intent
对象总是能通过所有任意的
的category
测试;intent
对象包含category
(至少一个),那么只有当
中声明的category
全部包含intent
对象中的所有category
的时候才通过category
测试。Activity
被隐式的Intent
启动,那么我们必须在该Activity
的
中声明值为android.intent.category.DEFAULT
的category
。为了指定可以接收的Intent
的data
,
需要声明0个或多个标签,例如:
...
每个标签都可以指定一个URI结构以及data的MIME类型。一个完整的URI由
scheme
、host
、port
和path
组成,其结构如下所示:
其中scheme
既可以是Android中常见的协议,也可以是我们自定义的协议。Android中常见的协议包括content协议
、http协议
、file协议
等,自定义协议可以使用自定义的字符串。
如下是一个content协议的URI:
content://com.example.project:200/folder/subfolder/etc
在该URI中,scheme
是content
,host
是com.example.project
,port
是200
,path
是folder/subfolder/etc
。
如下是一个自定义协议的URI:
ispring://blog.csdn.net/hgy
在该URI中,scheme
是ispring
,host
是blog.csdn.net
,没有明确设定port
,path
是hgy
。
组成URI的这些属性在标签中都是可选的 ,但存在如下的依赖关系:
scheme
,那么host
参数会被忽略host
,那么port
参数会被忽略scheme
和host
都没有指定,path
参数会被忽略当我们将intent
对象中的Uri参数与
中的标签指定的URI格式进行对别时,我们我们只对比
的标签指定的部分,例如:
中只指定了scheme
,那么所有带有该sheme
的URI都能匹配到该
。
中只指定了scheme
和authority
(authority
包括host
和port
两部分)而没有指定path
,那么所有具有相同scheme
和authority
的URI都能匹配到该
,而不用考虑path
为何值。
中同时指定了scheme
、authority
和path
,那么只有具有相同scheme
、authority
和path
的URI才能匹配到该
。需要注意的是,
的标签在指定
path
的值时,可以在里面使用通配符*,起到部分匹配的效果。
data
测试需要同时将intent
对象中的URI、MIME
类型与
的标签中指定的URI、
MIME
类型进行对比。
我们知道一个
下可以有多个标签,
intent
对象无需通过所有的标签测试,一般情况下,我们的
intent
对象只需通过了其中一个标签的测试并满足某些特定情形下的一些条件,那么该
intent
对象就通过了该
的data
测试。
进行对比的规则分以下几种情况:
这种情况下,只有当
也没有指定任何URI和MIME
类型的时候才能通过data
测试。
例如我们有如下intent
对象:
Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
1.上面的intent
对象可以通过下面的
的data测试:
2.上面的intent
对象无法通过下面的
测试:
这种情况下,只有当intent
对象的URI匹配到了
中的URI格式,并且
没有指定MIME
类型的时候才能通过data测试。需要注意的是,这里所说的
没有指定MIME类型的情形指的是
中所有的标签都没有指定
MIME
类型,即整个
中完全没有android:mimeType
这几个字,理解这点很重要,大家在下面的几个示例中可以体会到这点。
例如有如下intent
对象:
Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("ispring://blog.csdn.net/hgy413");
intent.setData(uri);
1.上面的intent
能通过如下的
的data测试:
2.上面的intent
对象可以通过以下
的data测试:
intent
对象虽然不能通过scheme为hgy的标签测试,但是可以通过scheme为ispring的data标签测试,且
intent
对象和
中的两个标签都没有指定MIME,所以上面的
intent
对象可以通过该
测试。
3.上面的intent
对象无法通过以下
的标签测试:
上面的intent
对象之所以不能通过
中唯一的一个标签测试是因为我们的
intent
对象没有指定MIME类型,但是上面的标签通过
android:mimeType="text/plain"
设置了MIME类型。
4.上面的intent
对象无法通过以下
的data测试:
上面的intent
对象之所以无法通过该
中的data测试,是因为intent
对象没有设置MIME类型,但是
中第二个data标签通过android:mimeType="text/plain"
设置了MIME类型。
这种情况下,只有当intent
中的MIME
类型与
中列出的MIME
类型相同,并且
没有指定任何的URI格式的时候才能通过data测试。需要注意的是,这里所说的
没有指定任何的URI格式的情形指的是
中所有标签都没有指定URI,即整个
中完全没有android:scheme
、android:host
、android:port
以及android:path
,理解这点很重要,大家在下面的几个示例中可以体会到这点。
例如有如下intent
对象:
Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
intent.setType("text/plain");
1.上面的intent
对象可以通过以下
的data测试:
2.上面的intent
对象可以通过以下
的data测试:
上面的intent
对象虽然没有通过MIME类型为image/*
的第一个data标签测试,但能通过第二个data标签测试,并且intent
对象和
都没有指定任何的URI格式。
3.上面的intent
对象不能通过以下
中的data测试:
上面的intent
对象中没有设置URI信息,但是在该
中设置了URI中的scheme
值,所以intent
无法通过
的data
测试。
4.上面的intent
对象无法通过以下
中的data测试:
上面的intent
对象没有指定URI信息,但是上面的
中第二个标签设置了URI中的scheme信息,所以
intent
对象无法通过该
的data测试。
这种情况下,要分别测试URI以及MIME类型测试是否通过,只有URI以及MIME测试都通过了,data测试才能通过。
intent
的MIME类型能够匹配
中列出的某一个
标签中的MIME类型值,那么MIME类型测试就通过了。intent
的URI格式能够匹配
中列出的某一个
中的URI,那么URI测试就通过了。intent
的URI是content:协议或file:协议,并且整个
的所有
标签中都没有指定URI,那么该intent
也能通过URI测试。换句话说,如果一个
只列出了MIME类型,没有列出任何URI相关的格式的话,那么这个
就默认是支持content:协议或file:协议的。下面举几个例子大家自己体会一下。
假设有如下协议为自定义协议ispring:
的intent
对象:
Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("ispring://blog.csdn.net/hgy");
String type = "text/plain";
intent.setDataAndType(uri, type);
1.上面的intent
对象可以通过下面的
的data
测试:
2.上面的intent
对象无法通过下面的
的data
测试:
port
不满足,URI测试不通过,导致data
测试失败。
3.上面的intent
对象无法通过下面的
的data测试:
android:mimeType
不满足,MIME
类型测试不通过,导致data
测试失败。
假设有如下协议为content:
的intent
对象:
Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("content://com.ispring.test");
String type = "text/plain";
intent.setDataAndType(uri, type);
1.上面的intent
对象无法通过下面的
的data测试:
URI中的scheme
不匹配,导致URI测试不通过,导致data测试失败。
2.上面的intent
对象可以通过下面的
的data测试:
intent
中使用的是content:协议,并且整个
中都没有定义URI格式,所以URI测试是可以通过的,并且MIME类型能找到匹配项,所以可以通过data测试。
参考:
https://developer.android.google.cn/guide/components/intents-filters
https://blog.csdn.net/iispring/article/details/48481793