【转】Android基础总结十一:intent-filter的action,category,data匹配规则

有两种方式来启动Activity,显式调用和隐式调用。显式调用需要指定包名、类名、组件名,隐式调用则涉及到IntentFilter的匹配问题。大多情况都是使用显式调用,比如打开一个activity、service。隐式调用使用得比较少,使用情况为不知道应用的包名,但是又想使用相应的功能,比如打开浏览器、打开邮件、打开应用市场、打开相机应用、打开分享应用等。拿打开浏览器来说,手机上可能存在多个浏览器,我们一般无法知道他们的包名,那么可以使用以下代码:

Intent intent = new Intent(); 
intent.setAction("android.intent.action.View"); 
intent.setDate(Uri.parser("http://xxxxxx")); 
startActivity(intent);

执行完这段代码后,系统出弹出一个对话框,列出所有的浏览器应用供选择,用户选择任意一个浏览器应用,即可浏览网页。

那么首先我们先具体分析下显式调用和隐式调用的原理:

1.Activity的调用模式

a.显式调用

显示调用需要明确的指出被启动的对象的组件信息、包括包名和类名

示例1:通过包名打开一个应用

Intent intent = new Intent(Intent.ACTION_MAIN); 
intent.addCategory(Intent.CATEGORY_LAUNCHER); 
ComponentName cn = new ComponentName("com.mg.axe.testappa","com.mg.axe.testappa.MainActivity"); 
intent.setComponent(cn);
startActivity(intent);

示例2: 打开一个Activity

Intent intent = new Intent(ActivityA.this,ActivityB.class); 
startActivity(intent);

b、隐式调用

需要Intent能匹配目标组件的IntentFilter中所设置的过滤信息.如果不匹配将无法启动目标Activity

示例1:通过action方式匹配对应的Activity

Intent intent = new Intent(); 
intent.setAction("android.intent.action.View"); 
startActivity(intent);

运行结果会像这样

为什么会匹配到这么多应用的Activity? 因为在这些Activity的IntentFilter匹配规则中有如下规则:

(由于我们这里没有匹配其他的条件,所以会匹配到很多应用的Activity,我们可以添加其他的匹配条件,比如入“category”,“data”的匹配来更加精确的匹配到所需要的Activity)

通过上面的实例,大概了解了显示调用和隐式调用的方式。接下来我们将重点放在隐式调用的IntentFilter的匹配中。

2.Action的匹配规则

Intent中的Action必须能够和Activity过滤规则中的Action匹配.(这里的匹配是完全相等). 一个过滤规则中有多个action,那么只要Intent中的action能够和Activity过滤规则中的任何一个action相同即可匹配成功。

在AndroidManifest中添加AActivity的action的匹配规则

<activity 
    android:name=".AActivity"          
    android:label="@string/app_name" 
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter> 
        <category android:name = "android.intent.category.DEFAULT" /> 
        <action android:name="com.axe.mg.what" /> 
    </intent-filter> 
</activity>

然后调用以下方法即可匹配到AActivity:

public void match(){
    Intent intent = new Intent();
    intent.setAction("com.axe.mg.what");
    startActivity(intent);
}

注意:
1、系统定义了一些Action。当然我们也可以自己定义action。比如<action android:name="com.axe.mg.what" />

2、在Activity中定义的Action匹配规则可能有多个,只要Intent中的action能够和Activity过滤规则中的任何一个action相同即可匹配成功。

3.category的匹配规则

如果Intent中的存在category,那么这些category都必须和Activity过滤规则中的category相同,才能和这个Activity匹配。Intent中的category数量可能少于Activity中配置的category数量,但是Intent中的这category必须和Activity中配置的category相同才能匹配。通俗的讲就是,比如Intent中有3个category,activity的过滤规则中有5个category,那intent中的3个category需要是activity的过滤规则中有5个category中的3个,若有任意一个未出现在这5个里面,匹配就失败。

我们在Activity中配置category匹配规则:

<activity 
    android:name=".AActivity" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter> 
        <category android:name = "android.intent.category.DEFAULT" /> 
        <category android:name="aaa.bb.cc"/> 
        <action android:name="com.axe.mg.what" /> 
    </intent-filter> 
