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

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

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

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

圖6-2 名為Activity2的Activity界面
為使程序達到上述效果,首先,在AndroidManifest.xml文件中注冊上面兩個Activity,應(yīng)使用<activity>標簽,嵌套在<application>標簽內(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)用程序中,用戶使用的每個組件都必須在AndroidManifest.xml文件中的<application>節(jié)點內(nèi)定義,所以<application>節(jié)點下共有兩個<activity>節(jié)點,分別代表應(yīng)用程序中所使用的兩個Activity:IntentDemo和Activity2。
在IntentDemo.java文件中,包含了顯示使用Intent啟動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個參數(shù)是應(yīng)用程序上下文,應(yīng)用程序上下文是IntentDemo;第2個參數(shù)是接收Intent的目標組件,使用的是顯式啟動方式,直接指明了需要啟動的Activity。
同理,在Activity2.java文件中,包含了顯示使用Intent啟動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.隱式啟動
隱式啟動Activity時,Android系統(tǒng)在應(yīng)用程序運行時解析Intent,并根據(jù)一定的規(guī)則對Intent和Activity進行匹配,使Intent上的動作、數(shù)據(jù)與Activity完全吻合。而匹配的Activity可以是應(yīng)用程序本身的,也可以是Android系統(tǒng)內(nèi)置的,還可以是第三方應(yīng)用程序提供的。因此,這種方式更強調(diào)了Android應(yīng)用程序中組件的可復(fù)用性。
由此可以看出,隱式啟動不需要指明需要啟動哪一個Activity,而由Android系統(tǒng)來決定,有利于使用第三方組件。
在默認情況下,Android系統(tǒng)會調(diào)用內(nèi)置的Web瀏覽器,代碼如代碼清單6-5所示。
代碼清單6-5隱式啟動默認情況調(diào)用內(nèi)置Web瀏覽器
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com")); startActivity(intent);
在上述代碼中,Intent的動作是Intent.ACTION_VIEW,是根據(jù)Uri的數(shù)據(jù)類型來匹配動作;數(shù)據(jù)部分的Uri是Web地址,使用Uri.parse(urlString)方法,可以簡單地把一個字符串解釋成Uri對象。
Intent的語法如代碼清單6-6所示。
代碼清單6-6 Intent語法
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlString));
Intent構(gòu)造函數(shù)的第1個參數(shù)是Intent需要執(zhí)行的動作;第2個參數(shù)是Uri,表示需要傳遞的數(shù)據(jù)。
Android系統(tǒng)支持的常見動作字符串常量表,如表6-3所示。
表6-3 Android系統(tǒng)支持的常見動作字符串常量表

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

