第六章 組件通訊與廣播消息(Intent)

參考資料:
《Android應(yīng)用程序開發(fā)》ISBN 9787302283164
參考軟件:
Android Studio、Eclipse+ADT、Android SDK、JDK
Intent簡介
Intent的中文意思是“意圖,意向”,在Android中提供了Intent機制來協(xié)助應(yīng)用間的交互與通訊,Intent負責(zé)對應(yīng)用中一次操作的動作、動作涉及數(shù)據(jù)、附加數(shù)據(jù)進行描述,Android則根據(jù)此Intent的描述,負責(zé)找到對應(yīng)的組件,將 Intent傳遞給調(diào)用的組件,并完成組件的調(diào)用。Intent不僅可用于應(yīng)用程序之間,也可用于應(yīng)用程序內(nèi)部的Activity/Service之間的交互。因此,可以將Intent理解為不同組件之間通信的“媒介”專門提供組件互相調(diào)用的相關(guān)信息。
1、Intent的概念:
Android中提供了Intent機制來協(xié)助應(yīng)用間的交互與通訊,或者采用更準確的說法是,Intent不僅可用于應(yīng)用程序之間,也可用于應(yīng)用程序內(nèi)部的activity, service和broadcast receiver之間的交互。Intent這個英語單詞的本意是“目的、意向、意圖”。
Intent是一種運行時綁定(runtime binding)機制,它能在程序運行的過程中連接兩個不同的組件。通過Intent,你的程序可以向Android表達某種請求或者意愿,Android會根據(jù)意愿的內(nèi)容選擇適當?shù)慕M件來響應(yīng)。
activity、service和broadcast receiver之間是通過Intent進行通信的,而另外一個組件Content Provider本身就是一種通信機制,不需要通過Intent。我們來看下面這個圖就知道了:

如果Activity1需要和Activity2進行聯(lián)系,二者不需要直接聯(lián)系,而是通過Intent作為橋梁。通俗來講,Intnet類似于中介、媒婆的角色。
2、對于向這三種組件發(fā)送intent有不同的機制:
使用Context.startActivity() 或Activity.startActivityForResult(),傳入一個intent來啟動一個activity。使用 Activity.setResult(),傳入一個intent來從activity中返回結(jié)果。
將intent對象傳給Context.startService()來啟動一個service或者傳消息給一個運行的service。將intent對象傳給Context.bindService()來綁定一個service。
將intent對象傳給Context.sendBroadcast(),Context.sendOrderedBroadcast(),或者Context.sendStickyBroadcast()等廣播方法,則它們被傳給 broadcast receiver。
?Intent的相關(guān)屬性
Intent由以下各個組成部分:
component(組件):目的組件
action(動作):用來表現(xiàn)意圖的行動
category(類別):用來表現(xiàn)動作的類別
data(數(shù)據(jù)):表示與動作要操縱的數(shù)據(jù)
type(數(shù)據(jù)類型):對于data范例的描寫
extras(擴展信息):擴展信息
Flags(標志位):期望這個意圖的運行模式
Intent類型分為顯式Intent(直接類型)、隱式Intent(間接類型)。官方建議使用隱式Intent。上述屬性中,component屬性為直接類型,其他均為間接類型。
相比與顯式Intent,隱式Intnet則含蓄了許多,它并不明確指出我們想要啟動哪一個活動,而是指定一系列更為抽象的action和category等信息,然后交由系統(tǒng)去分析這個Intent,并幫我們找出合適的活動去啟動。
Activity 中 Intent Filter 的匹配過程:

1、component(組件):目的組件
Component屬性明確指定Intent的目標組件的類名稱。(屬于直接Intent)
如果 component這個屬性有指定的話,將直接使用它指定的組件。指定了這個屬性以后,Intent的其它所有屬性都是可選的。
例如,啟動第二個Activity時,我們可以這樣來寫:
? ? ? ? ?button1.setOnClickListener(new?OnClickListener() { ? ? ? ? ??
??? ? ? ? ? @Override
? ? ? ? ? ??public void?onClick(View v) {
? ? ? ? ? ? ? ? ?//創(chuàng)建一個意圖對象
? ? ? ? ? ? ? ? ?Intent intent =?new?Intent();
? ? ? ? ? ? ? ? ?//創(chuàng)建組件,通過組件來響應(yīng)
? ? ? ? ? ? ? ? ?ComponentName component =?new ComponentName(MainActivity.this,SecondActivity.class);
?????????????????intent.setComponent(component);???????????????
? ? ? ? ? ? ? ? startActivity(intent);???????????????
?????????????}
? ? ? ? ?});
如果寫的簡單一點,監(jiān)聽事件onClick()方法里可以這樣寫:
Intent intent =?new?Intent();
//setClass函數(shù)的第一個參數(shù)是一個Context對象
//Context是一個類,Activity是Context類的子類,也就是說,所有的Activity對象,都可以向上轉(zhuǎn)型為Context對象
//setClass函數(shù)的第二個參數(shù)是一個Class對象,在當前場景下,應(yīng)該傳入需要被啟動的Activity類的class對象
intent.setClass(MainActivity.this, SecondActivity.class);
startActivity(intent);???
再簡單一點,可以這樣寫:(當然,也是最常見的寫法)
Intent intent =?newIntent(MainActivity.this,SecondActivity.class);
startActivity(intent);
2、Action(動作):用來表現(xiàn)意圖的行動
當日常生活中,描述一個意愿或愿望的時候,總是有一個動詞在其中。比如:我想“做”三個俯臥撐;我要“寫” 一封情書,等等。在Intent中,Action就是描述做、寫等動作的,當你指明了一個Action,執(zhí)行者就會依照這個動作的指示,接受相關(guān)輸入,表現(xiàn)對應(yīng)行為,產(chǎn)生符合的輸出。在Intent類中,定義了一批量的動作,比如ACTION_VIEW,ACTION_PICK等,基本涵蓋了常用動作。加的動作越多,越精確。
Action 是一個用戶定義的字符串,用于描述一個 Android 應(yīng)用程序組件,一個 Intent Filter 可以包含多個 Action。在 AndroidManifest.xml 的Activity 定義時,可以在其?<intent-filter >節(jié)點指定一個 Action列表用于標識 Activity 所能接受的“動作”。
action動作大全:
http://blog.csdn.net/ithomer/article/details/8242471
Intent的各種常見作用
http://www.cnblogs.com/hanyonglu/archive/2012/03/26/2417278.html
3、category(類別):用來表現(xiàn)動作的類別
Category屬性也是作為<intent-filter>子元素來聲明的。例如:
<intent-filter>
<actionandroid:name="com.vince.intent.MY_ACTION"></action>
<categoryandroid:name="com.vince.intent.MY_CATEGORY"></category>?
<categoryandroid:name="android.intent.category.DEFAULT"></category>?
</intent-filter>???
Action 和category通常是放在一起用的,所以這里一起介紹一下。我們來先來舉一個例子:
新建一個工程文件smyh006_Intent01,在默認文件的基礎(chǔ)之上,新建文件SecondActicity.java和activity_second.xml。
緊接著,我們要到清單文件中進行注冊,打開AndroidManifest.xml,添加SecondActivity的action和category的過濾器:


? ? ? ? ?<activity
? ? ? ? ? ? ?android:name=".SecondActivity">
? ? ? ? ? ? ?<intent-filter>
? ? ? ? ? ? ? ? ?<action?android:name="com.example.smyh006intent01.MY_ACTION"/>
? ? ? ? ? ? ? ? ??<category?android:name="android.intent.category.DEFAULT"/>
? ? ? ? ? ? ?</intent-filter>????????????
? ? ? ??</activity>


上方代碼,表示SecondActicity可以匹配第4行的MY_ACTION這個動作,此時,如果在其他的Acticity通過這個action的條件來查找,那SecondActicity就具備了這個條件。類似于相親時,我要求對方有哪些條件,然后對方這個SecondActicity恰巧滿足了這個條件(夠通俗了吧)。
注:如果沒有指定的category,則必須使用默認的DEFAULT(即上方第5行代碼)。
也就是說:只有<action>和<category>中的內(nèi)容同時能夠匹配上Intent中指定的action和category時,這個活動才能響應(yīng)Intent。如果使用的是DEFAULT這種默認的category,在稍后調(diào)用startActivity()方法的時候會自動將這個category添加到Intent中。
現(xiàn)在來修改MainActivity.java中按鈕的點擊事件,代碼如下:


? ? ? ? ?button1.setOnClickListener(new?OnClickListener() {???????????
??????????????@Override
? ? ? ? ? ? ?public void?onClick(View v) {
? ? ? ? ? ? ? ? ?//啟動另一個Activity,(通過action屬性進行查找)
? ? ? ? ? ? ? ? ? Intent intent =?new?Intent();
? ? ? ? ? ? ? ? ??//設(shè)置動作(實際action屬性就是一個字符串標記而已)
? ? ? ? ? ? ????intent.setAction("com.example.smyh006intent01.MY_ACTION");//方法:Intentandroid.content.Intent.setAction(String action)
??????????????????startActivity(intent);?????? ?
??????????????}
? ? ? ? });


上方代碼中,也可以換成下面這種簡潔的方式:


? ? ? ? button1.setOnClickListener(new?OnClickListener() {???????????
?????????????@Override
? ? ? ? ? ??public void?onClick(View v) {
? ? ? ? ? ? ? ? ?//啟動另一個Activity,(通過action屬性進行查找)
? ? ? ? ? ? ? ? ?Intent intent =?newIntent("com.example.smyh006intent01.MY_ACTION");//方法: android.content.Intent.Intent(String action)????????????????
? ? ? ? ? ? ? ? startActivity(intent);???????
?????????????}
? ? ? ? ?});


