- Android應(yīng)用程序開發(fā)與典型案例
- 鄭萌 趙常松等編著
- 163字
- 2018-12-27 18:22:17
第6章 組件間通信
在第5章的學(xué)習(xí)中,主要了解了Android程序界面的開發(fā),包括用戶界面基礎(chǔ)、用戶界面控件的使用、界面布局的特點(diǎn)及使用方法、菜單的使用方法、界面事件的處理方法等。在此基礎(chǔ)上,本章將對(duì)Android組件間的通信進(jìn)行學(xué)習(xí),包括Intent進(jìn)行組件通信的原理、Intent啟動(dòng)Activity的方法、獲取Activity返回值的方法、Intent過(guò)濾器的原理及其匹配機(jī)制、發(fā)送和接收廣播消息的方法等。
6.1 Intent對(duì)象及其屬性
Intent是一個(gè)動(dòng)作的完整描述,包含了動(dòng)作的產(chǎn)生組件、接收組件和傳遞的數(shù)據(jù)信息。Android則根據(jù)Intent的描述,在不同組件間傳遞消息,負(fù)責(zé)找到對(duì)應(yīng)的組件,將Intent傳遞給調(diào)用的組件,組件接收到傳遞的消息,執(zhí)行相關(guān)動(dòng)作,完成組件的調(diào)用。
Intent不僅可用于應(yīng)用程序之間,也可用于應(yīng)用程序內(nèi)部的Activity/Service之間的交互。Intent為Activity、Service和BroadcastReceiver等組件提供交互能力,還可以啟動(dòng)Activity和Service,在Android系統(tǒng)上發(fā)布廣播消息。這里的廣播消息是指可以接收到的特定數(shù)據(jù)或消息,也可以是手機(jī)的信號(hào)變化或電池的電量過(guò)低等信息。
因此,Intent在這里起著一個(gè)媒體中介的作用,專門提供組件互相調(diào)用的相關(guān)信息,實(shí)現(xiàn)調(diào)用者與被調(diào)用者之間的解耦。在SDK中給出了Intent作用的表現(xiàn)形式。
? 通過(guò)Context.startActivity() or Activity.startActivityForResult()啟動(dòng)一個(gè)Activity。
? 通過(guò)Context.startService() 啟動(dòng)一個(gè)服務(wù),或者通過(guò)Context.bindService() 和后臺(tái)服務(wù)交互。
? 通過(guò)廣播方法(比如Context.sendBroadcast(),Context.sendOrderedBroadcast(), Context.sendStickyBroadcast())發(fā)給broadcast receivers。
一般情況下,Intent對(duì)某操作的抽象描述包含下面幾個(gè)部分。
? 對(duì)執(zhí)行動(dòng)作的描述:操作(action)。
? 對(duì)這次動(dòng)作相關(guān)聯(lián)的數(shù)據(jù)進(jìn)行描述:數(shù)據(jù)(data)。
? 對(duì)數(shù)據(jù)類型的描述:數(shù)據(jù)類型(type)。
? 對(duì)執(zhí)行動(dòng)作的附加信息進(jìn)行描述:類別(category)。
? 其他一切附加信息的描述:附件信息(extras)。
? 對(duì)目標(biāo)組件的描述:目標(biāo)組件(component)。
6.1.1 Intent的action屬性
action是要執(zhí)行的動(dòng)作,也可以是在廣播Intent中已發(fā)生且正被報(bào)告的動(dòng)作。action部分是一個(gè)字符串對(duì)象。它描述了Intent會(huì)觸發(fā)的動(dòng)作。Android系統(tǒng)中已經(jīng)預(yù)定義了一些action常量,可以參看SDK幫助文檔,下表給出了一些標(biāo)準(zhǔn)的action常量,如表6-1所示。
表6-1 SDK中定義的標(biāo)準(zhǔn)動(dòng)作

