显式和隐式Intent介绍及intent-filter的action,category,data匹配规则

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

https://www.jianshu.com/p/5f644e0fdba9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值