</activity>

运行以下代码可以匹配到AActivity:

public void match()
{ 
    Intent intent = new Intent();
    intent.addCategory("aaa.bb.cc"); 
    intent.setAction("com.axe.mg.what"); 
    startActivity(intent); 
}

注意:
1、只通过category匹配是无法匹配到AActivity的。因为category属性是一个执行Action的附加信息。
所以只靠category是无法匹配的。像如下代码:

//没有setAction()无法匹配 
public void match()
{
    Intent intent = new Intent(); 
    intent.addCategory(Intent.CATEGORY_DEFAULT); 
    intent.addCategory("aaa.bb.cc"); 
    startActivity(intent); 
}

4.data的匹配规则

类似于action匹配,但是data有更复杂的结构

a.data的结构

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

data 由两部分组成:MineType 和 URI

MineType指的是媒体类型:例如imgage/jpeg,auto/mpeg4和viedo/*等,可以表示图片、文本、视频等不同的媒体格式

URl 可配置更多信息,类似于url。

我们可以看下URI的结构:

<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
content://com.axe.mg:100/fold/subfolder/etc
http://www.axe.com:500/profile/info

我们看下URL的属性:

Scheme: URL的模式,如果URI中没有指定Scheme.那么整个URI无效。默认值为content 和 file。

Host: URI的host。比如www.axe.com。如果指定了scheme和port,path等其他参数,但是host未指定,那么整个URI无效;如果只指定了scheme,没有指定host和其他参数,URI是有效的。可以这样理解:一个完整的URI :http://www.axe.com:500/profile/info 我将后面的prot 和path“:500/profile/info ”去掉,这个URI任然有效。如果我单独将www.axe.com 那这个URI就无效了。

Port: URI端口,当URI指定了scheme 和 host 参数时port参数才有意义。

path: 用来匹配完整的路径,如:http://example.com/blog/abc.html,这里将 path 设置为 /blog/abc.html 才能够进行匹配;

pathPrefix: 用来匹配路径的开头部分,拿上面的 Uri 来说,这里将 pathPrefix 设置为 /blog 就能进行匹配了;

pathPattern: 用表达式来匹配整个路径。
这里需要说下匹配符号与转义。
i.匹配符号:
“*” 用来匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…
“.” 用来匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…
因此 “.*” 就是用来匹配任意字符0次或更多,如:“.*html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”…
* ii.转义:*
因为当读取 Xml 的时候,“/” 是被当作转义字符的(当它被用作 pathPattern 转义之前),因此这里需要两次转义,读取 Xml 是一次,在 pathPattern 中使用又是一次。如:“” 这个字符就应该写成 “//”,“.” 这个字符就应该写成 “//.”,“/” 这个字符就应该写成 “”。

使用案例:
(1)如果我们想要匹配 http 以 “.pdf” 结尾的路径,使得别的程序想要打开网络 pdf 时,用户能够可以选择我们的程序进行下载查看。
我们可以将 scheme 设置为 “http”,pathPattern 设置为 “.*//.pdf”,整个 intent-filter 设置为:

<intent-filter>  
    <action android:name="android.intent.action.VIEW"></action>  
    <category android:name="android.intent.category.DEFAULT"></category>  
    <data android:scheme="http" android:pathPattern=".*//.pdf"></data>  
</intent-filter>

(2)如果我们做的是一个IM应用,或是其他类似于微博之类的应用,如何让别人通过 Intent 进行调用出现在选择框里呢?我们只用注册 android.intent.action.SEND 与 mimeType 为 “text/plain” 或 “/” 就可以了,整个 intent-filter 设置为:

<intent-filter>  
    <action android:name="android.intent.action.SEND" />  
    <category android:name="android.intent.category.DEFAULT" />  
    <data mimeType="*/*" />  
</intent-filter> 

这里设置 category 的原因是,创建的 Intent 的实例默认 category 就包含了 Intent.CATEGORY_DEFAULT ,google 这样做的原因是为了让这个 Intent 始终有一个 category。

b.一些实例

(1)只匹配scheme