問(wèn):除了SDK中定義的標(biāo)準(zhǔn)動(dòng)作外,可以使用自定義動(dòng)作嗎?
答:當(dāng)然,可以自定義動(dòng)作。
自定義的動(dòng)作在使用時(shí),一般要加上包名作為前綴(為防止重復(fù)定義),如“com.example.project.SHOW_COLOR”,并可定義相應(yīng)的Activity來(lái)處理自定義動(dòng)作。
除上表介紹的action常量外,開發(fā)者也可以定義自己的action描述。一般來(lái)講,定義自己的action字符串應(yīng)該以應(yīng)用程序的包名為前綴(防止重復(fù)定義)。由于action部分很大程度上決定了一個(gè)Intent的內(nèi)容,特別是數(shù)據(jù)(data)和附加(extras)字段,就像一個(gè)方法名決定了參數(shù)和返回值。正是這個(gè)原因,應(yīng)該盡可能明確指定動(dòng)作,并緊密關(guān)聯(lián)到其他Intent字段。即應(yīng)該定義組件能夠處理的Intent對(duì)象的整個(gè)協(xié)議,而不僅僅是單獨(dú)地定義一個(gè)動(dòng)作。一個(gè)Intent對(duì)象的動(dòng)作通過(guò)setAction()方法設(shè)置,通過(guò)getAction()方法讀取。
6.1.2 Intent的data屬性
data,即執(zhí)行動(dòng)作要操作的數(shù)據(jù)。
data描述了Intent的動(dòng)作所能操作數(shù)據(jù)的MIME類型和URL,不同的Action用不同的操作數(shù)據(jù)。例如,如果Activity字段是ACTION_EDIT,data字段將顯示包含用于編輯的文檔的URI;如果Activity是ACTION_CALL,data字段是一個(gè)tel://URI和將撥打的號(hào)碼;如果Activity是ACTION_VIEW,data字段是一個(gè)http://URI,接收活動(dòng)將被調(diào)用去下載和顯示URI指向的數(shù)據(jù)。在許多情況下,數(shù)據(jù)類型能夠從URI中推測(cè)出來(lái),特別是content://URIs,它表示位于設(shè)備上的數(shù)據(jù)且被內(nèi)容提供者(Content Provider)控制。但是類型也能夠顯示設(shè)置,setData()方法指定數(shù)據(jù)的URI,setType()指定MIME類型,setDataAndType()指定數(shù)據(jù)的URI和MIME類型。通過(guò)getData()讀取URI,getType()讀取類型。
匹配一個(gè)Intent到一個(gè)能夠處理data的組件,知道data的類型(它的MIME類型)和它的URI很重要。例如,一個(gè)組件能夠顯示圖像數(shù)據(jù)就不應(yīng)該被調(diào)用去播放音頻文件。
6.1.3 Intent的type屬性
數(shù)據(jù)類型(type),顯式指定Intent的數(shù)據(jù)類型(MIME)。一般Intent的數(shù)據(jù)類型能夠根據(jù)數(shù)據(jù)本身進(jìn)行判定,但是通過(guò)設(shè)置這個(gè)屬性,可以強(qiáng)制采用顯式指定的類型而不再進(jìn)行推導(dǎo)。
6.1.4 Intent的category屬性
category(類別),被執(zhí)行動(dòng)作的附加信息。例如,LAUNCHER_CATEGORY表示Intent的接受者應(yīng)該在Launcher中作為頂級(jí)應(yīng)用出現(xiàn);而ALTERNATIVE_CATEGORY表示當(dāng)前的Intent是一系列的可選動(dòng)作中的一個(gè),這些動(dòng)作可以在同一塊數(shù)據(jù)上執(zhí)行。其他的如表6-2所示。
表6-2 SDK中定義的標(biāo)準(zhǔn)動(dòng)作

通過(guò)addCategory()方法添加一個(gè)種類到Intent對(duì)象中;通過(guò)removeCategory()方法刪除一個(gè)之前添加的種類;通過(guò)getCategories()方法獲取Intent對(duì)象中的所有種類。
6.1.5 Intent的extras屬性
extras(附加信息)是一組鍵值對(duì),包含了需要傳遞給目標(biāo)組件并有其處理的一些附加信息。
就像動(dòng)作關(guān)聯(lián)的特定種類的數(shù)據(jù)URIs,也關(guān)聯(lián)到某些特定的附加信息。例如,一個(gè)ACTION_TIMEZONE_CHANGE intent有一個(gè)“time-zone”的附加信息,標(biāo)識(shí)新的時(shí)區(qū),ACTION_HEADSET_PLUG有一個(gè)“state”附加信息,標(biāo)識(shí)頭部現(xiàn)在是否塞滿或未塞滿;有一個(gè)“name”附加信息,標(biāo)識(shí)頭部的類型。如果你自定義了一個(gè)SHOW_COLOR動(dòng)作,顏色值將可以設(shè)置在附加的鍵值對(duì)中。例如,如果要執(zhí)行“發(fā)送電子郵件”這個(gè)動(dòng)作,可以將電子郵件的標(biāo)題、正文等保存在extras里,傳給電子郵件發(fā)送組件。
Intent有一系列putXXX()方法用于插入各種附加數(shù)據(jù),有一系列g(shù)etXXX()方法可以取出一系列數(shù)據(jù)。
使用Extras可以為組件提供擴(kuò)展信息。
6.1.6 Intent的ComponentName屬性
ComponentName(組件),指定Intent的目標(biāo)組件的類名稱。ComponentName包含兩個(gè)String成員,分別代表組件的全稱類名和包名,包名必須和AndroidManifest.xml文件標(biāo)記中的對(duì)應(yīng)信息一致。ComponentName通過(guò)setComponent()、setClass()或setClassName()設(shè)置,通過(guò)getComponent()讀取。
通常Android會(huì)根據(jù)Intent中包含的其他屬性的信息(如,action、data/type、category)進(jìn)行查找,最終找到一個(gè)與之匹配的目標(biāo)組件。但是,如果ComponentName這個(gè)屬性有指定,將直接使用指定的組件,而不再執(zhí)行上述查找過(guò)程。指定了這個(gè)屬性以后,Intent的其他所有屬性都是可選的。
對(duì)于Intent,組件名并不是必需的。如果一個(gè)Intent對(duì)象添加了組件名,則稱該Intent為“顯式Intent”,這樣的Intent在傳遞時(shí)會(huì)直接根據(jù)組件名去尋找目標(biāo)組件。如果沒有添加組件名,則稱為“隱式Intent”,Android會(huì)根據(jù)Intent中的其他信息來(lái)確定響應(yīng)該Intent的組件。
總之,action、data/type、category和extras一起使系統(tǒng)能夠理解諸如“查看某聯(lián)系人的詳細(xì)信息”或“給某人打電話”之類的短語(yǔ)。隨著應(yīng)用不斷地加入系統(tǒng)中,Android系統(tǒng)可以添加新的action、data/type、category來(lái)擴(kuò)展功能。當(dāng)然,最受益的還是應(yīng)用本身,可以利用這套語(yǔ)言機(jī)制來(lái)處理不同的動(dòng)作和數(shù)據(jù)。
6.2 系統(tǒng)標(biāo)準(zhǔn)ActivityAction應(yīng)用
6.2.1 啟動(dòng)Activity
在Android系統(tǒng)中,應(yīng)用程序一般都有多個(gè)Activity,Intent可以實(shí)現(xiàn)不同Activity之間的切換和數(shù)據(jù)傳遞。
啟動(dòng)Activity方式有以下兩種方式。
? 顯式啟動(dòng),必須在Intent中指明啟動(dòng)的Activity所在的類。
? 隱式啟動(dòng),Android系統(tǒng)根據(jù)Intent的動(dòng)作和數(shù)據(jù)來(lái)決定啟動(dòng)哪一個(gè)Activity,即在隱式啟動(dòng)時(shí),Intent中只包含需要執(zhí)行的動(dòng)作和所包含的數(shù)據(jù),而無(wú)須指明具體啟動(dòng)哪一個(gè)Activity,選擇權(quán)由Android系統(tǒng)和最終用戶來(lái)決定。
1.顯式啟動(dòng)
使用Intent顯式啟動(dòng)Activity,首先需要?jiǎng)?chuàng)建一個(gè)Intent,指定當(dāng)前的應(yīng)用程序上下文及要啟動(dòng)的Activity,并把創(chuàng)建好的Intent作為參數(shù)傳遞給startActivity()方法。代碼如代碼清單6-1所示。
代碼清單6-1顯式啟動(dòng)
Intent intent = new Intent(IntentDemo.this, Activity2.class); startActivity(intent);
以下將通過(guò)一個(gè)IntentDemo示例來(lái)詳細(xì)講解如何使用Intent顯式啟動(dòng)新的Activity。
IntentDemo示例中包含IntentDemo和Activity2這兩個(gè)Activity類,程序啟動(dòng)是默認(rèn)啟動(dòng)IntentDemo這個(gè)Activity,啟動(dòng)畫面如圖6-1所示。

