官术网_书友最值得收藏!

第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ò)濾器

主站蜘蛛池模板: 江口县| 南宁市| 宜都市| 航空| 新疆| 呼伦贝尔市| 新兴县| 齐河县| 开封县| 巫山县| 璧山县| 武隆县| 信阳市| 通榆县| 中宁县| 宁都县| 浮山县| 正宁县| 盐源县| 汨罗市| 黑河市| 于田县| 临澧县| 栖霞市| 隆尧县| 丁青县| 札达县| 桐柏县| 前郭尔| 九寨沟县| 明溪县| 沙田区| 泰安市| 鄂托克旗| 灯塔市| 工布江达县| 揭东县| 梨树县| 二连浩特市| 东城区| 新竹县|