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來展現
相關文章
相關標籤/搜索