圖6-1 名為IntentDemo的Activity界面
在圖6-1的界面中,單擊“跳轉(zhuǎn)到Activity2”按鈕后,程序啟動(dòng)Activity2這個(gè)Activity,界面如圖6-2所示。

圖6-2 名為Activity2的Activity界面
為使程序達(dá)到上述效果,首先,在AndroidManifest.xml文件中注冊(cè)上面兩個(gè)Activity,應(yīng)使用<activity>標(biāo)簽,嵌套在<application>標(biāo)簽內(nèi)部。代碼如代碼清單6-2所示。
代碼清單6-2 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.IntentDemo" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".IntentDemo" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".Activity2" android:label="@string/app_name"> </activity> </application> <uses-sdk android:minSdkVersion="3" /> </manifest>
在Android應(yīng)用程序中,用戶使用的每個(gè)組件都必須在AndroidManifest.xml文件中的<application>節(jié)點(diǎn)內(nèi)定義,所以<application>節(jié)點(diǎn)下共有兩個(gè)<activity>節(jié)點(diǎn),分別代表應(yīng)用程序中所使用的兩個(gè)Activity:IntentDemo和Activity2。
在IntentDemo.java文件中,包含了顯示使用Intent啟動(dòng)Activity2的核心代碼,如代碼清單6-3所示。
代碼清單6-3 IntentDemo.java
Button button = (Button)findViewById(R.id.btn); button.setOnClickListener(new OnClickListener(){ public void onClick(View view){ Intent intent = new Intent(IntentDemo.this, Activity2.class); startActivity(intent); } });
在單擊事件的處理函數(shù)中,Intent構(gòu)造函數(shù)的第1個(gè)參數(shù)是應(yīng)用程序上下文,應(yīng)用程序上下文是IntentDemo;第2個(gè)參數(shù)是接收Intent的目標(biāo)組件,使用的是顯式啟動(dòng)方式,直接指明了需要啟動(dòng)的Activity。
同理,在Activity2.java文件中,包含了顯示使用Intent啟動(dòng)IntentDemo的核心代碼,如代碼清單6-4所示。
代碼清單6-4 Activity2.java
Button button = (Button)findViewById(R.id.btn); button2.setOnClickListener(new OnClickListener(){ public void onClick(View view){ Intent intent = new Intent(Activity2.this, IntentDemo.class); startActivity(intent); } });
2.隱式啟動(dòng)
隱式啟動(dòng)Activity時(shí),Android系統(tǒng)在應(yīng)用程序運(yùn)行時(shí)解析Intent,并根據(jù)一定的規(guī)則對(duì)Intent和Activity進(jìn)行匹配,使Intent上的動(dòng)作、數(shù)據(jù)與Activity完全吻合。而匹配的Activity可以是應(yīng)用程序本身的,也可以是Android系統(tǒng)內(nèi)置的,還可以是第三方應(yīng)用程序提供的。因此,這種方式更強(qiáng)調(diào)了Android應(yīng)用程序中組件的可復(fù)用性。
由此可以看出,隱式啟動(dòng)不需要指明需要啟動(dòng)哪一個(gè)Activity,而由Android系統(tǒng)來(lái)決定,有利于使用第三方組件。
在默認(rèn)情況下,Android系統(tǒng)會(huì)調(diào)用內(nèi)置的Web瀏覽器,代碼如代碼清單6-5所示。
代碼清單6-5隱式啟動(dòng)默認(rèn)情況調(diào)用內(nèi)置Web瀏覽器
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com")); startActivity(intent);
在上述代碼中,Intent的動(dòng)作是Intent.ACTION_VIEW,是根據(jù)Uri的數(shù)據(jù)類型來(lái)匹配動(dòng)作;數(shù)據(jù)部分的Uri是Web地址,使用Uri.parse(urlString)方法,可以簡(jiǎn)單地把一個(gè)字符串解釋成Uri對(duì)象。
Intent的語(yǔ)法如代碼清單6-6所示。
代碼清單6-6 Intent語(yǔ)法
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlString));
Intent構(gòu)造函數(shù)的第1個(gè)參數(shù)是Intent需要執(zhí)行的動(dòng)作;第2個(gè)參數(shù)是Uri,表示需要傳遞的數(shù)據(jù)。
Android系統(tǒng)支持的常見動(dòng)作字符串常量表,如表6-3所示。
表6-3 Android系統(tǒng)支持的常見動(dòng)作字符串常量表