<activity 
    android:name=".CActivity" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter> 
        <action android:name="test" /> 
        <category android:name="android.intent.category.DEFAULT" /> 
        <data android:scheme="axe" /> 
    </intent-filter> 
</activity> 

匹配该Activity只需要data中scheme为axe就能匹配到

public void match()
{ 
    Intent intent=new Intent(); 
    //只设置Intent的Data属性 
    intent.setData(Uri.parse("axe://haha")); 
    startActivity(intent); 
}

(2)匹配 scheme host port

<activity 
    android:name=".CActivity" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter> 
        <action android:name="test" /> 
        <category android:name="android.intent.category.DEFAULT" /> 
        <data android:host="www.axe.com" android:port="8888" android:scheme="axe" /> 
    </intent-filter> 
</activity>

匹配这个Activity需要 scheme 为 axe ,host 为 www.axe.com, port为8888才能匹配。 只要有一个不正确都无法匹配

public void match(View view)
{ 
    Intent intent=new Intent(); 
    //只设置Intent的Data属性 
    intent.setData(Uri.parse("axe://www.axe.com:8888/mypath")); 
    startActivity(intent); 
}

(3)匹配 scheme host path

<activity 
    android:name=".CActivity" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter> 
        <action android:name="xx" /> 
        <category android:name="android.intent.category.DEFAULT" /> 
        <data android:host="www.axe.com" android:path="/mypath" android:scheme="axe" /> 
    </intent-filter> 
</activity>

匹配这个Activity 必须 scheme为 axe, host 为 www.axe.com, path 为 mypath才能匹配

public void match(View view) 
{ 
    Intent intent = new Intent(); 
    intent.setData(Uri.parse("axe://www.axe.com:4545/mypath")); 
    //port不写任然能匹配. data中没有要求做匹配 
    //intent.setData(Uri.parse("axe://www.axe.com/mypath")); 
    startActivity(intent); 
}

(4) 匹配 scheme host port path

<activity 
    android:name=".CActivity" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter> 
        <action android:name="xx" /> 
        <category android:name="android.intent.category.DEFAULT" /> 
        <data 
            android:host="www.axe.com" 
            android:path="/mypath" 
            android:port="8888" 
            android:scheme="axe" /> 
    </intent-filter> 
</activity>

匹配这个Activity 必须 scheme为 axe,,host 为 www.axe.com, path 为 mypath,port 为8888 才能匹配

public void match(V) 
{ 
    Intent intent = new Intent(); 
    intent.setData(Uri.parse("axe://www.axe.com:8888/mypath")); 
    startActivity(intent); 
}

(5)mintype匹配

<activity 
    android:name=".CActivity" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter> 
        <action android:name="xx" /> 
        <category android:name="android.intent.category.DEFAULT" /> 
        <data 
            android:mimeType="axe/abc" 
            android:host="www.axe.com"
            android:path="/mypath" 
            android:port="8888" 
            android:scheme="axe" /> 
    </intent-filter> 
</activity>

可以看到我们添加了mimeType。这种匹配方法我们需要做改变。我们不能使用
setType 和 setData , 需要使用setDataAndType()。
从源码可以看出:
setType() 会将URL设为null; setData()会将mineType设为null;
以下为源码:

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

匹配这个Activity 必须 scheme为 axe, host 为 www.axe.com, path 为 mypath,port 为8888, mineType 为 axe/abc才能匹配。

public void match() 
{ 
    Intent intent = new Intent(); 
    //注意这个方法 
    intent.setDataAndType(Uri.parse("axe://www.axe.com:8888/mypath"),"axe/abc"); 
    startActivity(intent); 
}

(6)Scheme的默认值content 和 file。

<activity 
    android:name=".CActivity" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter> 
        <action android:name="xx" /> 
        <category android:name="android.intent.category.DEFAULT" /> 
        <data android:mimeType="image/*" /> 
    </intent-filter> 
</activity>

上面的配置中我们并没有指定scheme。我们可以通过默认值content 和 file匹配。

public void match(){ 
    Intent intent=new Intent(); 
    //content也可匹配 
    intent.setDataAndType(Uri.parse("file://axe"),"image/png"); 
    startActivity(intent); 
}

(7)存在多个data的匹配

