我們知道,啟動(dòng)一個(gè)Activity分為兩用,隱式調(diào)用和顯式調(diào)用。原則上一個(gè)Intent不應(yīng)該即是顯式調(diào)用有是隱式調(diào)用,如果二者共存以顯式調(diào)用為主。
顯式調(diào)用
需要明確指定被啟動(dòng)對(duì)象的組件信息,包括包名和類名。一般是在相同的應(yīng)用程序內(nèi)部實(shí)現(xiàn)的。
以兩個(gè)Activity之間的跳轉(zhuǎn)為例:
Intent intent = new Intent(MainActivity.this, TestActivity.class);
startActivity(intent);
隱式調(diào)用
通過Intent Filter來實(shí)現(xiàn)的,它一般用在沒有明確指出目標(biāo)組件名稱的前提下。Android系統(tǒng)會(huì)根據(jù)隱式意圖中設(shè)置的動(dòng)作(action)、類別(category)、數(shù)據(jù)(URI和數(shù)據(jù)類型)找到最合適的組件來處理這個(gè)意圖。一般是用于在不同應(yīng)用程序之間。
下面來詳細(xì)分析各種屬性的匹配規(guī)則
action的匹配規(guī)則
一個(gè)intent filter中可以有多個(gè)action,那么只要intent中的action能和intent filter中的任何一個(gè)action相同集合匹配成功。需要注意的是,intent中沒有指定action,那么匹配失敗。除此之外,action區(qū)分大小寫。
category的匹配規(guī)則
category的匹配規(guī)則和action不同,intent中如果出現(xiàn)了category,不管有幾個(gè),都必須是intent filter中已經(jīng)定義的category,也就是說intent可以沒有category,一旦有,每個(gè)都必須和intent filter中定義的任意一個(gè)category相同。那為什么我們沒在intent filter中加android.intent.category.DEFAULT這個(gè)category會(huì)報(bào)錯(cuò)呢?原因是系統(tǒng)在調(diào)用startActivity或者startActivityForResult的時(shí)候會(huì)默認(rèn)在intent中加上android.intent.category.DEFAULT這個(gè)category。
還是以兩個(gè)Activity之間的跳轉(zhuǎn)為例:
Intent intent = new Intent();
intent.setAction("com.dev.test");
intent.addCategory("com.dev.testcat");
startActivity(intent);
AndroidManifest.xml中
<activity android:name=".TestActivity">
<intent-filter>
<action android:name="com.dev.test"/>
<category android:name="com.dev.testcat"/>
//必須加上,否者報(bào)錯(cuò)
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
data的匹配規(guī)則
data的匹配規(guī)則和action類似,如果intent filter中定義了data,那么intent中也必須要定義可匹配的data,否者匹配失敗。
首先來了解一下data的結(jié)構(gòu),data由兩部分組成,mimeType和URI。mimeType指媒體類型,比如image/jpeg、text/plaind、video/*等,可以表示圖片、文本、視頻等不同的媒體格式。
了解了data的結(jié)構(gòu),接下來開始介紹data的過濾規(guī)則,下面分情況說明
- 情況一:data規(guī)則不完整。如下所示
<intent-filter>
<data android:mimeType="image/*"/>
</intent-filter>
這種規(guī)則指定了媒體類型為所有類型的圖片,那么intent中的mimeType屬性必須為“image/*”才能匹配成功,這種情況雖然沒有指定URI,但intent中的URL部分的schema默認(rèn)值為content和file。也就是說雖然沒有指定URI,但是intent中的URI部分的schema必須為content或者file才能匹配成功,這點(diǎn)尤其需要注意。
- 情況二:定義了多組data規(guī)則,并且每個(gè)data都定義了完整屬性,既有URI又有mimeType。如下所示
<intent-filter>
<data android:mimeType="video/mpeg" android:scheme="http" ... />
<data android:mimeType="audio/mpeg" android:scheme="http" ... />
...
</intent-filter>
為了匹配這種intent filter,我們也需要在intent中完整定義其中一組data規(guī)則才能匹配成功。另外,如果要為intent指定完整的data,必須調(diào)用setDataAndType方法,不能先調(diào)用setData再調(diào)用setType,因?yàn)檫@兩個(gè)方法彼此會(huì)清除對(duì)方的值。
最后,但我們通過隱式方式啟動(dòng)一個(gè)Activity時(shí),可以做一下判斷,看是否有Activity能夠匹配我們的intent,以免不必要的奔潰。判斷方法有兩種:一是采用PackageManager的resolveActivity方法,二是采用Intent的resolveActivity方法,如果他們找不到匹配的Activity就會(huì)返回null,根據(jù)其返回值我們就規(guī)避上述問題了。
首先我們來看看Intent的resolveActivity方法源碼
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;
}
從中我們可以看出其內(nèi)部也是直接調(diào)用了PackageManager的resolveActivity方法來判斷匹配是否成功。
我們?cè)賮砜纯碢ackageManager的resolveActivity方法
public abstract ResolveInfo resolveActivity(Intent intent, int flags);
第一個(gè)參數(shù)很好理解,就是我們要解析的intent,第二個(gè)參數(shù)源碼中使用的是MATCH_DEFAULT_ONLY這個(gè)標(biāo)記位,這個(gè)標(biāo)記位的含義是僅僅匹配那些在intent filter中聲明了<category android:name="android.intent.category.DEFAULT"/>這個(gè)category的Activity。使用它的意義在于只要此方法不返回null,那么startActivity一定可以成功。最后是示例:
Intent intent = new Intent();
intent.setAction("com.dev.test");
intent.setType("image/*");
ComponentName componentName = intent.resolveActivity(getPackageManager());
if (componentName == null) {
//如果為null,表示匹配失敗
Toast.makeText(MainActivity.this, "匹配失敗", Toast.LENGTH_SHORT).show();
} else {
//如果不為null,啟動(dòng)Activity
startActivity(intent);
}