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

第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過濾器

主站蜘蛛池模板: 清远市| 马山县| 乐东| 建湖县| 平阳县| 乌兰察布市| 郧西县| 农安县| 陕西省| 文成县| 武胜县| 东乌| 安达市| 临朐县| 阿拉善盟| 凤凰县| 三门县| 玉林市| 会泽县| 合肥市| 吉安市| 郎溪县| 罗源县| 新邵县| 荥经县| 海口市| 滕州市| 广东省| 许昌市| 丹江口市| 巫山县| 鄱阳县| 无锡市| 涞水县| 武穴市| 嘉善县| 东乌| 岳池县| 泊头市| 松潘县| 斗六市|