IntentFilter匹配规则

首先废话一下,如果要写东西,csdn和简书还是老老实实选简书。不管什么,就冲csdn不会自动保存,简书会自动保存就够了。因为本篇我已经写了两遍(时间和精力都浪费了。。。),一遍在csdn,一遍在这,只因在csdn写的时候,手贱,没有保存就不小心把页面关闭了,最蛋疼但是之前写好的一篇也不在了,改天得重写。


images (1).jpeg

前言

Activity的启动方式分为两种:显式启动和隐式启动。
二者的区别:显式启动要明确地指定被启动对象的组件信息,包括包名和类名,而隐式启动则不需要明确指定组件信息,但是需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息(IntentFilter中的过滤信息有action、category、data),如果不匹配则无法启动目标Activity。原则上一个Intent不应该既是显式启动又是隐式启动,如果共存的话,则以显式启动启动为主。

显式启动

一个简单的显式启动启动如下:

// 此处A、B两个Activity在同一个包下,如果不在同一包下,且没有导入B相应的包,则B需要带上完整的包名
Intent intent = new Intent(A.this, B.class);
startActivity(intent);

需要注意的是:显式启动只能启动本应用内的Activity,而不能跨应用启动。

隐式启动

如果一个Activity要能够被隐式启动,则其intent-filter中必须要有一个action和一个android.intent.category.DEFAULT这样的category,否则无法被启动。而如果要启动一个能够被隐式启动的Activity,则Intent中必须要有一个目标Activity中intent-filter中已经定义的action,Intent中可以不传android.intent.category.DEFAULT的category,系统会自动添加android.intent.category.DEFAULT的category。
下面分析action、category、data过滤信息的匹配规则。
1.action匹配规则
action是一个字符串,系统预定义了一些action,同时我们也可以自定义action(action区分大小写,大小写不同,字符串相同的action会匹配失败)。
action的匹配规则如果Intent只有一个action,intent-filter有多个action,只要Intent中的action能够和intent-filter中的任何一个action相同即可匹配成功(需要注意的是,隐式启动中,Intent中必须要有action,如果没有指定action,那么匹配失败);如果Intent存在多个action,则这些action必须要在目标Activity的intent-filter中已经定义的,否则也会匹配失败
例如,SecondActivity有如下过滤规则:

<activity android:name=".SecondActivity" >
    <intent-filter>
        <action android:name="com.example.test1"/>
        <action android:name="com.example.test2"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

以下3个方式都可以正常启动SecondActivity。

// 方式一 intent-filter中存在com.example.test1的action
Intent intent = new Intent();
intent.setAction("com.example.test1");
startActivity(intent);
// 方式二 intent-filter中存在com.example.test2的action
Intent intent = new Intent();
intent.setAction("com.example.test2");
startActivity(intent);
// 方式三 intent-filter中存在com.example.test1的action和com.example.test2的action
Intent intent = new Intent();
intent.setAction("com.example.test1");
intent.setAction("com.example.test2");
startActivity(intent);

以下是action无法匹配成功的导致无法正常启动SecondActivity。

// 方式四 SecondActivity的intent-filter没有配置com.example.test3的action
Intent intent = new Intent();
intent.setAction("com.example.test3");
startActivity(intent);
// 方式五 SecondActivity的intent-filter虽然配置了com.example.test1的action,但是没有配置com.example.test3的action
Intent intent = new Intent();
intent.setAction("com.example.test1");
intent.setAction("com.example.test3");
startActivity(intent);
// 方式六 SecondActivity的intent-filter配置的com.example.test1的action为小写的test1,而Intent中的com.example.TEST1的action为大写的test1
Intent intent = new Intent();
intent.setAction("com.example.tesT1");
startActivity(intent);

2.category匹配规则
同action一样,category也是一个字符串,系统也预定义了一些category,同时我们也可以自定义category(category也是区分大小写,大小写不同,字符串相同的category也会匹配失败)。
category的匹配规则和action基本一样,区别在于:Intent中可以不传category,系统会默认添加上android.intent.category.DEFAULT这个category,但是一旦Intent中传入了category,则传入的所有category必须为目标Activity中intent-filter已经定义的category,否则匹配失败
例如,SecondActivity有如下过滤规则:

<activity android:name=".SecondActivity" >
      <intent-filter>
          <action android:name="com.example.test1"/>
          <category android:name="com.example.category1"/>
          <category android:name="com.example.category2"/>
          <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
</activity>

以下3个方式都可以正常启动SecondActivity。

// 方式一 Intent默认添加android.intent.category.DEFAULT的category
Intent intent = new Intent();
intent.setAction("com.example.test1");
startActivity(intent);
// 方式二 intent-filter中存在com.example.category1的category
Intent intent = new Intent();
intent.setAction("com.example.test1");
intent.addCategory("com.example.category1");
startActivity(intent);
// 方式三 intent-filter中存在com.example.category2的category
Intent intent = new Intent();
intent.setAction("com.example.test1");
intent.addCategory("com.example.category2");
startActivity(intent);

以下由于category匹配不成功导致无法正常启动SecondActivity。

