隱式 Intent 相比於顯式 Intent,隱式 Intent 則含蓄了許多,它並不明確指出我們想要啓動哪一個活動,而是指定了一系列更爲抽象的 action和category等信息,然後交由系統去分析這個 Intent,並幫我們找出合適的活動去啓動。
什麼叫做合適的活動呢?簡單來說就是可以響應我們這個隱式 Intent的活動,那麼目前SecondActivity 可以響應什麼樣的隱式 Intent 呢?額,現在好像還什麼都響應不了,不過很快就會有了。
使用隱式 Intent
通過在<activity>
標籤下配置<intent-filter>
的內容,可以指定當前活動能夠響應的 action 和 category,打開 AndroidManifest.xml
,添加如下代碼:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="pub.weber.bym.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.example.activitytest.MY_CATEGORY"/>
</intent-filter>
</activity>
在<action>
標籤中我們指明瞭當前活動可以響應 com.example.activitytest.ACTION_START
這個 action,而<category>
標籤則包含了一些附加信息,更精確地指明瞭當前的活動能夠響應的 Intent 中還可能帶有的 category。只有<action>
和<category>
中的內容同時能夠匹配上 Intent 中指定的 action 和 category 時,這個活動才能響應該 Intent。
修改 MainActivity 中按鈕的點擊事件,代碼如下所示:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("pub.weber.bym.activitytest.ACTION_START");
startActivity(intent);
}
});
可以看到,我們使用了 Intent 的另一個構造函數,直接將 action 的字符串傳了進去,表明我們想要啓動能夠響應 com.example.activitytest.ACTION_START
這個 action 的活動。那前面不是說要<action>
和<category>
同時匹配上才能響應的嗎?怎麼沒看到哪裏有指定category 呢?這是因爲 android.intent.category.DEFAULT
是一種默認的 category,在調用startActivity()方法的時候會自動將這個 category 添加到 Intent 中。
重新運行程序,在 MainActivity 的界面點擊一下按鈕,你同樣成功啓動 SecondActivity了。不同的是,這次你是使用了隱式 Intent 的方式來啓動的,說明我們在<activity>
標籤下配置的 action 和 category 的內容已經生效了!
每個 Intent 中只能指定一個 action,但卻能指定多個 category。目前我們的 Intent 中只有一個默認的 category,那麼現在再來增加一個吧。
修改 MainActivity 中按鈕的點擊事件,代碼如下所示:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("pub.weber.bym.activitytest.ACTION_START");
intent.addCategory("com.example.activitytest.MY_CATEGORY");
startActivity(intent);
}
});
可以調用 Intent 中的 addCategory()
方法來添加一個 category,這裏我們指定了一個自定義的 category,值爲 com.example.activitytest.MY_CATEGORY
。
現在重新運行程序,在 MainActivity 的界面點擊一下按鈕,你會發現,程序崩潰了!
錯誤信息中提醒我們,沒有任何一個活動可以響應我們的 Intent,爲什麼呢?這是因爲我們剛剛在 Intent 中新增了一個 category,而 SecondActivity 的<intent-filter>
標籤中並沒有聲明可以響應這個category,所以就出現了沒有任何活動可以響應該 Intent 的情況。現在我們在<intent-filter>
中再添加一個 category 的聲明,如下所示:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="pub.weber.bym.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.example.activitytest.MY_CATEGORY"/>
</intent-filter>
</activity>
再次重新運行程序,你就會發現一切都正常了。
更多隱式 Intent 的用法
使用隱式 Intent,我們不僅可以啓動自己程序內的活動,還可以啓動其他程序的活動,這使得 Android多個應用程序之間的功能共享成爲了可能。比如說你的應用程序中需要展示一個網頁,這時你沒有必要自己去實現一個瀏覽器(事實上也不太可能),而是隻需要調用系統的瀏覽器來打開這個網頁就行了。
修改 MainActivity 中按鈕點擊事件的代碼,如下所示:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://weber.pub/"));
startActivity(intent);
}
});
這裏我們首先指定了 Intent 的 action 是 Intent.ACTION_VIEW
,這是一個 Android 系統內置的動作,其常量值爲 android.intent.action.VIEW
。然後通過 Uri.parse()
方法,將一個網址字符串解析成一個 Uri 對象,再調用 Intent的 setData()
方法將這個 Uri 對象傳遞進去。
重新運行程序,在 MainActivity 界面點擊按鈕就可以看到打開了系統瀏覽器,如圖所示。
上述的代碼中,可能你會對 setData()
部分感覺到陌生,這是我們前面沒有講到過的。這個方法其實並不複雜,它接收一個 Uri 對象,主要用於指定當前 Intent 正在操作的數據,而這些數據通常都是以字符串的形式傳入到 Uri.parse()
方法中解析產生的。
與此對應,我們還可以在<intent-filter>
標籤中再配置一個<data>
標籤,用於更精確地指定當前活動能夠響應什麼類型的數據。<data>
標籤中主要可以配置以下內容。
android:scheme
用於指定數據的協議部分,如上例中的 http 部分。android:host
用於指定數據的主機名部分,如上例中的http://weber.pub/
部分。android:port
用於指定數據的端口部分,一般緊隨在主機名之後。android:path
用於指定主機名和端口之後的部分,如一段網址中跟在域名之後的內容。android:mimeType
用於指定可以處理的數據類型,允許使用通配符的方式進行指定。
只有<data>
標籤中指定的內容和 Intent 中攜帶的 Data完全一致時,當前活動才能夠響應該 Intent。不過一般在<data>
標籤中都不會指定過多的內容,如上面瀏覽器示例中,其實只需要指定 android:scheme
爲 http,就可以響應所有的 http 協議的 Intent 了。
新建一個 activity_third.xml 佈局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 3"
/>
</LinearLayout>
然後新建活動 ThirdActivity 繼承自 Activity,代碼如下:
package pub.weber.bym.activitytest;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
public class ThirdActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_third);
}
}
最後在 AndroidManifest.xml
中爲 ThirdActivity 進行註冊。
<activity android:name=".ThirdActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http"/>
</intent-filter>
</activity>
我們在 ThirdActivity 的 <intent-filter>
中 配 置 了 當 前 活 動 能 夠 響 應 的 action 是Intent.ACTION_VIEW
的常量值,而 category 則毫無疑問指定了默認的 category 值,另外在<data>
標籤中我們通過android:scheme指定了數據的協議必須是 http協議,這樣 ThirdActivity應該就和瀏覽器一樣,能夠響應一個打開網頁的 Intent 了。讓我們運行一下程序試試吧,在FirstActivity 的界面點擊一下按鈕,結果如圖所示。
可以看到,系統自動彈出了一個列表,顯示了目前能夠響應這個 Intent 的所有程序。點擊 Browser 還會像之前一樣打開瀏覽器,並顯示weber的主頁,而如果點擊了 ActivityTest,則會啓動ThirdActivity。需要注意的是,雖然我們聲明瞭 ThirdActivity 是可以響應打開網頁的Intent 的,但實際上這個活動並沒有加載並顯示網頁的功能,所以在真正的項目中儘量不要去做這種有可能誤導用戶的行爲,不然會讓用戶對我們的應用產生負面的印象。
除了 http 協議外,我們還可以指定很多其他協議,比如 geo 表示顯示地理位置、tel 表示撥打電話。下面的代碼展示瞭如何在我們的程序中調用系統撥號界面。
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
});
首先指定了 Intent 的 action 是 Intent.ACTION_DIAL
,這又是一個 Android 系統的內置動作。然後在 data部分指定了協議是 tel,號碼是 10086。重新運行一下程序,在 MainActivity 的界面點擊一下按鈕,結果如圖所示。