1、何為 Intent
Intent(意圖)主要用于 Android 各組件之間的交互,它可以指明當(dāng)前組件想要執(zhí)行的動作,也可以傳遞數(shù)據(jù)。一般來說,Intent 常被用來啟動 Activity、Service 以及 BroadCastReceiver。
Intent 分為顯式 Intent 和隱式 Intent,先來了解一下顯示 Intent。
2、顯式 Intent
在 Intent 的參數(shù)中明確的設(shè)置要跳轉(zhuǎn)的組件的包名和類名并跳轉(zhuǎn),叫顯式 Intent。
具體使用方法如下:
mBtnIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
Intent lIntent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(lIntent);
}
});
效果:

3、隱式 Intent
相比顯示 Intent 明確指明想要啟動的組件包名,隱式 Intent 恰好相反,它不明確指明想要啟動哪一個組件,而是在清單文件下相應(yīng)組件標(biāo)簽下配置 <intent - filter> 內(nèi)容,通過設(shè)置 Action、Data、Category,讓系統(tǒng)來自動篩選本次意圖想要啟動的組件。
那我們現(xiàn)在就去修改一下示例中的 SecondActivity 的 <intent - filter> 吧:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.ginkwang.activitysample.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
我們配置了 action 和 category 兩個標(biāo)簽,來了解一下這兩個標(biāo)簽的含義:
action
這個標(biāo)簽是必須添加的,其內(nèi)容有系統(tǒng)內(nèi)置的(詳見 Android Intent Action 大全),也可以自定義。-
category
表示組件的類別,一個組件可以配置多個 category 標(biāo)簽。常用的 category 標(biāo)簽有3個:DEFAULT —— 默認(rèn)動作
HOME —— 設(shè)置為本地桌面應(yīng)用
-
LAUNCHER —— 項(xiàng)目主 Activity
其中 DEFAULT 是最常用的標(biāo)簽,本例中也是使用的 DEFAULT。
然后再修改一下代碼中啟動組件的邏輯,改為隱式 Intent 啟動:
mBtnIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
Intent lIntent = new Intent("com.ginkwang.activitysample.ACTION_START");
startActivity(lIntent);
}
});
這里有個問題,隱式 Intent 不是要 action 和 category 兩個標(biāo)簽都要一一對應(yīng)才能啟動組件的嗎?
對,是這樣的沒錯。但是咱們在 category 標(biāo)簽下設(shè)置的是默認(rèn)動作 —— DEFAULT,它會在 startActivity() 方法執(zhí)行時,自動將 category 添加到 Intent 中。
同樣,如果 category 設(shè)置的是別的自定義的類別的話,就需要我們自己在啟動組件時填寫 category 信息了。
現(xiàn)在我們重新啟動一下應(yīng)用:

<intent - filter> 的兩個標(biāo)簽都介紹完了,接下來我們來看一下 data 標(biāo)簽。
data 代表數(shù)據(jù)檢測,用于精確的指定當(dāng)前活動能夠相應(yīng)什么類型的數(shù)據(jù)。data 標(biāo)簽可配置如下內(nèi)容:
android:host:指定主機(jī)名,例如:google.com
android:port:制定主機(jī)端口,例如: 80
android:path:指定URL的有效路徑值,例如: /index/examples
android:mimeType:指定組件可以執(zhí)行的數(shù)據(jù)類型,例如:image/jpeg,video/*
android:scheme:指定特定的模式,例如:content,http
和 action、category 一樣,只有 data 標(biāo)簽中指定的內(nèi)容與 Intent 中攜帶的 data 內(nèi)容相同時,當(dāng)前活動才能響應(yīng) Intent。
接下來展示,調(diào)用系統(tǒng)瀏覽器并打開指定網(wǎng)址:
mBtnBrowser.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
Uri url = Uri.parse("https://www.baidu.com/");
intent.setData(url);
startActivity(intent);
}
});
我們先定義了一個系統(tǒng)內(nèi)置的 action 常量,然后通過 Uri.parse 方法將百度網(wǎng)址解析成一個 Uri 對象,最后通過 Intent 的 setData 方法將 Uri 對象傳遞進(jìn)去。
看一下效果:

調(diào)用本地瀏覽器的操作并不難,其實(shí)我們也可以新建一個活動,并讓它響應(yīng)打開網(wǎng)頁的操作(此處的場景可以想象一下在某 APP 中點(diǎn)擊一段網(wǎng)址,系統(tǒng)會自動篩選可以打開網(wǎng)頁的 APP,然后給你一個選擇,用哪個 APP 打開這段網(wǎng)址)。
上代碼:
新建一個 ThirdActivity,代碼不表。然后在清單文件中配置 <intent-filter> ,
<activity android:name=".ThirdActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https"/>
</intent-filter>
</activity>
注意,最重要的一句就是 <data android:scheme="https"/>
,這表明此活動接收的數(shù)據(jù)協(xié)議為 https(應(yīng)為我們測試的百度網(wǎng)址是 https 協(xié)議的),這樣就表明 ThirdActivity 可以和瀏覽器一樣,可以打開網(wǎng)頁了。
注意:
這個操作我只在原生 Android 系統(tǒng)上面測試成功了,在 MIUI 和 FLYME 上面則沒有選擇打開操作的過程,直接就在系統(tǒng)瀏覽器上面打開網(wǎng)址了。
4、Intent 傳遞數(shù)據(jù)
使用 Intent 可以打開活動,那能不能打開活動的同時,給下一個活動傳遞數(shù)據(jù)呢?答案當(dāng)然是可以的。使用 Intent 的 putExtra 方法即可,看代碼;
Intent lIntent = new Intent(MainActivity.this, SecondActivity.class);
lIntent.putExtra("intent_data", "hello_second_activity");
startActivity(lIntent);
通過 Intent 打開 SecondActivity,然后通過 putExtra 方法給 SecondActivity 傳遞數(shù)據(jù)。putExtra 接收兩個參數(shù),第一個參數(shù)是本次傳值的 key,第二個參數(shù)才是傳的值 value。
然后 SecondActivity 接收傳遞值的代碼:
//接收 MAinActivity 傳遞來的數(shù)據(jù)
String intentStr = getIntent().getStringExtra("intent_data");
Toast.makeText(SecondActivity.this, intentStr, Toast.LENGTH_LONG).show();
這里的邏輯也十分簡單,通過 getIntent 獲取到 Intent,然后再調(diào)用 getStringExtra 方法,傳入 MainActivity 里約定好的 key 值,就接收到 Intent 傳遞來的數(shù)據(jù)了。
剛才演示的 Intent 傳遞的是 String 型的數(shù)據(jù),同理 Integer、Boolean 等基本數(shù)據(jù)類型數(shù)據(jù),都可以按照上面的操作進(jìn)行傳遞,只是在接收的時候按照相應(yīng)的數(shù)據(jù)類型調(diào)用相應(yīng)的方法進(jìn)行接收就 OK 了。
那如果有一個實(shí)體類對象要進(jìn)行傳遞呢?
總不能將其字段一一拆分出來然后按照基本數(shù)據(jù)類型的傳遞方法進(jìn)行傳遞吧?這樣不是不行,只是太繁瑣了。如果這個類里面有50個字段的話,那工程量是有多大?
好了,不啰嗦。Intent 有一個方法 putExtras 可以實(shí)現(xiàn)我們的需求。直接上代碼:
先新建一個實(shí)體類,取名 IntentTestBean
,并實(shí)現(xiàn) Serializable 類,將其序列化:
public class IntentTestBean implements Serializable {
?
private String name;
private String age;
private String sex;
?
public String getName() {
return name;
}
?
public void setName(String pName) {
name = pName;
}
?
public String getAge() {
return age;
}
?
public void setAge(String pAge) {
age = pAge;
}
?
public String getSex() {
return sex;
}
?
public void setSex(String pSex) {
sex = pSex;
}
?
@Override
public String toString() {
return "IntentTestBean{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
我們從 SecondActivity 中往 ThirdActivity 傳遞實(shí)體類,那 SecondActivity 里面的邏輯為:
/**
* 向 ThirdActivity 發(fā)送實(shí)體類數(shù)據(jù)
*/
private void intentBean() {
IntentTestBean lIntentTestBean = new IntentTestBean();
lIntentTestBean.setName("ginkwang");
lIntentTestBean.setAge("24");
lIntentTestBean.setSex("男");
Intent lIntent = new Intent(SecondActivity.this, ThirdActivity.class);
Bundle lBundle = new Bundle();
lBundle.putSerializable("intent_bean", lIntentTestBean);
lIntent.putExtras(lBundle);
startActivity(lIntent);
}
先把 IntentTestBean 類賦值,然后新建 Intent,指定跳轉(zhuǎn)目標(biāo),因?yàn)槲覀円獋鬟f的是對象,所以必須要使用 BundleBundle主要用于傳遞數(shù)據(jù),它保存的數(shù)據(jù),是以key-value(鍵值對)的形式存在的
。新建 Bundle 對象 ,接著將 IntentTestBean 類傳入進(jìn) Bundle,然后調(diào)用 Intent 的 putExtras 方法,將實(shí)體類傳遞出去。
在 ThirdActivity 中接收數(shù)據(jù)的邏輯如下:
//接收 SecondActivity 傳遞的實(shí)體類數(shù)據(jù)
IntentTestBean lIntentTestBean = (IntentTestBean) getIntent().
getSerializableExtra("intent_bean");
if (lIntentTestBean != null) {
Toast.makeText(ThirdActivity.this, lIntentTestBean.toString(), Toast.LENGTH_LONG).show();
}
這里的邏輯很簡單,和接收 String 類型數(shù)據(jù)的方法類似,就是這里需要使用 getSerializableExtra 方法獲取實(shí)體類對象,另外還有一個類型強(qiáng)制轉(zhuǎn)換。
最終效果:

5、Intent 傳遞數(shù)據(jù)
說完了傳遞數(shù)據(jù),接下來講一下 Intent 返回?cái)?shù)據(jù)給上一個活動。
利用 Intent 返回?cái)?shù)據(jù),就不能用 StartActivity 啟動活動了。Activity 中提供了一個 startActivityForResult 方法,此方法也可用于啟動活動,并在活動銷毀時傳遞數(shù)據(jù)給上一個活動。這正是我們想要的!
startActivityForResult 方法接收兩個參數(shù),第一個還是攜帶數(shù)據(jù)的 Intent,第二個是請求碼,用于回調(diào)時對數(shù)據(jù)來源的判斷。
看一下啟動活動的代碼:
Intent lIntent = new Intent(MainActivity.this, SecondActivity.class);
lIntent.putExtra("intent_data", "hello_second_activity");
startActivityForResult(lIntent, 1);
和之前幾乎一樣,就是把 startActivity 換成了 startActivityForResult,請求碼設(shè)置為1。
然后看一下 SecondActivity 中返回?cái)?shù)據(jù)的代碼:
//向 MainActivity 返回?cái)?shù)據(jù)
Intent lIntent = new Intent();
lIntent.putExtra("return_data", "hello_main_activity");
setResult(RESULT_OK, lIntent);
finish();
還是新建一個 Intent 對象,但不給它指定任何意圖,僅僅用作傳遞數(shù)據(jù)。接著調(diào)用 setResult 方法,此方法專門用于向上一個活動傳遞數(shù)據(jù),接收兩個參數(shù),第一個是 Intent,第二個是處理結(jié)果,一般使用 RESULT_OK 或 RESULT_CANCELED。最后調(diào)用 finish 方法銷毀當(dāng)前活動。
之后還要在 MainActivity 中重寫一個 onActivityResult 用作回調(diào):
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1://判斷請求碼是否為1,缺失是否從 SecondActivity 返回的
if (resultCode == RESULT_OK) {//再判斷結(jié)果值看是否返回結(jié)果成功
String resultData = data.getStringExtra("return_data");
Toast.makeText(MainActivity.this, resultData, Toast.LENGTH_LONG).show();
}
break;
}
}
相關(guān)邏輯在代碼中都標(biāo)注好了,另外實(shí)體類的數(shù)據(jù)返回也是如此,這里就不貼代碼了。
最后,效果圖:

