Android 啟動 Activity 分為兩種,顯式調(diào)用和隱式調(diào)用,顯式調(diào)用需要明確地指定被啟動對象的組件信息,包括包名和類名,而隱式調(diào)用則不需要明確指定組件信息。原則上一個 Intent 不應(yīng)該即是顯式調(diào)用又是隱式調(diào)用,如果二者共存的化,以顯式調(diào)用為主。
顯式調(diào)用
顯式調(diào)用代碼如下。
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
隱式調(diào)用
隱式調(diào)用需要 Intent 能夠匹配目標(biāo)組件的 IntentFilter 所設(shè)置的過濾信息,如果不匹配將無法啟動目前Activity。IntentFilter 中的過濾信息有 action(動作)、category(類別)、data。
為了匹配過濾列表,需要同時匹配過濾列表中的 action、category、data 信息,否則匹配失敗。一個過濾列表中的 action、category 和 data 可以有多個,所有的 action、category、Data 分別構(gòu)成不同類別,同一類別的信息共同約束當(dāng)前類別的匹配過程。只有一個 Intent 同時匹配 action 類別、category 類別、data 類別才算完全匹配,只有完全匹配才能成功啟動目標(biāo) Activity。一個 Activity 中可以有多個 intent-filter,一個 Intent 只要能匹配任何一組 intent-filter 就能成功啟動對應(yīng)的 Activity。
action 的匹配規(guī)則
action 是一個字符串,系統(tǒng)預(yù)定義了一些 action,同時我們也可以自己在應(yīng)用中定義自己的 action。一個 intent-filter 里可以有多個 action,只要 Intent 中的 action 能夠和過濾規(guī)則中的任何一個 action 相同即可匹配成功。action 區(qū)分大小寫。
category 的匹配規(guī)則
category 是一個字符串,系統(tǒng)預(yù)定義了一些 category,同時我們也可以自己在應(yīng)用中定義自己的 category。要求 Intent 如果含有 category ,那么所有的 category 都category和過濾規(guī)則中的其中一個 category 相同。系統(tǒng)在調(diào)用 startActivity 或者 staActivityForResult 都會默認(rèn)加上 "android.intent.category.DEFAULT",所以必須在 intent-filter 加上 "android.intent.category.DEFAULT" 這個 category。
Intent intent = new Intent();
intent.setAction("com.test.second");
intent.addCategory("com.test.second.category");
startActivity(intent);
AnroidManifest.xml
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.test.second"/>
<category android:name="com.test.second.category"/>
<!--必須加上,否則報錯-->
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="com.test.second"/>
<category android:name="com.test.second.category2"/>
<!--必須加上,否則報錯-->
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
data 的匹配規(guī)則
data 的匹配規(guī)則和 action 類似,如果過濾規(guī)則中定義了 data,那么 Intent 必須要定義可匹配的 data。
data 由兩部分組成,mimeType 和 URI。mimeType 指媒體類型,比如 image/jpeg、text/plaind、video/* 等等,可以表示圖片、文本、視頻等不同的媒體格式。
URI中的結(jié)構(gòu)如下。
<scheme>//<host>:<port>/[<path> | <pathPrefix> | <pathPattern>]
scheme:URI 的模式,比如 http、file、content 等,如果 URI 沒有指定 scheme,那么整個 URI 的其他參數(shù)都無效,意味著 URI 是無效的。
host:URI 的主機(jī)號,如 www.xxx.com,如果 host 未指定,那么整個 URI 中的其他參數(shù)無效, 意味著 URI 是無效的。
port:URI 中的端口號,比如 80,僅當(dāng) URI 中指定了 scheme 和 host 參數(shù)的時候 port 參數(shù)才是有意義的。
path、pathPrefix 和 pathPattern:表示路徑信息,path 表示完整的路徑信息,pathPrefix 表示路徑的前綴信息,pathPattern 表示完整的路徑信息,但是它里面可以包含通配符 , 表示 0 個或多個任意字符,遵循正則表達(dá)式的規(guī)范,如果想要表示真實的字符串,* 要寫成 \\*,\ 要寫成 \\\\。
- 情況一:data 規(guī)則不完整
<intent-filter>
<data android:mimeType="image/*"/>
</intent-filter>
這種規(guī)則指定了媒體類型為所有類型的圖片,那么 Intent 中的 mimeType 屬性必須與 "image/*" 相匹配,這種情況下雖然過濾規(guī)則沒有指定 URI,但是卻有默認(rèn)值,URI 的默認(rèn)值為 content 和 file,所以 Intent 中的 URI 部分的 schema 必須為 content 或者 file 才能匹配。如下示例:
intent.setDataAndType(Uri.parse("file://abc"), "image/png");
如果要為 Intent 指定完整的 data,必須要調(diào)用 setDataAndType,不能先調(diào)用 setData 再調(diào)用 setType,因為這兩個方法會彼此清除對方的值。
public @NonNull Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
public @NonNull Intent setType(@Nullable String type) {
mData = null;
mType = type;
return this;
}
- 情況二:定義了多組data規(guī)則,并且每個data都定義了完整屬性,既有URI又有mimeType。
<intent-filter>
<data android:mimeType="video/mpeg" android:scheme="http" ... />
<data android:mimeType="audio/mpeg" android:scheme="http" ... />
</intent-filter>
intent.setDataAndType(Uri.parse("http://www.baidu.com"), "video/mpeg");
通過隱式調(diào)用方式啟動一個 Activity 的時候,可以做以下判斷,看是否有 Activity 能夠匹配隱式 Intent,如果不做判斷就有可能出現(xiàn)錯誤。判斷方法有兩種:一是采用PackageManager的resolveActivity方法,二是采用Intent的resolveActivity方法,如果他們找不到匹配的Activity就會返回null。
Intent intent = new Intent();
intent.setAction("com.test.second");
intent.setDataAndType(Uri.parse("http://www.baidu.com"), "video/mpeg");
ComponentName componentName = intent.resolveActivity(getPackageManager());
if (componentName == null) {
Toast.makeText(this, "匹配出錯", Toast.LENGTH_SHORT).show();
} else {
startActivity(intent);
}