圖6-3 隱式啟動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行對按鈕“Go”添加監(jiān)聽,當觸摸單擊或通過手機按鍵單擊該按鈕時,觸發(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示例中,通過使用startActivity(Intent)方法啟動Activity后,兩個Activity之間相互獨立,沒有任何關(guān)聯(lián)。然而,在很多情況下,后啟動的Activity是為了讓用戶對特定信息進行選擇,在關(guān)閉這個Activity后,用戶的選擇信息需要返回給未關(guān)閉的那個Activity。由此,按照Activity啟動的先后順序,先啟動的稱為父Activity,后啟動的稱為子Activity,如果需要將子Activity的部分信息返回給父Activity,則可以使用Sub-Activity的方式去啟動子Activity。
獲取子Activity的返回值,一般分以下3個步驟。
1.以Sub-Activity的方式啟動子Activity
首先,調(diào)用startActivityForResult(Intent, requestCode)函數(shù),其中,參數(shù)Intent用于決定啟動哪個Activity,參數(shù)requestCode是唯一的標識子Activity的請求碼。
顯式啟動子Activity的代碼如代碼清單6-11所示。
代碼清單6-11顯式啟動子Activity
int SUBACTIVITY1 = 1; Intent intent = new Intent(this, SubActivity1.class); startActivityForResult(intent, SUBACTIVITY1);
隱式啟動子Activity的代碼如代碼清單6-12所示。
代碼清單6-12隱式啟動子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ù)有兩個參數(shù):結(jié)果碼和返回值。結(jié)果碼表明了子Activity的返回狀態(tài),通常為Activity.RESULT_OK或Activity.RESULT_CANCELED,或自定義的結(jié)果碼,結(jié)果碼均為整數(shù)類型;返回值封裝在Intent中,子Activity通過Intent將需要返回的數(shù)據(jù)傳遞給父Activity。數(shù)據(jù)主要是Uri形式,可以附加一些額外信息,這些額外信息用Extra的集合表示。
代碼清單6-13所示代碼說明了如何在子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中獲取返回值
當子Activity關(guān)閉時,啟動它的父Activity的onActivityResult()函數(shù)將被調(diào)用;如果需要在父Activity中處理子Activity的返回值,則覆蓋此函數(shù)即可。此函數(shù)的語法如代碼清單6-14所示。
代碼清單6-14 onActivityResult()語法
public void onActivityResult(int requestCode, int resultCode, Intent data);
在上述代碼中,第1個參數(shù)requestCode,用來表示是哪一個子Activity的返回值;第2個參數(shù)resultCode用于表示子Activity的返回狀態(tài);第3個參數(shù)data是子Activity的返回數(shù)據(jù),返回數(shù)據(jù)類型是Intent。返回數(shù)據(jù)的用途不同,Uri數(shù)據(jù)的協(xié)議也就不同。也可以使用Extra方法返回一些原始類型的數(shù)據(jù)。
代碼清單6-15所示代碼說明如何在父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行代碼是兩個子Activity的請求碼;第7行代碼對請求碼進行匹配;第9行和第11行代碼對結(jié)果碼進行判斷,如果返回的結(jié)果碼是Activity.RESULT_OK,則在代碼的第10行使用getData()函數(shù)獲取Intent中的Uri數(shù)據(jù);反之,若返回的結(jié)果碼是Activity.RESULT_CANCELED,則不進行任何操作。
ActivityCommunication示例說明了如何以Sub-Activity方式啟動子Activity,以及使用Intent進行組件間的通信。
在如圖6-4所示的主界面中,當用戶單擊“啟動Activity1”和“啟動Activity2”按鈕時,程序?qū)⒎謩e啟動兩個不同的子Activity:子SubActivity1和SubActivity2,它們的界面分別如圖6-5所示。

圖6-4 ActivityCommunication主界面

圖6-5 SubActivity1和SubActivity2界面
SubActivity1提供了一個輸入框,以及“接受”和“撤銷”兩個按鈕,如果在輸入框中輸入信息后單擊“接受”按鈕,程序會把輸入框中的信息傳遞給父Activity,并在父Activity界面上顯示;如果用戶單擊“撤銷”按鈕,則程序不會向父Activity傳遞任何信息。
SubActivity2主要為了說明如何在父Activity中處理多個子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行分別定義了兩個子Activity的請求碼,而在第16行和第23行以Sub-Activity的方式分別啟動兩個子Activity。
第29行代碼是子Activity關(guān)閉后的返回值處理函數(shù),其中requestCode是子Activity返回的請求碼,應(yīng)與第2行和第3行定義的兩個請求碼相匹配;resultCode是結(jié)果碼,在第32行代碼對結(jié)果碼進行判斷,如果等于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行代碼中使用這個Uri構(gòu)造Intent;第15行代碼中,將Intent作為返回值,RESUIT_OK作為結(jié)果碼,通過調(diào)用setResult()函數(shù),將返回值和結(jié)果碼傳遞給父Activity;第16行代碼調(diào)用finish()函數(shù)關(guān)閉當前的子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個參數(shù)為null,表示數(shù)據(jù)需要傳遞給父Activity。
6.3 Intent過濾器
Intent過濾器是一種根據(jù)Intent中的動作(Action)、類別(Categorie)和數(shù)據(jù)(Data)等內(nèi)容,對適合接收該Intent的組件進行匹配和篩選的機制。它可以匹配數(shù)據(jù)類型、路徑和協(xié)議,還可以用來確定多個匹配項順序的優(yōu)先級(Priority)。
應(yīng)用程序的Activity組件、Service組件和BroadcastReceiver都可以注冊Intent過濾器,這些組件在特定的數(shù)據(jù)格式上就可以產(chǎn)生相應(yīng)的動作。
6.3.1 注冊Intent過濾器
注冊Intent過濾器的方法如下:
在AndroidManifest.xml文件的各個組件的節(jié)點下定義<intent-filter>節(jié)點,然后在<intent-filter>節(jié)點中聲明該組件所支持的動作、執(zhí)行的環(huán)境和數(shù)據(jù)格式等信息。
<intent-filter>節(jié)點支持<action>標簽、<category>標簽和<data>標簽,其中:<action>標簽定義Intent過濾器的“類別”;<category>標簽定義Intent過濾器的“動作”;<data>標簽定義Intent過濾器的“數(shù)據(jù)”。
<intent-filter>節(jié)點支持的標簽和屬性如表6-4所示。
表6-4 <intent-filter>節(jié)點支持的標簽和屬性

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