以下將通過(guò)一個(gè)WebViewIntentDemo示例來(lái)了解如何隱式啟動(dòng)Activity。
如圖6-3所示,當(dāng)用戶在文本框中輸入要訪問(wèn)的網(wǎng)址后,通過(guò)單擊“Go”按鈕,程序根據(jù)用戶輸入的網(wǎng)址生成一個(gè)Intent,并以隱式啟動(dòng)的方式調(diào)用Android內(nèi)置的Web瀏覽器,并打開指定的Web頁(yè)面。本例輸入的網(wǎng)址是google主頁(yè)的主站地址,地址是:http://www.google.com。

圖6-3 隱式啟動(dòng)Activity
其中,main.xml代碼如代碼清單6-7所示。
代碼清單6-7 main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <EditText android:id="@+id/url_field" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1.0" android:lines="1" android:inputType="textUri" android:imeOptions="actionGo" /> <Button android:id="@+id/go_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/go_button" /> </LinearLayout>
其中,strings.xml代碼如代碼清單6-8所示。
代碼清單6-8 strings.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name"> WebViewIntentDemo </string> <string name="go_button">Go</string> </resources>
WebViewIntentDemo.java代碼如代碼清單6-9所示。
代碼清單6-9 WebViewIntentDemo.java
import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnKeyListener; import android.widget.Button; import android.widget.EditText; public class WebViewIntentDemo extends Activity { private EditText urlText; private Button goButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Get a handle to all user interface elements urlText = (EditText) findViewById(R.id.url_field); goButton = (Button) findViewById(R.id.go_button); // Setup event handlers goButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { openBrowser(); } }); urlText.setOnKeyListener(new OnKeyListener() { public boolean onKey(View view, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER) { openBrowser(); return true; } return false; } }); } }
在上述代碼中第26行對(duì)按鈕“Go”添加監(jiān)聽,當(dāng)觸摸單擊或通過(guò)手機(jī)按鍵單擊該按鈕時(shí),觸發(fā)openBrowser()方法。其中,openBrowser()方法代碼如代碼清單6-10所示。
代碼清單6-10 openBrowser()方法
/** Open a browser on the URL specified in the text box */ private void openBrowser() { Uri uri = Uri.parse(urlText.getText().toString()); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); }
6.2.2 獲取Activity返回值
在6.2.1節(jié)的IntentDemo示例中,通過(guò)使用startActivity(Intent)方法啟動(dòng)Activity后,兩個(gè)Activity之間相互獨(dú)立,沒有任何關(guān)聯(lián)。然而,在很多情況下,后啟動(dòng)的Activity是為了讓用戶對(duì)特定信息進(jìn)行選擇,在關(guān)閉這個(gè)Activity后,用戶的選擇信息需要返回給未關(guān)閉的那個(gè)Activity。由此,按照Activity啟動(dòng)的先后順序,先啟動(dòng)的稱為父Activity,后啟動(dòng)的稱為子Activity,如果需要將子Activity的部分信息返回給父Activity,則可以使用Sub-Activity的方式去啟動(dòng)子Activity。
獲取子Activity的返回值,一般分以下3個(gè)步驟。
1.以Sub-Activity的方式啟動(dòng)子Activity
首先,調(diào)用startActivityForResult(Intent, requestCode)函數(shù),其中,參數(shù)Intent用于決定啟動(dòng)哪個(gè)Activity,參數(shù)requestCode是唯一的標(biāo)識(shí)子Activity的請(qǐng)求碼。
顯式啟動(dòng)子Activity的代碼如代碼清單6-11所示。
代碼清單6-11顯式啟動(dòng)子Activity
int SUBACTIVITY1 = 1; Intent intent = new Intent(this, SubActivity1.class); startActivityForResult(intent, SUBACTIVITY1);
隱式啟動(dòng)子Activity的代碼如代碼清單6-12所示。
代碼清單6-12隱式啟動(dòng)子Activity
int SUBACTIVITY2 = 2; Uri uri = Uri.parse("content://contacts/people"); Intent intent = new Intent(Intent.ACTION_PICK, uri); startActivityForResult(intent, SUBACTIVITY2);
2.設(shè)置子Activity的返回值
在子Activity調(diào)用finish()函數(shù)關(guān)閉前,調(diào)用setResult()函數(shù)將所需的數(shù)據(jù)返回給父Activity。其中,setResult()函數(shù)有兩個(gè)參數(shù):結(jié)果碼和返回值。結(jié)果碼表明了子Activity的返回狀態(tài),通常為Activity.RESULT_OK或Activity.RESULT_CANCELED,或自定義的結(jié)果碼,結(jié)果碼均為整數(shù)類型;返回值封裝在Intent中,子Activity通過(guò)Intent將需要返回的數(shù)據(jù)傳遞給父Activity。數(shù)據(jù)主要是Uri形式,可以附加一些額外信息,這些額外信息用Extra的集合表示。
代碼清單6-13所示代碼說(shuō)明了如何在子Activity中設(shè)置返回值。
代碼清單6-13在子Activity中設(shè)置返回值
Uri data = Uri.parse(“tel:” + tel_number); Intent result = new Intent(null, data); result.putExtra(“address”, “ “); setResult(RESULT_OK, result); finish();
3.在父Activity中獲取返回值
當(dāng)子Activity關(guān)閉時(shí),啟動(dòng)它的父Activity的onActivityResult()函數(shù)將被調(diào)用;如果需要在父Activity中處理子Activity的返回值,則覆蓋此函數(shù)即可。此函數(shù)的語(yǔ)法如代碼清單6-14所示。
代碼清單6-14 onActivityResult()語(yǔ)法
public void onActivityResult(int requestCode, int resultCode, Intent data);
在上述代碼中,第1個(gè)參數(shù)requestCode,用來(lái)表示是哪一個(gè)子Activity的返回值;第2個(gè)參數(shù)resultCode用于表示子Activity的返回狀態(tài);第3個(gè)參數(shù)data是子Activity的返回?cái)?shù)據(jù),返回?cái)?shù)據(jù)類型是Intent。返回?cái)?shù)據(jù)的用途不同,Uri數(shù)據(jù)的協(xié)議也就不同。也可以使用Extra方法返回一些原始類型的數(shù)據(jù)。
代碼清單6-15所示代碼說(shuō)明如何在父Activity中處理子Activity的返回值。
代碼清單6-15在父Activity中處理子Activity的返回值
private static final int SUBACTIVITY1 = 1; private static final int SUBACTIVITY2 = 2; @Override public void onActivityResult(int requestCode, int resultCode, Intent data){ Super.onActivityResult(requestCode, resultCode, data); switch(requestCode){ case SUBACTIVITY1: if (resultCode == Activity.RESULT_OK){ Uri uriData = data.getData(); }else if (resultCode == Activity.RESULT_CANCEL){ } break; case SUBACTIVITY2: if (resultCode == Activity.RESULT_OK){ Uri uriData = data.getData(); } break; } }
在上述代碼中,第1行代碼和第2行代碼是兩個(gè)子Activity的請(qǐng)求碼;第7行代碼對(duì)請(qǐng)求碼進(jìn)行匹配;第9行和第11行代碼對(duì)結(jié)果碼進(jìn)行判斷,如果返回的結(jié)果碼是Activity.RESULT_OK,則在代碼的第10行使用getData()函數(shù)獲取Intent中的Uri數(shù)據(jù);反之,若返回的結(jié)果碼是Activity.RESULT_CANCELED,則不進(jìn)行任何操作。
ActivityCommunication示例說(shuō)明了如何以Sub-Activity方式啟動(dòng)子Activity,以及使用Intent進(jìn)行組件間的通信。
在如圖6-4所示的主界面中,當(dāng)用戶單擊“啟動(dòng)Activity1”和“啟動(dòng)Activity2”按鈕時(shí),程序?qū)⒎謩e啟動(dòng)兩個(gè)不同的子Activity:子SubActivity1和SubActivity2,它們的界面分別如圖6-5所示。

