Intent Filter匹配规则,以及使用注意点

Intent 类别

  • 显式Intent
    直接指定要启动的组件的彻底限定名的Intent被称为显式Intent.在启动显式Intent的时候,系统将忽略Intent Filter,直接启动相应的组件(固然,若是目标组件设定了权限,还须要验证调用者是否得到相应权限)
  • 隐式Intent
    没有指定组件彻底限定名的Intent被称为隐式Intent.一般使用<intent-filter>标签配合<action>,<data>以及<category>来定义一个Intent的过滤规则

一般咱们应该优先使用显式Intent,这样不只性能会好一些,并且被启动的组件是肯定的(就是你经过类名所指定的那个)html

**注意:**若是targetSdkVersion>=21的话,startService,bindService不能使用隐式Intent,不然会抛出IllegalArgumentExceptionjava

ContextImpl.java
private void validateServiceIntent(Intent service) {
	if (service.getComponent() == null && service.getPackage() == null) {
		if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
			IllegalArgumentException ex = new IllegalArgumentException("Service Intent must be explicit: " + service);
			throw ex;
		} else {
			Log.w(TAG, "Implicit intents with startService are not safe: " + service
                        + " " + Debug.getCallers(2, 3));
		}
	}
}

隐式Intent的匹配

  • action
    <intent-filter>中能够定义0个或多个<action>
    • 只要Intent中的action(Intent.getAction)包含在<intent-filter>之中,那么action匹配环节就经过了
    • 若是Intent没有设置action,那么只要<intent-filter>定义了<action>,那么action匹配环节也会经过

**须要注意的是:**若是<intent-filter>中没有定义任何<action>,那么不管你发送的Intent是否设置了action,这个<intent-filter>都不会匹配android

  • category
    <intent-filter>中能够定义0个或者多个<category>
    • 只有当Intent中设置的category<intent-filter>中所定义的category的子集时,category匹配环节才会经过(对于没有设置categoryIntent,这个环节始终经过)

**须要注意的是:**若是隐式Intent配合startActivity或者startActivityForResult使用,那么要想匹配,<intent-filter>中须要加入android.intent.category.DEFAULT,缘由是Android framework会加上PackageManager.MATCH_DEFAULT_ONLYapp

能够参考Intent.java
public ComponentName resolveActivity(PackageManager pm) {
	if (mComponent != null) {
		return mComponent;
	}
	ResolveInfo info = pm.resolveActivity(this, PackageManager.MATCH_DEFAULT_ONLY); // 这儿
	if (info != null) {
		return new ComponentName(info.activityInfo.applicationInfo.packageName,info.activityInfo.name);
	}
	return null;
}
  • data
    <intent-filter>能够定义0个或多个<data>标签
    data包括2部分类容:URI, mimeType
    • URI的匹配
      URI包括schema,host,port,path. * 若是没有指定schema,那么host将被忽略 * 若是没有指定host,那么port将被忽略 * 若是schemahost都没有指定,那么path将被忽略 * 若<data>中没有定义URI相关信息,那么只有Intent也没有定义URI信息,URI匹配才会经过 * 若Intent中的URI的相应部分匹配<data>中定义的URI的有效部分,那么URI匹配经过(例如: <data>中只定义了schema,那么只要Intent中的URI的schema和其匹配,那么URI匹配经过) * 若Intent中的URI的schema为content或者file,且<data>中没有指定URI,那么URI匹配经过ide

      • mimeType的匹配
        • */*匹配全部mimeType
        • image/*匹配全部主类型为image的mimeType
        • text/html匹配text/html
        • 只有当<data>没有指定mimeType且Intent也没有指定mimeType,或者<data>Intent都指定了mimeType,且匹配时,mimeType匹配才经过
      • 只有URI和mimeType都匹配经过时,data匹配才经过
        **注意:**当定义了多个<data>标签时,至关于定义了一组mimeType和一组URI,只有和这一组mimeType中的一个mimeType匹配以及这一组URI中的URI匹配时,<data>匹配才经过
      例如
      <intent-filter>
      	<action android:name="com.yyter.intentfilter.ignored"/>
      	<data android:mimeType="vnd.android.cursor.item/vnd.com.yyter.intentfilter.action.target"/>
      	<data android:scheme="yyter"/>
      	<data android:mimeType="image/*" android:scheme="http" android:host="android.com"/>
      	<data android:scheme="http" android:host="www.baidu.com"/>
      	<category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
      
      1.intent.setType("image/jpeg");//不匹配
      2.intent.setDataAndType(Uri.parse("yyter://"), "image/jpeg");//不匹配
      3.intent.setDataAndType(Uri.parse("yyter://www.baidu.com"), "image/jpeg");//匹配
      4.intent.setDataAndType(Uri.parse("yyter://android.com"), "image/jpeg");//匹配
      5.intent.setDataAndType(Uri.parse("http://android.com"), "vnd.android.cursor.item/vnd.com.yyter.intentfilter.action.target");//匹配
      ...

      **建议:**一个<data>标签要么只定义mimeType, 要么只定义URI,不要混在一块儿性能

  • 只有action,categorydata都匹配经过时,<intent-filter>才匹配,一个组件能够定义多个<intent-filter>,只要其中一个<intent-filter>匹配,那么这个组件就经过匹配.

**注意:**对于定义了<intent-filter>的组件,那么它将是对外可见的,若是只是app内部使用,能够在组件标签中加上android:exported="false"ui

0个或多个组件匹配的状况

  • Broadcast
    不管0个仍是多个BroadcastReceiver匹配,发送者都没有感知
  • Service
    • targetSdkVersion >= 21时,使用隐式Intent启动Service,会抛出IllegalArgumentException
    • targetSdkVersion <= 20时,
      • 0个service匹配时,startService返回null
      • 多个service能够匹配时,只有第一个匹配的service被启动,并返回那个service的ComponentName(Android,找到一个匹配的service后,就不会接着寻找了)
  • ContentProvider (ContentProvider是经过ContentResolver来使用的,并不使用Intent来启动,也不能定义<intent-filter>)
  • Activity
    • 0个Activity匹配时,抛出ActivityNotFoundException
    • 多个Activity匹配时
      • 若是有设定的默认应用,那么启动那个应用的Activity
      • 若是没有设定的默认应用,那么弹出应用选择框,由用户选择

备注(经过隐式Intent启动Activity)

  • 强制显示应用选择框
Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

if (sendIntent.resolveActivity(getPackageManager()) != null) {
	String title = getResources().getString(R.string.chooser_title);
	Intent chooser = Intent.createChooser(sendIntent, title);
    startActivity(chooser);
}

若是只有一个应用匹配时,将直接启动对应的Activity,不会弹出选择框this

  • 也可使用PackageManagerqueryIntentActivities方法,找出匹配的全部Activity,而后使用自定义UI来展现
相关文章
相关标签/搜索