AndroidManifest.xml文件中的每個組件的<intent-filter>都被解析成一個Intent過濾器對象。當應(yīng)用程序安裝到Android系統(tǒng)時,所有的組件和Intent過濾器都會注冊到Android系統(tǒng)中。這樣,Android系統(tǒng)便知道了如何將任意一個Intent請求通過Intent過濾器映射到相應(yīng)的組件上。
6.3.2 Intent解析
Intent到Intent過濾器的映射過程稱為“Intent解析”。Intent解析可以在所有組件中找到一個可與請求的Intent達成最佳匹配的Intent過濾器。
Intent解析的匹配規(guī)則。
? Android系統(tǒng)把所有應(yīng)用程序包中的Intent過濾器集合在一起,形成一個完整的Intent過濾器列表。
? 在Intent與Intent過濾器進行匹配時,Android系統(tǒng)會將列表中所有Intent過濾器的“動作”和“類別”與Intent進行匹配,任何不匹配的Intent過濾器都將被過濾掉。沒有指定“動作”的Intent過濾器可以匹配任何的Intent,但沒有指定“類別”的Intent過濾器只能匹配沒有“類別”的Intent。
? 把Intent數(shù)據(jù)Uri的每個子部與Intent過濾器的<data>標簽中的屬性進行匹配,如果<data>標簽指定了協(xié)議、主機名、路徑名或MIME類型,那么這些屬性都要與Intent的Uri數(shù)據(jù)部分進行匹配,任何不匹配的Intent過濾器均被過濾掉。
? 如果Intent過濾器的匹配結(jié)果多于一個,則可以根據(jù)在<intent-filter>標簽中定義的優(yōu)先級標簽來對Intent過濾器進行排序,優(yōu)先級最高的Intent過濾器將被選擇。
在此以6.2.1 節(jié)中隱式啟動Activity的例子WebViewIntentDemo為基礎(chǔ),在AndroidManifest.xml文件中注冊Intent過濾器,以及設(shè)置<intent-filter>節(jié)點屬性來捕獲指定的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)用程序的操作注冊到系統(tǒng)中,當用戶調(diào)用Intent時,可以根據(jù)輸入的ACTION和Uri參數(shù)來找到這個應(yīng)用程序。例如,在上述代碼中,以http協(xié)議為例,打開Google Map可以用URI: geo:38.899533,-77.036476。
此外,“協(xié)議”在Android里都可以隨便定義。例如,寫一個打開文件的關(guān)聯(lián)Intent,如file:///sdcard/abc.txt。也可以用type進行關(guān)聯(lián),其代碼如代碼清單6-20所示。
代碼清單6-20用type進行關(guān)聯(lián)
<intent-filter> <action android:name="android.intent.action.VIEW" /> <type android:value="test.item"/> </intent-filter>
然后,通過代碼清單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)用程序注冊了BroadcastReceiver,則可以接收到指定的廣播消息。
下面將介紹廣播信息的使用方法。
首先,創(chuàng)建一個Intent。調(diào)用sendBroadcast()函數(shù),就可把Intent攜帶的消息廣播出去,如果要在Intent傳遞額外數(shù)據(jù),可以用Intent的putExtra()方法。
注意:
在構(gòu)造Intent時必須用全局唯一的字符串標識其要執(zhí)行的動作,通常使用應(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文件或在代碼中注冊一個BroadcastReceiver,并在其中使用Intent過濾器指定要處理的廣播消息。在BroadcastReceiver接收到與之匹配的廣播消息后,onReceive()方法會被調(diào)用;onReceive()方法必須要在5秒內(nèi)執(zhí)行完畢,否則Android系統(tǒng)會認為該組件失去響應(yī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)用程序不需要一直運行,當Android系統(tǒng)接收到與之匹配的廣播消息時,會自動啟動此BroadcastReceiver?;谝陨咸卣?,BroadcastReceiver適合做一些資源管理的工作。
如圖6-6所示,BroadcastReceiverDemo示例說明了如何在應(yīng)用程序中注冊BroadcastReceiver,并接收指定類型的廣播消息。

圖6-6 BroadcastReceiverDemo示例
如圖6-6所示,在單擊“發(fā)送廣播消息”按鈕后,EditText控件中內(nèi)容將以廣播消息的形式發(fā)送出去,示例內(nèi)部的BroadcastReceiver將接收這個廣播消息,并顯示在用戶界面的下方。
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作為識別廣播消息的字符串標識;第4行代碼添加了額外信息;第5行代碼調(diào)用sendBroadcast()函數(shù)發(fā)送廣播消息。
為了能夠使應(yīng)用程序中的BroadcastReceiver接收指定的廣播消息,首先要在AndroidManifest.xml文件中添加Intent過濾器,聲明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)建了一個<receiver>節(jié)點;在第15行中聲明了Intent過濾器的動作為“com.example.BroadcastReceiverDemo”,這與BroadcastReceiverDemo.java文件中Intent的動作相一致,表明這個BroadcastReceiver可以接收動作為“com.example. BroadcastReceiverDemo”的廣播消息。
MyBroadcastReceiver.java文件創(chuàng)建了一個自定義的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ù),當接收到AndroidManifest.xml文件定義的廣播消息后,程序?qū)⒆詣诱{(diào)用onReceive()函數(shù)。
6.5 本章小結(jié)
本章主要對Android組件間的通信進行學習,包括Intent進行組件通信的原理、Intent啟動Activity的方法、獲取Activity返回值的方法、Intent過濾器的原理及其匹配機制、發(fā)送和接收廣播消息的方法。
關(guān)鍵知識點測評
1.以下有關(guān)Intent的說法,不正確的一個是(?。?。
A.Intent不可以用于應(yīng)用程序之間交互
B.Intent可以通過Context.startActivity()啟動一個Activity
C.Intent可以啟動Activity和Service,在Android系統(tǒng)上發(fā)布廣播消息
D.Intent描述了動作的產(chǎn)生組件、接收組件和傳遞的數(shù)據(jù)信息
2.以下有關(guān)Intent的敘述,正確的一個是( )。
A.Intent顯式啟動時,構(gòu)造函數(shù)的第2個參數(shù)是應(yīng)用程序上下文
B.隱式啟動Activity時,匹配的Activity不可以是第三方應(yīng)用程序提供的
C.Android系統(tǒng)可以在Intent中指明啟動的Activity所在的類,也可以根據(jù)Intent的動作和數(shù)據(jù)來決定啟動哪一個Activity
D.只有Activity組件可以注冊Intent過濾器
- Functional Python Programming
- 深入核心的敏捷開發(fā):ThoughtWorks五大關(guān)鍵實踐
- Deploying Node.js
- CockroachDB權(quán)威指南
- arc42 by Example
- 新編Premiere Pro CC從入門到精通
- Java設(shè)計模式及實踐
- Instant RubyMotion App Development
- Getting Started with NativeScript
- C程序設(shè)計實踐教程
- 超簡單:用Python讓Excel飛起來(實戰(zhàn)150例)
- Java 9 with JShell
- Python數(shù)據(jù)科學實踐指南
- LabVIEW入門與實戰(zhàn)開發(fā)100例(第4版)
- 精益軟件開發(fā)管理之道