圖6-4 ActivityCommunication主界面

圖6-5 SubActivity1和SubActivity2界面
SubActivity1提供了一個(gè)輸入框,以及“接受”和“撤銷”兩個(gè)按鈕,如果在輸入框中輸入信息后單擊“接受”按鈕,程序會(huì)把輸入框中的信息傳遞給父Activity,并在父Activity界面上顯示;如果用戶單擊“撤銷”按鈕,則程序不會(huì)向父Activity傳遞任何信息。
SubActivity2主要為了說(shuō)明如何在父Activity中處理多個(gè)子Activity,因此僅提供了用于關(guān)閉SubActivity2的“關(guān)閉”按鈕。
下面介紹ActivityCommunication的核心代碼。
首先是ActivityCommunication.java文件,如代碼清單6-16所示。
代碼清單6-16 ActivityCommunication.java
public class ActivityCommunication extends Activity { private static final int SUBACTIVITY1 = 1; private static final int SUBACTIVITY2 = 2; TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textView = (TextView)findViewById(R.id.textShow); final Button btn1 = (Button)findViewById(R.id.btn1); final Button btn2 = (Button)findViewById(R.id.btn2); btn1.setOnClickListener(new OnClickListener(){ public void onClick(View view){ Intent intent = new Intent(ActivityCommunication.this, SubActivity1.class); startActivityForResult(intent, SUBACTIVITY1); } }); btn2.setOnClickListener(new OnClickListener(){ public void onClick(View view){ Intent intent = new Intent(ActivityCommunication.this, SubActivity2.class); startActivityForResult(intent, SUBACTIVITY2); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch(requestCode){ case SUBACTIVITY1: if (resultCode == RESULT_OK){ Uri uriData = data.getData(); textView.setText(uriData.toString()); } break; case SUBACTIVITY2: break; } } }
在上述代碼中,第2行和第3行分別定義了兩個(gè)子Activity的請(qǐng)求碼,而在第16行和第23行以Sub-Activity的方式分別啟動(dòng)兩個(gè)子Activity。
第29行代碼是子Activity關(guān)閉后的返回值處理函數(shù),其中requestCode是子Activity返回的請(qǐng)求碼,應(yīng)與第2行和第3行定義的兩個(gè)請(qǐng)求碼相匹配;resultCode是結(jié)果碼,在第32行代碼對(duì)結(jié)果碼進(jìn)行判斷,如果等于RESULT_OK,則在第35行代碼獲取子Activity的返回值中的數(shù)據(jù),其中,data是返回值,子Activity需要返回的數(shù)據(jù)就保存在data中。
SubActivity1.java的核心代碼如代碼清單6-17所示。
代碼清單6-17 SubActivity1.java
public class SubActivity1 extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.subactivity1); final EditText editText = (EditText)findViewById(R.id.edit); Button btnOK = (Button)findViewById(R.id.btn_ok); Button btnCancel = (Button)findViewById(R.id.btn_cancel); btnOK.setOnClickListener(new OnClickListener(){ public void onClick(View view){ String uriString = EditText.getText().toString(); Uri data = Uri.parse(uriString); Intent result = new Intent(null, data); setResult(RESULT_OK, result); finish(); } }); btnCancel.setOnClickListener(new OnClickListener(){ public void onClick(View view){ setResult(RESULT_CANCELED, null); finish(); } }); } }
上述SubActivity1.java的核心代碼中,第13行代碼將EditText控件的內(nèi)容作為數(shù)據(jù)保存在Uri中;第14行代碼中使用這個(gè)Uri構(gòu)造Intent;第15行代碼中,將Intent作為返回值,RESUIT_OK作為結(jié)果碼,通過(guò)調(diào)用setResult()函數(shù),將返回值和結(jié)果碼傳遞給父Activity;第16行代碼調(diào)用finish()函數(shù)關(guān)閉當(dāng)前的子Activity。
SubActivity2.java的核心代碼如代碼清單6-18所示。
代碼清單6-18 SubActivity2.java
public class SubActivity2 extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.subactivity2); Button btnReturn = (Button)findViewById(R.id.btn_return); btnReturn.setOnClickListener(new OnClickListener(){ public void onClick(View view){ setResult(RESULT_CANCELED, null); finish(); } }); } }
上述SubActivity2.java的核心代碼中,第10行的setResult()函數(shù)僅設(shè)置了結(jié)果碼,第2個(gè)參數(shù)為null,表示數(shù)據(jù)需要傳遞給父Activity。
6.3 Intent過(guò)濾器
Intent過(guò)濾器是一種根據(jù)Intent中的動(dòng)作(Action)、類別(Categorie)和數(shù)據(jù)(Data)等內(nèi)容,對(duì)適合接收該Intent的組件進(jìn)行匹配和篩選的機(jī)制。它可以匹配數(shù)據(jù)類型、路徑和協(xié)議,還可以用來(lái)確定多個(gè)匹配項(xiàng)順序的優(yōu)先級(jí)(Priority)。
應(yīng)用程序的Activity組件、Service組件和BroadcastReceiver都可以注冊(cè)Intent過(guò)濾器,這些組件在特定的數(shù)據(jù)格式上就可以產(chǎn)生相應(yīng)的動(dòng)作。
6.3.1 注冊(cè)Intent過(guò)濾器
注冊(cè)Intent過(guò)濾器的方法如下:
在AndroidManifest.xml文件的各個(gè)組件的節(jié)點(diǎn)下定義<intent-filter>節(jié)點(diǎn),然后在<intent-filter>節(jié)點(diǎn)中聲明該組件所支持的動(dòng)作、執(zhí)行的環(huán)境和數(shù)據(jù)格式等信息。
<intent-filter>節(jié)點(diǎn)支持<action>標(biāo)簽、<category>標(biāo)簽和<data>標(biāo)簽,其中:<action>標(biāo)簽定義Intent過(guò)濾器的“類別”;<category>標(biāo)簽定義Intent過(guò)濾器的“動(dòng)作”;<data>標(biāo)簽定義Intent過(guò)濾器的“數(shù)據(jù)”。
<intent-filter>節(jié)點(diǎn)支持的標(biāo)簽和屬性如表6-4所示。
表6-4 <intent-filter>節(jié)點(diǎn)支持的標(biāo)簽和屬性

