Android中系统组件的启动方式有显式启动和隐式启动,我们今天主要就了解一下隐式启动中的匹配规则。
1、Intent Filter匹配规则
只有action、category、data三方都匹配,Intent才算是匹配成功,进而才能打开相应的Component。一个Component若声明了多个Intent Filter,只需要匹配任意一个即可启动该组件。
<activity
android:name="PagerActivity"
android:label="@string/title_activity_view_pager"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
intent-filter>
<intent-filter>
<action android:name="com.angcyo.activity" />
<category android:name="com.angcyo"/>
intent-filter>
<intent-filter>
<action android:name="com.angcyo.activity" />
<category android:name="com.angcyo"/>
<data android:mimeType="image/*"/>
intent-filter>
activity>
其中 intent-filter 声明的部分就是此Activity匹配规则,每声明一个 intent-filter , 就定义了一组匹配规则,匹配的时候,只要匹配其中任意一组,就能启动此Activity。
action的匹配规则
一个Intent Filter中可声明多个action,Intent中的action与其中的任一个action在字符串形式上完全相同(区分大小写),action方面就匹配成功。可通过setAction方法为Intent设置action,也可在构造Intent时传入action。需要注意的是,隐式Intent必须指定action(如不指定action则必须指定data或mimetype,这种情况下,只要IntentFilter至少含有一个action就可以匹配)。
category的匹配规则
Intent中的category必须都在Intent Filter中出现才算匹配成功。Intent可以不指定category,若Intent中未指定category,系统会自动为它带上“android.intent.category.DEFAULT”。所以,想要接收隐式Intent的Component都必须在manifest文件中的Intent Filter声明中带上“android.intent.category.DEFAULT”,我们可以通过addCategory方法为Intent添加category。
data的匹配规则
data可进一步分为uri(由scheme、host、port、path | pathPattern | pathPrefix这4部分组成)和mimetype。
Intent的uri可通过setData方法设置,mimetype可通过setType方法设置。同action类似,只要Intent的data只要与Intent Filter中的任一个data声明完全相同,data方面就匹配成功。
需要注意的是:若Intent Filter的data声明部分未指定uri,则缺省uri为content或file,Intent中的uri的scheme部分需为content或file才能匹配,若要为Intent指定完整的data,必须用setDataAndType方法。
首先我们先来看一下Intent Filter中指定data的语法:
<data android:scheme="...“
android:host="..."
android:port="..."
android:path="..."
android:pathPattern="..."
android:pathPrefix="..."
android:mimeType="..." />
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
2、示例
(1)只匹配scheme
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.a"/>
<action android:name="android.intent.action.b"/>
<category android:name="android.intent.category.a"/>
<category android:name="android.intent.category.b"/>
<category android:name="android.intent.category.c"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="dxf"/>
</intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction("android.intent.action.a");
intent.addCategory("android.intent.category.a");
intent.setData(Uri.parse("dxf://adc"));
startActivity(intent);
(2)匹配 scheme host port
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.a"/>
<action android:name="android.intent.action.b"/>
<category android:name="android.intent.category.a"/>
<category android:name="android.intent.category.b"/>
<category android:name="android.intent.category.c"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="dxf" android:host="www.dxf.com" android:port="8888" />
</intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction("android.intent.action.a");
intent.addCategory("android.intent.category.a");
intent.setData(Uri.parse("dxf://www.dxf.com:8888"));
startActivity(intent);
(3)匹配 scheme host path
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.a"/>
<action android:name="android.intent.action.b"/>
<category android:name="android.intent.category.a"/>
<category android:name="android.intent.category.b"/>
<category android:name="android.intent.category.c"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="dxf" android:host="www.dxf.com" android:path="/mypath" />
</intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction("android.intent.action.a");
intent.addCategory("android.intent.category.a");
intent.setData(Uri.parse("dxf://www.dxf.com:8080/mypath"));
startActivity(intent);
(4)匹配 scheme host port path
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.a"/>
<action android:name="android.intent.action.b"/>
<category android:name="android.intent.category.a"/>
<category android:name="android.intent.category.b"/>
<category android:name="android.intent.category.c"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="dxf" android:host="www.dxf.com" android:port="8888" android:path="/mypath" />
</intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction("android.intent.action.a");
intent.addCategory("android.intent.category.a");
intent.setData(Uri.parse("dxf://www.dxf.com:8888/mypath"));
startActivity(intent);
(5)mimetype匹配
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.a"/>
<action android:name="android.intent.action.b"/>
<category android:name="android.intent.category.a"/>
<category android:name="android.intent.category.b"/>
<category android:name="android.intent.category.c"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*" />
</intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction("android.intent.action.a");
intent.addCategory("android.intent.category.a");
intent.setType("image/png");
//Scheme的默认值content/file
//intent.setDataAndType(Uri.parse("content://adc"),"image/png");
startActivity(intent);
(6)全匹配
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.a"/>
<action android:name="android.intent.action.b"/>
<category android:name="android.intent.category.a"/>
<category android:name="android.intent.category.b"/>
<category android:name="android.intent.category.c"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="dxf" android:host="www.dxf.com" android:port="8888" android:path="/mypath" android:mimeType="dxf/adc"/>
</intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction("android.intent.action.a");
intent.addCategory("android.intent.category.a");
intent.setDataAndType(Uri.parse("dxf://www.dxf.com:8888/mypath"),"dxf/adc");
startActivity(intent);
3、其他注意点
(1)存在多个data匹配
一个Activity只要能匹配任何一组data,并且每个data都指定了完整的属性。
<intent-filter>
<action android:name="xx" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="dxf/abc"
android:host="www.dxf.com"
android:path="/mypath"
android:port="8888"
android:scheme="dxf" />
<data
android:mimeType="dxf/ddd"
android:host="www.dxf.com"
android:path="/mypath"
android:port="8888"
android:scheme="dxf_dxf" />
intent-filter>
但有时匹配不上,暂不知其规律,建议只使用一个data。如果有多个规则需要匹配,那就添加intent-filter。
(2)多个intent-filter匹配
<intent-filter>
<action android:name="xx" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="dxf/abc"
android:host="www.dxf.com"
android:path="/mypath"
android:port="8888"
android:scheme="dxf" />
intent-filter>
<intent-filter>
<action android:name="xx" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="dxf/ddd"
android:host="www.dxf.com"
android:path="/mypath"
android:port="8888"
android:scheme="dxf_dxf" />
intent-filter>
一个Activity只要能匹配任何一组intent-filter,即可成功启动对应的Activity。
(3)无法匹配时的crash log
Caused by: android.content.ActivityNotFoundException: No Activity found to handle Intent { cat=[android.intent.category.DEFAULT] }
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)
(4)LAUNCHER启动的Activity都需要配置
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
4、判断是否有匹配的Intent
(1)intent.resolveActivity()
ComponentName name = intent.resolveActivity(getPackageManager());
(2)getPackageManager().resolveActivity()
ResolveInfo info = getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
(3)getPackageManager().queryIntentActivities()
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);
5、附件
常用的schema有http、ftp、file、content等;
常用的mimeType有:
{".*","*/*"},
{".3gp", "video/3gpp"},
{".apk", "application/vnd.android.package-archive"},
{".asf", "video/x-ms-asf"},
{".avi", "video/x-msvideo"},
{".bin", "application/octet-stream"},
{".bmp", "image/bmp"},
{".c", "text/plain"},
{".class", "application/octet-stream"},
{".conf", "text/plain"},
{".cpp", "text/plain"},
{".doc", "application/msword"},
{".exe", "application/octet-stream"},
{".gif", "image/gif"},
{".gtar", "application/x-gtar"},
{".gz", "application/x-gzip"},
{".h", "text/plain"},
{".htm", "text/html"},
{".html", "text/html"},
{".jar", "application/java-archive"},
{".java", "text/plain"},
{".jpeg", "image/jpeg"},
{".jpg", "image/jpeg"},
{".js", "application/x-javascript"},
{".log", "text/plain"},
{".m3u", "audio/x-mpegurl"},
{".m4a", "audio/mp4a-latm"},
{".m4b", "audio/mp4a-latm"},
{".m4p", "audio/mp4a-latm"},
{".m4u", "video/vnd.mpegurl"},
{".m4v", "video/x-m4v"},
{".mov", "video/quicktime"},
{".mp2", "audio/x-mpeg"},
{".mp3", "audio/x-mpeg"},
{".mp4", "video/mp4"},
{".mpc", "application/vnd.mpohun.certificate"},
{".mpe", "video/mpeg"},
{".mpeg", "video/mpeg"},
{".mpg", "video/mpeg"},
{".mpg4", "video/mp4"},
{".mpga", "audio/mpeg"},
{".msg", "application/vnd.ms-outlook"},
{".ogg", "audio/ogg"},
{".pdf", "application/pdf"},
{".png", "image/png"},
{".pps", "application/vnd.ms-powerpoint"},
{".ppt", "application/vnd.ms-powerpoint"},
{".prop", "text/plain"},
{".rar", "application/x-rar-compressed"},
{".rc", "text/plain"},
{".rmvb", "audio/x-pn-realaudio"},
{".rtf", "application/rtf"},
{".sh", "text/plain"},
{".tar", "application/x-tar"},
{".tgz", "application/x-compressed"},
{".txt", "text/plain"},
{".wav", "audio/x-wav"},
{".wma", "audio/x-ms-wma"},
{".wmv", "audio/x-ms-wmv"},
{".wps", "application/vnd.ms-works"},
{".xml", "text/xml"},
{".xml", "text/plain"},
{".z", "application/x-compress"},
{".zip", "application/zip"},