Intent详解 (二) : 隐式Intent

来源:互联网 发布:js监听手机键盘事件 编辑:程序博客网 时间:2024/04/27 22:39

《Intent详解(一):显式Intent》
《Intent详解(二):隐式Intent》
《Intent详解(三):Intent的Flags》
《Intent详解(四):使用隐式Intent》

隐式Intent

所谓的隐式Intent与显式Intent的明显区别就是隐式Intent不用指定要启动的组件的信息。如果二者共存则以显式Intent调用为主。那隐式Intent有什么用呢?例如我们现在想查看一张照片,但是我们不知道哪个Activity具有这个功能,那我们就可以通过隐式Intent来查找具有这个功能的Activity,并启动它。

隐式Intent大致工作原理就是我们如果想要启动某类组件那么我们就要对Intent指定相关属性、这些属性可以描述我们想要启动组件的特性,然后系统会根据我们的描述去查找相关的组件。那么系统是怎么查找的呢?那我们要先来说一下AndroidManifest.xml文件,我们在这个文件中配置组件时,例如配置Activity时有些会在其中配置有标签,这个标签中的内容就是描述这个Activity的匹配规则,例如我们创建新工程时主Activity的配置:

<activity    android:name=".MainActivity">    <intent-filter>        <action android:name="android.intent.action.MAIN" />        <category android:name="android.intent.category.LAUNCHER" />    </intent-filter></activity>

我们看到MainActivity有配有intent-filter,并在其中指定了action为android.intent.action.MAIN和category为android.intent.category.LAUNCHER,这就是系统预先定义好的,只有配置这两个信息才是作为APP的“主Activity”。

接下来我们就对这些配置信息进行详细的说明。

IntentFilter中的过滤信息有action、category、data

我们通过下面的代码进行说明各个属性的匹配规则:

<activity android:name=".TestActivity">    <intent-filter>        <action android:name="com.todo.action1"/>        <action android:name="com.todo.action2"/>        <category android:name="android.intent.category.DEFAULT"/>        <category android:name=“com.todo.category1"/>        <data android:mimeType="image/*"/>    </intent-filter></activity>

action的匹配规则

action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action。

action的匹配规则是Intent的action必须能够和IntentFilter规则中的action匹配,这里说的匹配是指action的字符串值完全一样。例如我们intent中设置了两个action,那么这两个action必须在intentFilter中全部匹配才算匹配成功。当然对于隐式的Intent必须至少有一个action。需要注意的是action却分大小写。

category的匹配规则

category是一个字符串,系统预定义了一些category,同时我们也可以在应用中定义自己的category。

category的匹配规则是:Intent可以没有category,但是如果有category,不管是几个category,则这些category必须每个都要与IntentFilter中的某个category能够匹配上,否则匹配失败。如果Intent没有category也能够匹配成功,原因是系统在调用startActivity或者startActivityForResult的时候会默认为Intent加上“android.intent.category.DEFAULT”这个category,所以如果使用隐式Intent则必须在要启动的组件的intent-filter中指定“android.intent.category.DEFAULT”这个category。

data 的匹配规则

data的匹配规则和action类似,如果过滤规则中定义了data,那么Intent重必须也要定义可匹配的data。

在介绍data的匹配规则之前,我们要先了解一下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/etchttp://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表示路径的前缀信息。

前面说到要求Intent中必须data数据,并且data数据能够完全匹配过滤规则中的某一个data。这里的完全匹配是指过滤规则中出现的data部分也出现在了Intent中的data中。下面分情况说明。

1)如下过滤规则:

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

这种规则制定了媒体类型为所有类型的图片,那么Intent重的mimeType属性必须为“image/*”才能匹配,这种情况下虽然过滤规则没有制定URI,但是却有默认值,URI的默认值为content和file。也就是说,虽然没有制定URI,但是Intent中的URI部分的scheme必须为content或者file才能匹配,这点是需要尤其注意的。为了匹配(1)中的规则我们可以写出如下实例:

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

另外,如果要为Intent制定完整的data,必须调用setDataAndType方法,不能先调用setData在调用setType,因为这两个方法彼此会清楚对方的值,这个看源码就很容易理解,比如setData:

public Intent setData(Uri data) {     mData = data;     mType = null;     return this;}

可以发现,setData会把mimeType设置为null,同理setType也会把URI设置为null。

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。为了匹配(2)中的规则,我们可以写出如下实例:

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

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

注:过中的scheme为http,则Intent中配置为Uri.parse(“http:”)则可以匹配成功。

通过上面的实例,我们应该已经明白了data的匹配规则,关于data还有一种特殊情况需要说明,这也是它和action不同的地方,如下两种特殊的写法,它们的作用是一样的:

<intent-filter>     <data android:scheme=“file” android:host=“www.baidu.com”/>     ...</intent-filter><intent-filter>     <data android:scheme=“file”/>     <data android:host=“www.baidu.com”/>     ...<intent-filter>

到这里我们已经把Intent与IntentFilter的匹配都讲解一遍了。

最后,当我们使用隐式Intent的时候,可以做一下判断,看看是否有能够匹配我们隐式Intent的组件,如果不做判断就有可能出现程序崩溃的问题。

判断方法有两种:

1)采用PackageManager的resolveActivity方法或者Intent的resolveActivity方法,如果他们找不到匹配的Activity就会返回null,我们通过判断返回值就可以避免上述错误了。例如:

if (intent.resolveActivity(getPackageManager()) != null) {          startActivity(intent);}if(getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {           startActivity(intent);}

2) 另外PackageManager还提供了queryIntentActivities方法,这个方法和resolveActivity方法不同的是他不是返回最佳匹配的Activity信息而是返回所有匹配成功的Activity信息。我们看看queryIntentActivities和resolveActivity的方法原型:

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

上述两个方法的第一个参数比较好理解,第二个参数需要注意,我们要使用MATCH_DEFAULT_ONLY这个标记为,这个标记为的含义是仅仅匹配那些在intent-filter中声明了这个category的组件。使用这个标记位的意义在于,只要上述两个方法不返回null,那么startActivity一定可以成功。如果不用这个标记位,就可以吧intent-filter中不含DEFAULT的那些Activity给匹配出来,从而导致startActivity可能失败。因为不含有DEFAULT这个category的Activity是无法接收隐式Intent的。

if(getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY) != null ){           startActivity(intent);}

在action和category中,有一类action和category比较重要,它们是:

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

这二者共同作用是用来标明这个是一个入口Activity并且会出现在系统的应用列表中,少量任何一个都没有实际意义,也就无法出现在系统的应用列表中,也就是二者缺一不可。另外针对Service和BroadcastReceiver,PackageManager同样提供了类似的方法去获取成功匹配的组件信息。

0 0
原创粉丝点击