<category>標(biāo)簽用來(lái)指定Intent過(guò)濾器的服務(wù)方式,每個(gè)Intent過(guò)濾器可以定義多個(gè)<category>標(biāo)簽,程序開發(fā)人員可使用自定義的類別,或使用Android系統(tǒng)提供的類別。其中,Android系統(tǒng)提供的類別如表6-5所示。
表6-5 Android系統(tǒng)提供的類別

AndroidManifest.xml文件中的每個(gè)組件的<intent-filter>都被解析成一個(gè)Intent過(guò)濾器對(duì)象。當(dāng)應(yīng)用程序安裝到Android系統(tǒng)時(shí),所有的組件和Intent過(guò)濾器都會(huì)注冊(cè)到Android系統(tǒng)中。這樣,Android系統(tǒng)便知道了如何將任意一個(gè)Intent請(qǐng)求通過(guò)Intent過(guò)濾器映射到相應(yīng)的組件上。
6.3.2 Intent解析
Intent到Intent過(guò)濾器的映射過(guò)程稱為“Intent解析”。Intent解析可以在所有組件中找到一個(gè)可與請(qǐng)求的Intent達(dá)成最佳匹配的Intent過(guò)濾器。
Intent解析的匹配規(guī)則。
? Android系統(tǒng)把所有應(yīng)用程序包中的Intent過(guò)濾器集合在一起,形成一個(gè)完整的Intent過(guò)濾器列表。
? 在Intent與Intent過(guò)濾器進(jìn)行匹配時(shí),Android系統(tǒng)會(huì)將列表中所有Intent過(guò)濾器的“動(dòng)作”和“類別”與Intent進(jìn)行匹配,任何不匹配的Intent過(guò)濾器都將被過(guò)濾掉。沒有指定“動(dòng)作”的Intent過(guò)濾器可以匹配任何的Intent,但沒有指定“類別”的Intent過(guò)濾器只能匹配沒有“類別”的Intent。
? 把Intent數(shù)據(jù)Uri的每個(gè)子部與Intent過(guò)濾器的<data>標(biāo)簽中的屬性進(jìn)行匹配,如果<data>標(biāo)簽指定了協(xié)議、主機(jī)名、路徑名或MIME類型,那么這些屬性都要與Intent的Uri數(shù)據(jù)部分進(jìn)行匹配,任何不匹配的Intent過(guò)濾器均被過(guò)濾掉。
? 如果Intent過(guò)濾器的匹配結(jié)果多于一個(gè),則可以根據(jù)在<intent-filter>標(biāo)簽中定義的優(yōu)先級(jí)標(biāo)簽來(lái)對(duì)Intent過(guò)濾器進(jìn)行排序,優(yōu)先級(jí)最高的Intent過(guò)濾器將被選擇。
在此以6.2.1 節(jié)中隱式啟動(dòng)Activity的例子WebViewIntentDemo為基礎(chǔ),在AndroidManifest.xml文件中注冊(cè)Intent過(guò)濾器,以及設(shè)置<intent-filter>節(jié)點(diǎn)屬性來(lái)捕獲指定的Intent。
在AndroidManifest.xml中添加如代碼清單6-19所示。
代碼清單6-19 AndroidManifest.xml中添加代碼
<intent-filter> <action android:name="android.intent.action.VIEW" /> <data android:schema="http" /> </intent-filter>
利用 <intent-filter> 可以把應(yīng)用程序的操作注冊(cè)到系統(tǒng)中,當(dāng)用戶調(diào)用Intent時(shí),可以根據(jù)輸入的ACTION和Uri參數(shù)來(lái)找到這個(gè)應(yīng)用程序。例如,在上述代碼中,以http協(xié)議為例,打開Google Map可以用URI: geo:38.899533,-77.036476。
此外,“協(xié)議”在Android里都可以隨便定義。例如,寫一個(gè)打開文件的關(guān)聯(lián)Intent,如file:///sdcard/abc.txt。也可以用type進(jìn)行關(guān)聯(lián),其代碼如代碼清單6-20所示。
代碼清單6-20用type進(jìn)行關(guān)聯(lián)
<intent-filter> <action android:name="android.intent.action.VIEW" /> <type android:value="test.item"/> </intent-filter>
然后,通過(guò)代碼清單6-21所示代碼就能定位到應(yīng)用。
代碼清單6-21用type定位到應(yīng)用代碼
Intent it = new Intent(Intent.ACTION_VIEW); it.setType("test.item”); startActivity(it)
6.4 廣播消息
應(yīng)用程序和Android系統(tǒng)都可以使用Intent發(fā)送廣播消息。其中,廣播消息的內(nèi)容可以是與應(yīng)用程序密切相關(guān)的數(shù)據(jù)信息,也可以是Android的系統(tǒng)信息,例如,網(wǎng)絡(luò)連接變化、電池電量變化、接收短信和系統(tǒng)設(shè)置變化等。如果應(yīng)用程序注冊(cè)了BroadcastReceiver,則可以接收到指定的廣播消息。
下面將介紹廣播信息的使用方法。
首先,創(chuàng)建一個(gè)Intent。調(diào)用sendBroadcast()函數(shù),就可把Intent攜帶的消息廣播出去,如果要在Intent傳遞額外數(shù)據(jù),可以用Intent的putExtra()方法。
注意:
在構(gòu)造Intent時(shí)必須用全局唯一的字符串標(biāo)識(shí)其要執(zhí)行的動(dòng)作,通常使用應(yīng)用程序包的名稱。
利用Intent發(fā)送廣播消息,并添加了額外的數(shù)據(jù),然后調(diào)用sendBroadcast()發(fā)送廣播消息的代碼如代碼清單6-11所示。
代碼清單6-22利用Intent發(fā)送廣播消息
String UNIQUE_STRING = "com.example.BroadcastReceiverDemo"; Intent intent = new Intent(UNIQUE_STRING); intent.putExtra("key1", "value1"); intent.putExtra("key2", "value2"); sendBroadcast(intent);
廣播消息發(fā)送后,利用BroadcastReceiver監(jiān)聽廣播消息。具體方法如下:在AndroidManifest.xml文件或在代碼中注冊(cè)一個(gè)BroadcastReceiver,并在其中使用Intent過(guò)濾器指定要處理的廣播消息。在BroadcastReceiver接收到與之匹配的廣播消息后,onReceive()方法會(huì)被調(diào)用;onReceive()方法必須要在5秒內(nèi)執(zhí)行完畢,否則Android系統(tǒng)會(huì)認(rèn)為該組件失去響應(yīng),并提示用戶強(qiáng)行關(guān)閉該組件。
創(chuàng)建BroadcastReceiver需繼承BroadcastReceiver類,并重載onReceive()方法。代碼如代碼清單6-23所示。
代碼清單6-23利用BroadcastReceiver監(jiān)聽廣播消息
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //TODO: React to the Intent received. } }
BroadcastReceiver的應(yīng)用程序不需要一直運(yùn)行,當(dāng)Android系統(tǒng)接收到與之匹配的廣播消息時(shí),會(huì)自動(dòng)啟動(dòng)此BroadcastReceiver?;谝陨咸卣?,BroadcastReceiver適合做一些資源管理的工作。
如圖6-6所示,BroadcastReceiverDemo示例說(shuō)明了如何在應(yīng)用程序中注冊(cè)BroadcastReceiver,并接收指定類型的廣播消息。

圖6-6 BroadcastReceiverDemo示例
如圖6-6所示,在單擊“發(fā)送廣播消息”按鈕后,EditText控件中內(nèi)容將以廣播消息的形式發(fā)送出去,示例內(nèi)部的BroadcastReceiver將接收這個(gè)廣播消息,并顯示在用戶界面的下方。
BroadcastReceiverDemo.java文件中包含發(fā)送廣播消息的代碼,其關(guān)鍵代碼如代碼清單6-24所示。
代碼清單6-24 BroadcastReceiverDemo.java
button.setOnClickListener(new OnClickListener(){ public void onClick(View view){ Intent intent = new Intent("com.example.BroadcastReceiverDemo"); intent.putExtra("message", entryText.getText().toString()); sendBroadcast(intent); } });
在上述代碼中,第3行代碼創(chuàng)建Intent,將com.example.BroadcastReceiverDem作為識(shí)別廣播消息的字符串標(biāo)識(shí);第4行代碼添加了額外信息;第5行代碼調(diào)用sendBroadcast()函數(shù)發(fā)送廣播消息。
為了能夠使應(yīng)用程序中的BroadcastReceiver接收指定的廣播消息,首先要在AndroidManifest.xml文件中添加Intent過(guò)濾器,聲明BroadcastReceiver可以接收的廣播消息。其中,AndroidManifest.xml文件的完整代碼如代碼清單6-25所示。
代碼清單6-25 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.BroadcastReceiverDemo" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".BroadcastReceiverDemo" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="com.example.BroadcastReceiverDemo" /> </intent-filter> </receiver> </application> <uses-sdk android:minSdkVersion="3" /> </manifest>
在上述代碼中,第14行代碼中創(chuàng)建了一個(gè)<receiver>節(jié)點(diǎn);在第15行中聲明了Intent過(guò)濾器的動(dòng)作為“com.example.BroadcastReceiverDemo”,這與BroadcastReceiverDemo.java文件中Intent的動(dòng)作相一致,表明這個(gè)BroadcastReceiver可以接收動(dòng)作為“com.example. BroadcastReceiverDemo”的廣播消息。
MyBroadcastReceiver.java文件創(chuàng)建了一個(gè)自定義的BroadcastReceiver,其核心代碼如代碼清單6-26所示。
代碼清單6-26 MyBroadcastReceiver.java
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String msg = intent.getStringExtra("message"); Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); } }
在上述代碼中,第1行代碼首先繼承了BroadcastReceiver類;第3行代碼重載了onReceive()函數(shù),當(dāng)接收到AndroidManifest.xml文件定義的廣播消息后,程序?qū)⒆詣?dòng)調(diào)用onReceive()函數(shù)。
6.5 本章小結(jié)
本章主要對(duì)Android組件間的通信進(jìn)行學(xué)習(xí),包括Intent進(jìn)行組件通信的原理、Intent啟動(dòng)Activity的方法、獲取Activity返回值的方法、Intent過(guò)濾器的原理及其匹配機(jī)制、發(fā)送和接收廣播消息的方法。
關(guān)鍵知識(shí)點(diǎn)測(cè)評(píng)
1.以下有關(guān)Intent的說(shuō)法,不正確的一個(gè)是(?。?。
A.Intent不可以用于應(yīng)用程序之間交互
B.Intent可以通過(guò)Context.startActivity()啟動(dòng)一個(gè)Activity
C.Intent可以啟動(dòng)Activity和Service,在Android系統(tǒng)上發(fā)布廣播消息
D.Intent描述了動(dòng)作的產(chǎn)生組件、接收組件和傳遞的數(shù)據(jù)信息
2.以下有關(guān)Intent的敘述,正確的一個(gè)是(?。?。
A.Intent顯式啟動(dòng)時(shí),構(gòu)造函數(shù)的第2個(gè)參數(shù)是應(yīng)用程序上下文
B.隱式啟動(dòng)Activity時(shí),匹配的Activity不可以是第三方應(yīng)用程序提供的
C.Android系統(tǒng)可以在Intent中指明啟動(dòng)的Activity所在的類,也可以根據(jù)Intent的動(dòng)作和數(shù)據(jù)來(lái)決定啟動(dòng)哪一個(gè)Activity
D.只有Activity組件可以注冊(cè)Intent過(guò)濾器
- Reporting with Visual Studio and Crystal Reports
- Java 開發(fā)從入門到精通(第2版)
- 營(yíng)銷數(shù)據(jù)科學(xué):用R和Python進(jìn)行預(yù)測(cè)分析的建模技術(shù)
- 正則表達(dá)式經(jīng)典實(shí)例(第2版)
- jQuery開發(fā)基礎(chǔ)教程
- UI設(shè)計(jì)全書(全彩)
- 深度學(xué)習(xí)原理與PyTorch實(shí)戰(zhàn)(第2版)
- Principles of Strategic Data Science
- Natural Language Processing with Python Quick Start Guide
- Learning Cocos2d-JS Game Development
- jQuery Mobile Web Development Essentials(Second Edition)
- Python Linux系統(tǒng)管理與自動(dòng)化運(yùn)維
- Getting Started with hapi.js
- C語(yǔ)言開發(fā)寶典
- ACE技術(shù)內(nèi)幕:深入解析ACE架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)原理