一个Activity只要能匹配任何一组data,并且每个data都指定了完整的属性(有时候匹配不上, 这个规律还未找到)

<activity 
    android:name=".CActivity" 
    android:label="@string/app_name"
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter>
        <action android:name="xx" /> 
        <category android:name="android.intent.category.DEFAULT" /> 
        <!-- 只要Intent的Data属性的scheme是lee,即可启动该Activity --> 
        <data 
            android:mimeType="axe/abc" 
            android:host="www.axe.com" 
            android:path="/mypath" 
            android:port="8888" 
            android:scheme="axe" />
        <data 
            android:mimeType="axe/ddd" 
            android:host="www.axe.com" 
            android:path="/mypath" android:port="8888" 
            android:scheme="axee" /> 
    </intent-filter> 
</activity>

以下两种方式都可以匹配.但是有时候会匹配不到.暂时不知道规律.建议只使用一个data.如果有多个规则需要匹配. 那就添加intent-filter

public void match() 
{ 
    Intent intent = new Intent();
    intent.setDataAndType(Uri.parse("axe://www.axe.com:8888/mypath"),"axe/abc"); 
    startActivity(intent); 
} 

public void match() 
{ 
    Intent intent = new Intent(); 
    intent.setDataAndType(Uri.parse("axee://www.axe.com:8888/mypath"),"axe/ddd"); 
    startActivity(intent); 
}

4.其他

a.无法匹配时的crash log

//类似于这样。 
Caused by: android.content.ActivityNotFoundException: No Activity found to handle Intent { cat=[android.intent.category.DEFAULT,aaa.bb.cc] } 
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1781) 
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1501) 
at android.app.Activity.startActivityForResult(Activity.java:3804) 
at android.app.Activity.startActivityForResult(Activity.java:3765)

b.一个Activity只要能匹配任何一组intent-filter,即可成功启动对应的Activity

<activity 
    android:name=".CActivity" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme.NoActionBar"> 
    <intent-filter> 
        <action android:name="xx" />
        <category android:name="android.intent.category.DEFAULT" /> 
        <data 
            android:mimeType="axe/abc" 
            android:host="www.axe.com"
            android:path="/mypath" 
            android:port="8888" 
            android:scheme="axe" /> 
    </intent-filter> 
    <intent-filter> 
        <action android:name="xx" /> 
        <category android:name="android.intent.category.DEFAULT" /> 
        <data 
            android:mimeType="axe/ddd" 
            android:host="www.axe.com" 
            android:path="/mypath" 
            android:port="8888" 
            android:scheme="axee" /> 
    </intent-filter> 
</activity>

以下两种方式都可以匹配

public void match() 
{
    Intent intent = new Intent(); 
    intent.setDataAndType(Uri.parse("axe://www.axe.com:8888/mypath"),"axe/abc"); 
    startActivity(intent); 
} 
public void match() 
{ 
    Intent intent = new Intent(); 
    intent.setDataAndType(Uri.parse("axee://www.axe.com:8888/mypath"),"axe/ddd"); 
    startActivity(intent); 
}

c.经常看看到的action和category说明

android.intent.action.MAIN 决定一个应用程序最先启动那个组件
android.intent.category.LAUNCHER 决定应用程序是否显示在程序列表里(说白了就是是否在桌面上显示一个图标)
android.intent.category.HOME 按住“HOME”键,该程序显示在HOME列表里。

第一种情况:有MAIN,无LAUNCHER,程序列表中无图标
原因:android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里

第二种情况:无MAIN,有LAUNCHER,程序列表中无图标
原因:android.intent.action.MAIN决定应用程序最先启动的Activity,如果没有Main,则不知启动哪个Activity,故也不会有图标出现
所以这两个属性一般成对出现。
如果一个应用中有两个组件intent-filter都添加了android.intent.action.MAIN和
android.intent.category.LAUNCHER这两个属性, 则这个应用将会显示两个图标, 写在前面的组件先运行。

from: https://blog.csdn.net/lixpjita39/article/details/78201689

文章乃参考、转载其他博客所得,仅供自己学习作笔记使用!!!
原文地址:https://www.cnblogs.com/xuan52rock/p/14810416.html