Intent类型
Intent 分为两种类型:
显式 Intent:按名称(完全限定类名)指定要启动的组件。 通常,您会在自己的应用中使用显式 Intent 来启动组件,这是因为您知道要启动的 Activity 或服务的类名。例如,启动新 Activity 以响应用户操作,或者启动服务以在后台下载文件。
创建显式 Intent 启动 Activity 或服务时,系统将立即启动 Intent 对象中指定的应用组件。
隐式 Intent :不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。 例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。
创建隐式 Intent 时,Android 系统通过将 Intent 的内容与在设备上其他应用的清单文件中声明的 Intent 过滤器进行比较,从而找到要启动的相应组件。 如果 Intent 与 Intent 过滤器匹配,则系统将启动该组件,并向其传递 Intent 对象。 如果多个 Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。
构建 Intent
Intent 中包含的主要信息如下:
1. 组件名称
要启动的组件名称。这是可选项,但也是构建显式 Intent 的一项重要信息,这意味着 Intent 应当仅传递给由组件名称定义的应用组件。 如果没有组件名称,则 Intent 是隐式的。
Intent 的这一字段是一个 ComponentName 对象,您可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。 例如, com.example.ExampleActivity。您可以使用 setComponent()、setClass()、setClassName() 或 Intent 构造函数设置组件名称。
2. action
指定要执行的通用操作(例如,“查看”或“选取”)的字符串。您可以指定自己的操作,供 Intent 在您的应用内使用(或者供其他应用在您的应用中调用组件)。但是,您通常应该使用由 Intent 类或其他框架类定义的操作常量。如一些常用action:
ACTION_VIEW
, ACTION_SEND
3. data
引用待操作数据和/或该数据 MIME 类型的 URI(Uri 对象)。提供的数据类型通常由 Intent 的操作决定。例如,如果操作是 ACTION_EDIT,则数据应包含待编辑文档的 URI。
4. Category
一个包含应处理 Intent 组件类型的附加信息的字符串。以下是一些常见类别:
CATEGORY_BROWSABLE
,目标 Activity 允许本身通过网络浏览器启动,以显示链接引用的数据,如图像或电子邮件。
CATEGORY_LAUNCHER
,该 Activity 是任务的初始 Activity,在系统的应用启动器中列出。
5. Extra
携带完成请求操作所需的附加信息的键值对。您可以使用各种 putExtra() 方法添加 extra 数据,每种方法均接受两个参数:键名和值。您还可以创建一个包含所有 extra 数据的 Bundle 对象,然后使用 putExtras() 将Bundle 插入 Intent 中。
6. IntentFlag
在 Intent 类中定义的、充当 Intent 元数据的标志。标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务),以及启动之后如何处理(例如,它是否属于最近的 Activity 列表)。具体可以查看我之前写的一篇文章。IntentFlag大全及使用总结
显式 Intent 示例:
// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
隐式 Intent 示例:
// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
Intent Filter匹配规则
Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。一个组件可以声明多个Intent Filter,只需要匹配任意一个即可启动该组件。 一个Intent Filter中的action、type、category可以有多个,所有的action、type、category分别构成不同类别,同一类别信息共同约束当前类别的匹配过程。只有一个Intent同时匹配一个Intent Filter的action、type、category这三个类别才算完全匹配,只有完全匹配才能启动Activity。
比如下面定义了两个intent-filter,只要有一个Intent Filter的action、type、category完全匹配即可:
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http" android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="com.study.jankin.test"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="demoapp" />
</intent-filter>
1. action的匹配规则
一个Intent Filter中可声明多个action,Intent中的action与其中的任一个action在字符串形式上完全相同(注意,区分大小写,大小写不同但字符串内容相同也会造成匹配失败),action方面就匹配成功。
注意,隐式Intent必须指定action。
比如我们在Manifest文件中为Activity定义了如下Intent Filter:
<intent-filter>
<action android:name="com.study.jankin.test"/>
</intent-filter>
在程序中我们就可以通过下面的代码启动该Activity
Intent intent = new Intent("com.study.jankin.test");
startActivity(intent);
2. category的匹配规则
category也是一个字符串,但是它与action的过滤规则不同,它要求Intent中个如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同。也就是说,Intent中如果出现了category,不管有几个category,对于每个category来说,它必须是过滤规则中的定义了的category。当然,Intent中也可以没有category(若Intent中未指定category,系统会自动为它带上“android.intent.category.DEFAULT”),如果没有,仍然可以匹配成功。我们可以通过addCategory方法为Intent添加category。
如常用的 <category android:name="android.intent.category.BROWSABLE"/>
可以让组件通过浏览器启动。
3. data的匹配规则
同action类似,如果过滤规则中定义了data, 那么Intent中必须也要定义可匹配的data,只要Intent的data与Intent Filter中的任一个data声明完全相同,data方面就完全匹配成功。
data由两部分组成:mimeType和Uri。
MineType指的是媒体类型:例如imgage/jpeg,auto/mpeg4和viedo/*等,可以表示图片、文本、视频等不同的媒体格式
Uri 可配置更多信息,类似于url。Uri的默认scheme是content或file。
URI的结构:
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
content://com.axe.mg:100/fold/subfolder/etc
http://www.axe.com:500/profile/info
Uri的属性说明:
Scheme:URI的模式,比如http、file、content。如果URI中没有指定Scheme,那么整个URI无效。默认值为content 和 file。
Host:URI的主机名。比如www.baidu.com,如果host未指定,那么整个Uri中的其他参数无效,这也意味着Uri是无效的。
Port:URI端口,当URI指定了scheme 和 host 参数时port参数才有意义。
path:用来匹配完整的路径,如:http://example.com/blog/abc.html,这里将 path 设置为 /blog/abc.html 才能够进行匹配;
pathPrefix: 用来匹配路径的开头部分,拿上面的 Uri 来说,这里将 pathPrefix 设置为 /blog 就能进行匹配了;
pathPattern: 用表达式来匹配整个路径。这里需要说下匹配符号与转义。
匹配符号:
1、“” 用来匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…
2、“.” 用来匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…
3、因此 “.*” 就是用来匹配任意字符0次或更多,如:“.*html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”…
转义:
因为当读取 Xml 的时候,“\” 是被当作转义字符的(当它被用作 pathPattern 转义之前),因此这里需要两次转义,读取 Xml 是一次,在 pathPattern 中使用又是一次。如:“*”
这个字符就应该写成 “\\*”
,“\”
这个字符就应该写成 “\\\\”
。
Intent的uri可通过setData()方法设置,mimetype可通过setType()方法设置。
注意:这两个方法会互相清除对方设置的值,如果需要同时设置uri和mimetype可以调用setDataAndType()方法
这三个方法的源码如下:
public @NonNull Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
public @NonNull Intent setType(@Nullable String type) {
mData = null;
mType = type;
return this;
}
public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
mData = data;
mType = type;
return this;
}
下面看一个例子,清单文件中该Activity的intent-filter配置如下:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.study.jankin.test"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<data
android:host="www.jankin.com"
android:pathPattern=".*\\.pdf"
android:scheme="http"/>
</intent-filter>
</activity>
我们可以通过如下代码启动该Activity:
Intent intent = new Intent("com.study.jankin.test");
intent.setData(Uri.parse("http://www.jankin.com/a/b/a.pdf"));
if(intent.resolveActivity(getPackageManager()) != null){
startActivity(intent);
}
Intent Filter常见问题
1. 查询是否有Activity可以匹配我们指定Intent的组件
- 采用PackageManager的resolveActivity或者Intent的resolveActivity方法会获得最适合Intent的一个Activity。
- 调用PackageManager的queryIntentActivities会返回所有成功匹配Intent的Activity。
其中PackageManager中queryIntentActivities和resolveActivity的方法原型:
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, @ResolveInfoFlags int flags);
public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags);
上述两个方法的第一个参数比较好理解,第二个参数要注意,要使用PackageManager.MATCH_DEFAULT_ONLY
这个标记位,这个标记位的含义是仅仅匹配那些在intent-filter中声明<category android:name="android.intent.category.DEFAULT"/>
这个category的Activity。使用这个标记位可以保证我们只要上述两个方法不返回null,那么startActivity一定可以成功。如果不用这个标记位,就可以把intent-filter中category不含default的那些Activity匹配出来,因为不含有default这个category的Activity无法接收隐式Intent,从而可能导致startActivity失败。
2. android.intent.action.MAIN 与android.intent.category.LAUNCHER的区别
android.intent.action.MAIN决定一个应用程序最先启动那个组件
android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里(说白了就是是否在桌面上显示一个图标)
这两个属性组合情况:
第一种情况:有MAIN,无LAUNCHER,程序列表中无图标
原因:android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里
第二种情况:无MAIN,有LAUNCHER,程序列表中无图标
原因:android.intent.action.MAIN决定应用程序最先启动的Activity,如果没有Main,则不知启动哪个Activity,故也不会有图标出现
所以这两个属性一般成对出现。
3. 关于隐式intent
每一个通过 startActivity() 方法发出的隐式 Intent 都至少有一个 category,就是 “android.intent.category.DEFAULT”,所以只要是想接收一个隐式 Intent 的 Activity 都应该包括 “android.intent.category.DEFAULT” category,不然将导致 Intent 匹配失败.
参考
https://developer.android.com/guide/components/intents-filters?hl=zh-cn
https://blog.csdn.net/mynameishuangshuai/article/details/51673273