上方第5行代碼:在這個Intent中,我并沒有指定具體哪一個Activity,我只是指定了一個action的常量。所以說,隱式Intent的作用就表現(xiàn)的淋漓盡致了。此時,點擊MainActicity中的按鈕,就會跳到SecondActicity中去。
上述情況只有SecondActicity匹配成功。如果有多個組件匹配成功,就會以對話框列表的方式讓用戶進行選擇。我們來詳細介紹一下:
我們新建文件ThirdActicity.java和activity_third.xml,然后在清單文件AndroidManifest.xml中添加ThirdActivity的action和category的過濾器:
? ? ? ? ?<activity
? ? ? ? ? ? ?android:name=".ThirdActivity">
? ? ? ? ? ? ?<intent-filter>
? ? ? ? ? ? ? ? ??<action?android:name="com.example.smyh006intent01.MY_ACTION"/>
? ? ? ? ? ? ? ? ??<category?android:name="android.intent.category.DEFAULT"/>
? ? ? ? ? ? ?</intent-filter>???????????
? ? ? ? ?</activity>


此時,運行程序,當點擊MainActivity中的按鈕時,彈出如下界面:

相信大家看到了這個界面,應(yīng)該就一目了然了。于是我們可以做出如下總結(jié):
在自定義動作時,使用activity組件時,必須添加一個默認的類別
具體的實現(xiàn)為:
<intent-filter>
??????????????<action android:name="com.example.action.MY_ACTION"/>
??????????????<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
如果有多個組件被匹配成功,就會以對話框列表的方式讓用戶進行選擇。
每個Intent中只能指定一個action,但卻能指定多個category;類別越多,動作越具體,意圖越明確(類似于相親時,給對方提了很多要求)。
目前我們的Intent中只有一個默認的category,現(xiàn)在可以通過intent.addCategory()方法來實現(xiàn)。修改MainActivity中按鈕的點擊事件,代碼如下:
?? ? ? ? button1.setOnClickListener(new?OnClickListener() {???????????
? ? ? ? ? ? @Override
? ? ? ? ? ? ?public void?onClick(View v) {
? ? ? ? ? ? ? ??//啟動另一個Activity,(通過action屬性進行查找)
? ? ? ? ? ? ? ? ? Intent intent =?new?Intent();
? ? ? ? ? ? ? ? ??//設(shè)置動作(實際action屬性就是一個字符串標記而已)
? ? ? ? ? ? ? ?intent.setAction("com.example.smyh006intent01.MY_ACTION");?//方法:Intent android.content.Intent.setAction(String action)
? ? ? ? ? ? ? ? intent.addCategory("com.example.smyh006intent01.MY_CATEGORY");
??????????????????startActivity(intent);???????
?????????????}
? ? ? ? ?});
既然在Intent中增加了一個category,那么我們要在清單文件中去聲明這個category,不然程序?qū)o法運行。代碼如下:
? ? ? ? ? ? android:name=".SecondActivity">
? ? ? ? ? ? ?<intent-filter>
? ? ? ? ? ? ? ? ??<action?android:name="com.example.smyh006intent01.MY_ACTION"/>
? ? ? ? ? ? ? ? ??<category?android:name="android.intent.category.DEFAULT"/>
? ? ? ? ? ? ? ? ??<category?android:name="com.example.smyh006intent01.MY_CATEGORY"/>
? ? ? ? ? ? ?</intent-filter>???????????
? ? ? ??</activity>
此時,點擊MainActicity中的按鈕,就會跳到SecondActicity中去。
總結(jié)如下:
自定義類別:?在Intent添加類別可以添加多個類別,那就要求被匹配的組件必須同時滿足這多個類別,才能匹配成功。操作Activity的時候,如果沒有類別,須加上默認類別
4、data(數(shù)據(jù)):表示與動作要操縱的數(shù)據(jù)
·????????Data屬性是Android要訪問的數(shù)據(jù),和action和Category聲明方式相同,也是在<intent-filter>中。
·????????多個組件匹配成功顯示優(yōu)先級高的;相同顯示列表。
Data是用一個uri對象來表示的,uri代表數(shù)據(jù)的地址,屬于一種標識符。通常情況下,我們使用action+data屬性的組合來描述一個意圖:做什么
使用隱式Intent,我們不僅可以啟動自己程序內(nèi)的活動,還可以啟動其他程序的活動,這使得Android多個應(yīng)用程序之間的功能共享成為了可能。比如應(yīng)用程序中需要展示一個網(wǎng)頁,沒有必要自己去實現(xiàn)一個瀏覽器(事實上也不太可能),而是只需要條用系統(tǒng)的瀏覽器來打開這個網(wǎng)頁就行了。
【實例】打開指定網(wǎng)頁:
MainActivity.java中,監(jiān)聽器部分的核心代碼如下:
? ? ? ? button1.setOnClickListener(new?OnClickListener() {???????????
?????????????@Override
? ? ? ? ? ??public void?onClick(View v) {
? ? ? ? ? ? ? ? ?Intent intent =?new?Intent();
? ? ? ? ? ? ? ? intent.setAction(Intent.ACTION_VIEW);
? ? ? ? ? ? ? ? ?Uri data =Uri.parse("http://www.baidu.com");
?????????????????intent.setData(data);????????????????
?????????????????startActivity(intent);???????
? ? ? ? ? ? }
? ? ? ? ?});
當然,上方代碼也可以簡寫成:
? ? ? button1.setOnClickListener(new?OnClickListener() {???????????
?????????????@Override
? ? ? ? ? ??public void?onClick(View v) {
? ? ? ? ? ? ? ? ?Intent intent =?new?Intent(Intent.ACTION_VIEW);
? ? ? ? ? ? ? ? ?intent.setData(Uri.parse("http://www.baidu.com"));???????????????
?????????????????startActivity(intent);???????
?????????????}
? ? ? ? ?});
第4行代碼:指定了Intent的action是?Intent.ACTION_VIEW,表示查看的意思,這是一個Android系統(tǒng)內(nèi)置的動作;
第5行代碼:通過Uri.parse()方法,將一個網(wǎng)址字符串解析成一個Uri對象,再調(diào)用intent的setData()方法將這個Uri對象傳遞進去。
當點擊按鈕時,將跳到如下界面:

此時,調(diào)用的是系統(tǒng)默認的瀏覽器,也就是說,只調(diào)用了這一個組件?,F(xiàn)在如果有多個組件得到了匹配,應(yīng)該是什么情況呢?
我們修改修改清單文件中對SecondAcivity的聲明:
? ? ? ? ?<activity
? ? ? ? ? ??android:name=".SecondActivity">
? ? ? ? ? ? ?<intent-filter>
? ? ? ? ? ? ? ? ?<action?android:name="android.intent.action.VIEW" />
? ? ? ? ? ? ? ? ??<category?android:name="android.intent.category.DEFAULT"/>
? ? ? ? ? ? ? ? ??<data?android:scheme="http"?android:host="www.baidu.com"/>?????????????????
? ? ? ? ? ? ?</intent-filter>???????????
? ? ? ? ?</activity>
現(xiàn)在,SecondActivity也匹配成功了,我們運行程序,點擊MainActicity的按鈕時,彈出如下界面供我們選擇:

我們可以總結(jié)如下:
?當Intent匹配成功的組件有多個時,顯示優(yōu)先級高的組件,如果優(yōu)先級相同,顯示列表讓用戶自己選擇
優(yōu)先級從-1000至1000,并且其中一個必須為負的才有效
注:系統(tǒng)默認的瀏覽器并沒有做出優(yōu)先級聲明,其優(yōu)先級默認為正數(shù)。
優(yōu)先級的配置如下:
在清單文件中修改對SecondAcivity的聲明,即增加一行代碼,通過來android:priority設(shè)置優(yōu)先級,如下:


? ? ? ? ?<activity
? ? ? ? ? ? ?android:name=".SecondActivity">
? ? ? ? ? ? ?<intent-filter?android:priority="-1">
? ? ? ? ? ? ? ? ??<action?android:name="android.intent.action.VIEW"/>
? ? ? ? ? ? ? ? ??<category?android:name="android.intent.category.DEFAULT"/>
? ? ? ? ? ? ? ? ??<data?android:scheme="http"
android:host="www.baidu.com"/>?????????????????????????????????
? ? ? ? ? ??</intent-filter>???????????
? ? ? ? ?</activity>
注:
Data屬性的聲明中要指定訪問數(shù)據(jù)的Uri和MIME類型??梢栽?lt;data>元素中通過一些屬性來設(shè)置:
android:scheme、android:path、android:port、android:mimeType、android:host等,通過這些屬性來對應(yīng)一個典型的Uri格式scheme://host:port/path。例如:http://www.google.com。
5、type(數(shù)據(jù)類型):對于data范例的描寫
如果Intent對象中既包含Uri又包含Type,那么,在<intent-filter>中也必須二者都包含才能通過測試。
Type屬性用于明確指定Data屬性的數(shù)據(jù)類型或MIME類型,但是通常來說,當Intent不指定Data屬性時,Type屬性才會起作用,否則Android系統(tǒng)將會根據(jù)Data屬性值來分析數(shù)據(jù)的類型,所以無需指定Type屬性。
data和type屬性一般只需要一個,通過setData方法會把type屬性設(shè)置為null,相反設(shè)置setType方法會把data設(shè)置為null,如果想要兩個屬性同時設(shè)置,要使用Intent.setDataAndType()方法。
【任務(wù)】:data+type屬性的使用?【實例】:播放指定路徑的mp3文件。
具體如下:
新建工程文件smyh006_Intent02,MainActivity.java中按鈕監(jiān)聽事件部分的代碼如下:
? ? ? ? button.setOnClickListener(new?OnClickListener(){
??????????????@Override
? ? ? ? ? ? ?public void?onClick(View v) {
? ? ? ? ? ? ? ? ?Intent intent =?new?Intent();
?? ? ? ? ? ? ? ? intent.setAction(Intent.ACTION_VIEW);
? ? ? ? ? ? ? ? ? Uri data =Uri.parse("file:///storage/sdcard0/平凡之路.mp3");
? ? ? ? ? ? ? ? ??//設(shè)置data+type屬性
? ? ? ? ? ? ? ? ? intent.setDataAndType(data,"audio/mp3");?//方法:Intentandroid.content.Intent.setDataAndType(Uri data, String type)
?????????????????startActivity(intent);???????????????
?????????????}???????????
? ? ? ? ?});
代碼解釋:
第6行:"file://"表示查找文件,后面再加上我的小米手機存儲卡的路徑:/storage/sdcard0,再加上具體歌曲的路徑。
第8行:設(shè)置data+type屬性
運行后,當點擊按鈕時,效果如下:

上方界面中,使用的是小米系統(tǒng)默認的音樂播放器。
6、extras(擴展信息):擴展信息
是其它所有附加信息的集合。使用extras可以為組件提供擴展信息,比如,如果要執(zhí)行“發(fā)送電子郵件”這個動作,可以將電子郵件的標題、正文等保存在extras里,傳給電子郵件發(fā)送組件。
7、Flags(標志位):期望這個意圖的運行模式
一個程序啟動后系統(tǒng)會為這個程序分配一個task供其使用,另外同一個task里面可以擁有不同應(yīng)用程序的activity。那么,同一個程序能不能擁有多個task?這就涉及到加載activity的啟動模式,這個需要單獨講一下。
注:android中一組邏輯上在一起的activity被叫做task,自己認為可以理解成一個activity堆棧。
例子1:點擊按鈕返回Home界面:?運行效果圖:

核心代碼:
Intent?it?=?new?Intent();it.setAction(Intent.ACTION_MAIN);it.addCategory(Intent.CATEGORY_HOME);startActivity(it);
Intent的常見應(yīng)用
https://blog.csdn.net/u012758088/article/details/68922219?locationNum=9&fps=1
1、打開指定網(wǎng)頁:(直接復(fù)制的上面的代碼)
MainActivity.java中,監(jiān)聽器部分的核心代碼如下:
? ? ? button1.setOnClickListener(new?OnClickListener() {???????????
?????????????@Override
? ? ? ? ? ??public voidonClick(View v) {
? ? ? ? ? ? ? ? ?Intent intent =?new?Intent();
? ? ? ? ? ? ? ? ?intent.setAction(Intent.ACTION_VIEW);//方法:android.content.Intent.Intent(String action)
? ? ? ? ? ? ? ? ?Uri data =Uri.parse("http://www.baidu.com");
?????????????????intent.setData(data);????????????????
?????????????????startActivity(intent);???????
? ? ? ? ? ? }
? ? ? ? });
當然,上方代碼也可以簡寫成:
? ?button1.setOnClickListener(new?OnClickListener() {???????????
?????????????@Override
? ? ? ? ? ? ?public voidonClick(View v) {
? ? ? ? ? ? ? ? ?Intent intent =?new?Intent(Intent.ACTION_VIEW);
? ? ? ? ? ? ? intent.setData(Uri.parse("http://www.baidu.com"));????????????????
?????????????????startActivity(intent);???????
? ? ? ? ? ? }
? ? ? ? ?});
第4行代碼:指定了Intent的action是?Intent.ACTION_VIEW,表示查看的意思,這是一個Android系統(tǒng)內(nèi)置的動作;
第5行代碼:通過Uri.parse()方法,將一個網(wǎng)址字符串解析成一個Uri對象,再調(diào)用intent的setData()方法將這個Uri對象傳遞進去。
或者可以寫成:
? ? ? ?button1.setOnClickListener(new?OnClickListener() {???????????
? ? ? ? ? ? @Override
? ? ? ? ? ??public voidonClick(View v) {
? ? ? ? ? ? ? ? ?Uri uri =Uri.parse("http://www.baidu.com");
? ? ? ? ? ? ? ? ?Intent intent =?new Intent(Intent.ACTION_VIEW,uri);//方法:?? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ?? ? ? ? ? ? ??android.content.Intent.Intent(String action, Uri uri)? ? ? ?
?????????????????startActivity(intent);???????
?????????????}
? ? ? ? });
2、打電話:
【方式一】打開撥打電話的界面:
? ? ? ? ? ? ? ???Intent intent =?new?Intent(Intent.ACTION_DIAL);
? ? ? ? ? ? ? ? intent.setData(Uri.parse("tel:10086"));
? ? ? ? ? ? ? ? ?startActivity(intent);?
運行程序后,點擊按鈕,顯示如下界面:

【方式二】直接撥打電話:
? ? ? ? ? ? ? ??Intent intent =?new?Intent(Intent.ACTION_CALL);
? ? ? ? ? ? ? ? intent.setData(Uri.parse("tel:10086"));
? ? ? ? ? ? ? ? startActivity(intent);
要使用這個功能必須在配置文件中加入權(quán)限:(加一行代碼)
1???? <uses-sdk
2???????? android:minSdkVersion="8"
3????????android:targetSdkVersion="16" />
4???? <uses-permissionandroid:name="android.permission.CALL_PHONE"/>
3、發(fā)短信:
【方式一】打開發(fā)送短信的界面:action+type
? ? ? ? ?Intent intent =?new?Intent(Intent.ACTION_VIEW);
? ? ? ? intent.setType("vnd.android-dir/mms-sms");
? ? ? ? intent.putExtra("sms_body","具體短信內(nèi)容");?//"sms_body"為固定內(nèi)容
? ? ? ? ?startActivity(intent);
【方式二】打開發(fā)短信的界面(同時指定電話號碼):action+data
? ? ? ? Intent intent =?new?Intent(Intent.ACTION_SENDTO);
? ? ? ? intent.setData(Uri.parse("smsto:18780260012"));
? ? ? ? intent.putExtra("sms_body","具體短信內(nèi)容");?//"sms_body"為固定內(nèi)容???????
? ? ? ? startActivity(intent);
4、播放指定路徑音樂:action+data+type
? ? ? ? ?Intent intent =?new?Intent(Intent.ACTION_VIEW);
? ? ? ? ?Uri uri =Uri.parse("file:///storage/sdcard0/平凡之路.mp3");
////路徑也可以寫成:"/storage/sdcard0/平凡之路.mp3"
? ? ? ? intent.setDataAndType(uri,"audio/mp3");?//方法:Intent android.content.Intent.setDataAndType(Uridata, String type)
? ? ? ? startActivity(intent);
5、卸載程序:action+data(例如點擊按鈕,卸載某個應(yīng)用程序,根據(jù)包名來識別)
注:無論是安裝還是卸載,應(yīng)用程序是根據(jù)包名package來識別的。
? ? ? ? ?Intent intent =?new?Intent(Intent.ACTION_DELETE);
? ? ? ? ?Uri data =Uri.parse("package:com.example.smyh006intent01");
?????????intent.setData(data);
? ? ? ? ?startActivity(intent);
6、安裝程序:action+data+type
? ? ? ? ?Intent intent =?new?Intent(Intent.ACTION_VIEW);
? ? ? ? ?Uri data = Uri.fromFile(new File("/storage/sdcard0/AndroidTest/smyh006_Intent01.apk"));????//路徑不能寫成:"file:///storage/sdcard0/···"
? ? ? ? ?intent.setDataAndType(data,"application/vnd.android.package-archive");??//Type的字符串為固定內(nèi)容
? ? ? ? ?startActivity(intent);
注:第2行的路徑不能寫成:"file:///storage/sdcard0/···",不然報錯如下:

疑問:通過下面的這種方式安裝程序,運行時為什么會出錯呢?
? ? ?//通過指定的action來安裝程序
? ? ?publicvoid?installClickTwo(View view){
? ? ? ? ?Intent intent =?new?Intent(Intent.ACTION_PACKAGE_ADDED);
? ? ? ? ?Uri data = Uri.fromFile(newFile("/storage/sdcard0/AndroidTest/smyh006_Intent01.apk"));????//路徑不能寫成:"file:///storage/sdcard0/···"
?????????intent.setData(data);
?????????startActivity(intent);
? ? ?}
運行后,主界面如下:

Intent數(shù)據(jù)傳遞
1.Intent傳遞簡單數(shù)據(jù)
直接通過調(diào)用Intent的putExtra()方法存入數(shù)據(jù),然后在獲得Intent后調(diào)用getXxxExtra獲得對應(yīng)類型的數(shù)據(jù);傳遞多個的話,可以使用Bundle對象作為容器,通過調(diào)用Bundle的putXxx先將數(shù)據(jù)存儲到Bundle中,然后調(diào)用Intent的putExtras()方法將Bundle存入Intent中,然后獲得Intent以后,調(diào)用getExtras()獲得Bundle容器,然后調(diào)用其getXXX獲取對應(yīng)的數(shù)據(jù)!另外數(shù)據(jù)存儲有點類似于Map的<鍵,值>!
2.Intent傳遞數(shù)組
寫入數(shù)組:
bd.putStringArray("StringArray",new String[]{"呵呵","哈哈"});
//可把StringArray換成其他數(shù)據(jù)類型,比如int,float等等...
讀取數(shù)組:
String[]str = bd.getStringArray("StringArray")
3.Intent傳遞集合
嗯,數(shù)組很簡單吧,那我們再來傳下集合~這個就稍微復(fù)雜點了,分情況處理:
(1)List<基本數(shù)據(jù)類型或String>
寫入集合:
intent.putStringArrayListExtra(name,value)
intent.putIntegerArrayListExtra(name,value)
讀取集合:
intent.getStringArrayListExtra(name)
intent.getIntegerArrayListExtra(name)
(2)List< Object>
將list強轉(zhuǎn)成Serializable類型,然后傳入(可用Bundle做媒介)
寫入集合:
putExtras(key,(Serializable)list)
讀取集合:
(List<Object>)getIntent().getSerializable(key)
PS:Object類需要實現(xiàn)Serializable接口
(3)Map<String, Object>,或更復(fù)雜的
解決方法是:外層套個List
//傳遞復(fù)雜些的參數(shù)
Map<String,Object> map1 = new HashMap<String, Object>();?
map1.put("key1","value1");?
map1.put("key2","value2");?
List<Map<String,Object>> list = new ArrayList<Map<String, Object>>();?
list.add(map1);?
Intentintent = new Intent();?
intent.setClass(MainActivity.this,ComplexActivity.class);?
Bundlebundle = new Bundle();?
//須定義一個list用于在budnle中傳遞需要傳遞的ArrayList<Object>,這個是必須要的?
ArrayListbundlelist = new ArrayList();??
bundlelist.add(list);??
bundle.putParcelableArrayList("list",bundlelist);?
intent.putExtras(bundle);???????????????
startActivity(intent);
4.Intent傳遞對象
傳遞對象的方式有兩種:將對象轉(zhuǎn)換為Json字符串或者通過Serializable,Parcelable序列化不建議使用Android內(nèi)置的摳腳Json解析器,可使用fastjson或者Gson第三方庫!
(1)將對象轉(zhuǎn)換為Json字符串
Gson解析的例子:
Model:
publicclass Author{
??? private int id;
??? private String name;
??? //...
}
?
publicclass Author{
??? private int id;
??? private String name;
??? //...
}
寫入數(shù)據(jù):
Bookbook=new Book();
book.setTitle("Java編程思想");
Authorauthor=new Author();
author.setId(1);
author.setName("BruceEckel");
book.setAuthor(author);
Intentintent=new Intent(this,SecondActivity.class);
intent.putExtra("book",newGson().toJson(book));
startActivity(intent);
讀取數(shù)據(jù):
StringbookJson=getIntent().getStringExtra("book");
Bookbook=new Gson().fromJson(bookJson,Book.class);
Log.d(TAG,"booktitle->"+book.getTitle());
Log.d(TAG,"bookauthor name->"+book.getAuthor().getName());
(2)使用Serializable,Parcelable序列化對象
Serializable實現(xiàn):
①業(yè)務(wù)Bean實現(xiàn):Serializable接口,寫上getter和setter方法②Intent通過調(diào)用putExtra(String name, Serializable value)傳入對象實例當然對象有多個的話多個的話,我們也可以先Bundle.putSerializable(x,x);
③新Activity調(diào)用getSerializableExtra()方法獲得對象實例: eg:Product pd = (Product)getIntent().getSerializableExtra("Product");
④調(diào)用對象get方法獲得相應(yīng)參數(shù)
Parcelable實現(xiàn):
一般流程:
①業(yè)務(wù)Bean繼承Parcelable接口,重寫writeToParcel方法,將你的對象序列化為一個Parcel對象;
②重寫describeContents方法,內(nèi)容接口描述,默認返回0就可以③實例化靜態(tài)內(nèi)部對象CREATOR實現(xiàn)接口Parcelable.Creator
④同樣式通過Intent的putExtra()方法傳入對象實例,當然多個對象的話,我們可以先放到Bundle里Bundle.putParcelable(x,x),再Intent.putExtras()即可
一些解釋:
通過writeToParcel將你的對象映射成Parcel對象,再通過createFromParcel將Parcel對象映射成你的對象。也可以將Parcel看成是一個流,通過writeToParcel把對象寫到流里面,在通過createFromParcel從流里讀取對象,只不過這個過程需要你來實現(xiàn),因此寫的順序和讀的順序必須一致。
實現(xiàn)Parcelable接口的代碼示例:
//InternalDescription Interface,You do not need to manage?
@Override?
publicint describeContents() {?
???? return 0;?
}?
@Override?
publicvoid writeToParcel(Parcel parcel, int flags){?
??? parcel.writeString(bookName);?
??? parcel.writeString(author);?
??? parcel.writeInt(publishTime);?
}?
publicstatic final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){?
??? @Override?
??? public Book[] newArray(int size) {?
??????? return new Book[size];?
??? }?
??? @Override?
??? public Book createFromParcel(Parcel source){?
??????? Book mBook = new Book();???
??????? mBook.bookName = source.readString();??
??????? mBook.author = source.readString();???
??????? mBook.publishTime = source.readInt();??
??????? return mBook;?
??? }?
};
Android Studio生成Parcleable插件:
Intellij/Andriod Studio插件android-parcelable-intellij-plugin只要ALT+Insert,即可直接生成Parcleable接口代碼。
另外:Android中大量用到Parcelable對象,實現(xiàn)Parcable接口又是非常繁瑣的,可以用到第三方的開源框架:Parceler,因為Maven的問題,暫時還沒試。
參考地址:[Android的Parcelable自動生成]
3.兩種序列化方式的比較:
兩者的比較:
·????????1)在使用內(nèi)存的時候,Parcelable比Serializable性能高,所以推薦使用Parcelable。
·????????2)Serializable在序列化的時候會產(chǎn)生大量的臨時變量,從而引起頻繁的GC。
·????????3)Parcelable不能使用在要將數(shù)據(jù)存儲在磁盤上的情況,因為Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性在外界有變化的情況下。盡管Serializable效率低點,但此時還是建議使用Serializable。
5.Intent傳遞Bitmap
bitmap默認實現(xiàn)Parcelable接口,直接傳遞即可
實現(xiàn)代碼:
Bitmapbitmap = null;
Intentintent = new Intent();
Bundlebundle = new Bundle();
bundle.putParcelable("bitmap",bitmap);
intent.putExtra("bundle",bundle);
6.傳來傳去不方便,直接定義全局數(shù)據(jù)
如果是傳遞簡單的數(shù)據(jù),有這樣的需求,Activity1 ->Activity2 -> Activity3 -> Activity4,你想在Activity中傳遞某個數(shù)據(jù)到Activity4中,怎么破,一個個頁面?zhèn)髅矗?/p>
顯然不科學(xué)是吧,如果你想某個數(shù)據(jù)可以在任何地方都能獲取到,你就可以考慮使用?Application全局對象了!
Android系統(tǒng)在每個程序運行的時候創(chuàng)建一個Application對象,而且只會創(chuàng)建一個,所以Application 是單例(singleton)模式的一個類,而且Application對象的生命周期是整個程序中最長的,他的生命周期等于這個程序的生命周期。如果想存儲一些比靜態(tài)的值(固定不改變的,也可以變),如果你想使用 Application就需要自定義類實現(xiàn)Application類,并且告訴系統(tǒng)實例化的是我們自定義的Application 而非系統(tǒng)默認的,而這一步,就是在AndroidManifest.xml中衛(wèi)我們的application標簽添加:name屬性!
關(guān)鍵部分代碼:
1)自定義Application類:
classMyApp extends Application {
??? private String myState;
??? public String getState(){
??????? return myState;
??? }
??? public void setState(String s){
??????? myState = s;
??? }
}
2)AndroidManifest.xml中聲明:
<applicationandroid:name=".MyApp" android:icon="@drawable/icon"
? android:label="@string/app_name">
3)在需要的地方調(diào)用:
classBlah extends Activity {
??? @Override
??? public void onCreate(Bundle b){
?????? ?...
??? MyApp appState = ((MyApp)getApplicationContext());
??? String state = appState.getState();
??????? ...
??? }
}
高逼格寫法:
在任何位置都能獲取到Application全局對象。
Applicaiton是系統(tǒng)的一個組件,他也有自己的一個生命周期,我們可以在onCraete里獲得這個 Application對象。貼下修改后的代碼吧!
classMyApp extends Application {
??? private String myState;
??? private static MyApp instance;
??? public static MyApp getInstance(){
??????? return instance;
??? }
??? public String getState(){
??????? return myState;
??? }
??? public void setState(String s){
??????? myState = s;
??? }
??? @Override
??? public void onCreate(){
??????? onCreate();
??????? instance = this;
??? }
}
然后在任意地方我們就可以直接調(diào)用:MyApp.getInstance()來獲得Application的全局對象!
注意事項:
Application對象是存在于內(nèi)存中的,也就有它可能會被系統(tǒng)殺死,比如這樣的場景:
我們在Activity1中往application中存儲了用戶賬號,然后在Activity2中獲取到用戶賬號,并且顯示!
如果我們點擊home鍵,然后過了N久候,系統(tǒng)為了回收內(nèi)存kill掉了我們的app。這個時候,我們重新打開這個app,這個時候很神奇的,回到了Activity2的頁面,但是如果這個時候你再去獲取Application 里的用戶賬號,程序就會報NullPointerException,然后crash掉~
之所以會發(fā)生上述crash,是因為這個Application對象是全新創(chuàng)建的,可能你以為App是重新啟動的,其實并不是,僅僅是創(chuàng)建一個新的Application,然后啟動上次用戶離開時的Activity,從而創(chuàng)造App 并沒有被殺死的假象!所以如果是比較重要的數(shù)據(jù)的話,建議你還是進行本地化,另外在使用數(shù)據(jù)的時候要對變量的值進行非空檢查!還有一點就是:不止是Application變量會這樣,單例對象以及公共靜態(tài)變量也會這樣~
7.單例模式傳參
上面的Application就是基于單例的,單例模式的特點就是可以保證系統(tǒng)中一個類有且只有一個實例。這樣很容易就能實現(xiàn),在A中設(shè)置參數(shù),在B中直接訪問了。這是幾種方法中效率最高的。
范例代碼:(代碼來自于網(wǎng)上~)
①定義一個單例類:
publicclass XclSingleton?
{?
??? //單例模式實例?
??? private static XclSingleton instance = null;?
?????
??? //synchronized?用于線程安全,防止多線程同時創(chuàng)建實例?
??? public synchronized static XclSingletongetInstance(){?
??????? if(instance == null){?
??????????? instance = new XclSingleton();?
??????? }????
??????? return instance;?
??? }????
?????
?? ?finalHashMap<String, Object> mMap;?
??? private XclSingleton()?
??? {?
??????? mMap = new HashMap<String,Object>();?
??? }?
?????
??? public void put(String key,Object value){?
??????? mMap.put(key,value);?
??? }?
?????
??? public Object get(String key)?
??? {?
??????? return mMap.get(key);?
??? }?
?????
}
②設(shè)置參數(shù):
XclSingleton.getInstance().put("key1","value1");?
XclSingleton.getInstance().put("key2","value2");
Activity跳轉(zhuǎn)后返回值
獲取子Activity的返回值,一般可以分為以下三個步驟
1、以Sub-Activity的方式啟動子Activity
2、設(shè)置子Activity的返回值
3、在父Activity中獲取返回值
startActivityForResult(intent, 0);
其中的0表示是哪個activity返回的