// 方式四 SecondActivity的intent-filter没有配置com.example.category3的category
Intent intent = new Intent();
intent.setAction("com.example.test1");
intent.addCategory("com.example.category3");
startActivity(intent);
// 方式五 SecondActivity的intent-filter虽然配置了com.example.category1的category,但是没有配置com.example.category3的category
Intent intent = new Intent();
intent.setAction("com.example.test1");
intent.addCategory("com.example.category1");
intent.addCategory("com.example.category3");
startActivity(intent);
// 方式六 SecondActivity的intent-filter配置的com.example.category1的category为小写的test1,而Intent中的com.example..CATEGORY1的category为大写的category1
Intent intent = new Intent();
intent.setAction("com.example.test1");
intent.addCategory("com.example.CATEGORY1");
startActivity(intent);

3.data匹配规则
首先,认识data匹配规则之前得先了解data的的结构。
data语法如下:

<data android:scheme="string"
          android:host="string"
          android:port="string"
          android:path="string"
          android:pathPattern="string"
          android:pathPrefix="string"
          android:mimeType="string" />

data由两部分组成,mimeType和URI。mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式,而URI中包含的数据就比较多了,下面是URI的结构:

<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

如:

content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info

每个数据的含义如下:
scheme:URI的模式,比如http、file、content等,如果URI中没指定scheme,那么整个URI的其他参数无效,也就意味着URI无效。
host:URI的主机名,比如www.baidu.com,如果host未指定,那么整个URI的其他参数无效,也就意味着URI无效。
port:URI中的端口号,比如80,仅当URI中指定了scheme和host参数的时候,port参数才有意义。
path、pathPattern、pathPrefix:这三个参数表述的是路径信息,其中path表示完整的路径信息;pathPattern也表示完整的路径信息,但是它里面可以包含通配符“*”,“*”表示0个或多个任意字符,需要注意的是,由于正则表达式的规范,如果想表示真实的字符串,那么“*”要写成“\\*”,“\”要写成“\\\\”,pathPrefix表示路径的前缀信息。
了解data数据格式后,现在可以说下data的匹配规则。data匹配规则和action类似,如果intent-filter中存在data信息,则Intent中必须要有data数据,并且data数据能够完全匹配过滤规则中的某个data。这里分几种情况说明:
(1)有如下过滤规则:

<intent-filter>
      ...
      <data android:mimeType="image/*" />
</intent-filter>

这种规则指定了媒体类型为所有类型的图片,那么Intent中mimeType属性必须为“image/*”才能匹配,虽然没有指定URI,但却有默认值,URI的默认值为content和file。因此,Intent中URI部分的scheme必须为content或者file。
为了匹配上面个的规则,可以写出如下示例:

intent.setDataAndType(Uri.parse("file://abc"), "image/png");

intent.setDataAndType(Uri.parse("content://abc"), "image/png");

如果要为Intent指定完整的data,必须要用setDataAndType方法,不能先调用setData再调用setType,因为这两个方法会互相清除对方
(2)有如下过滤规则:

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

这种规则指定了两组data规则,且每个data都指定了完整的属性值,既有URI又有mimeType。为了匹配如上规则,可以写出如下示例:

intent.setDataAndType(Uri.parse("http://abc"), "video/mpeg");

或者

intent.setDataAndType(Uri.parse("http://abc"), "audio/mpeg");

data规则基本和action的类似,不同之处,在于data有如下两种写法,但是它们作用是一样的:

// 方式一
<intent-filter>
      ...
      <data android:host="www.baidu.com" android:scheme="http"/>
</intent-filter>
// 方式二
<intent-filter>
      ...
       <data android:scheme="http"/>
       <data android:host="www.baidu.com"/>
</intent-filter>

最后

IntentFilte的匹配规则已经基本完结,另外,IntentFilter的匹配规则也适用于Service和BroadcastReceiver,不过系统对于Service的建议是尽量用显式启动方式来启动服务。
在action和category中,如下两个比较重要:

<action android:name="android.intent.action.MAIN"  />
<category android:name="android.intent.category.LAUNCHER"/>

这二者共同表明这是一个入口Activity并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中。
同时,对于隐式启动Activity的时候,可以先做个判断,看看是否有Activity能够匹配Intent。PackageManager提供了两种方法,一个是resolveActivity方法,如果匹配不成功就返回null;另一个是queryIntentActivities,返回所有成功匹配的Activity信息。
两个方法原型如下:

public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags);
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, @ResolveInfoFlags int flags);

上述两个方法的第一个参数就是要就是要启动的intent对象,第二个参数则必须为PackageManager.MATCH_DEFAULT_ONLY这个标记位,这个标记位的含义是仅仅匹配那些在intent-filter中声明了 <category android:name="android.intent.category.DEFAULT"/>这个category的Activity。使用这个标记位的意义在于,只要上述两个方法不返回null,那么startActivity一定可以成功。如果不用这个标记位,那么就可以把intent-filter中不包含该DEFAULT的category的Activity匹配出来,导致startActivity失败。因为不包含DEFAULT的category的Activity是无法接收隐式Intent的。针对Service和BroadcastReceiver,PackageManager也提供了类似的方法用于判断是否有匹配成功的组件信息。

附言

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

推荐阅读更多精彩内容