Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android引入了一個名為Intent的概念用來喚醒各種組件

Android引入了一個名為Intent的概念用來喚醒各種組件

編輯:關於Android編程

  Android引入了一個名為Intent的概念用來喚醒各種組件。Android中的組件包括:activities(UI 組件),services(後台代碼),broadcast receivers(用來接收廣播消息的代碼)和content providers(用來抽象數據的代碼)。        Android的Intent基礎        盡管將intent作為喚醒其他組件機制是很好理解的,不過Android還賦予了Intent這個概念更多的含義。你可以在你的應用中通過intent喚醒其他的應用,還可以喚醒應用內部及外部的各種組件。你可以通過intent發起事件,而其它人則通過一種類似發布與訂閱的方式來做出相應。你可以通過intent喚醒鬧鐘提醒。        注:什麼是intent,簡而言之,可以說intent就是一個動作,並且該動作上負載著數據。        從最簡單的水平來看,intent是一個讓Android去做(喚醒)什麼的動作。Android喚醒某個動作取決於為該動作注冊了什麼內容。假設你的Activity內容如下:   public class BasicViewActivity extends Activity  { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.some_view); } }//eof-class          some_view布局應該指向res/layout下的一個合法文件。Android然後允許你通過在該應用的manifest文件中注冊這個activity,這樣改activity就可以被喚醒了。注冊方法如下:   <activity android:name=".BasicViewActivity"             android:label="Basic View Tests"> <intent-filter>       <action android:name="com.androidbook.intent.action.ShowBasicView"/>       <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>            這種注冊方法不僅僅包括注冊一個activity,還包括一個可以喚醒該activity的動作(action)。Activity的設計者通常會為這個action取個名字,並且將這個action作為intent filter(意圖過濾器)的一部分。本章後面會介紹更多關於intent的內容。        現在你已經指定了一個activity並通過action對其進行了注冊,這樣就可以通過一個intent來喚醒這個BasicViewActivity了。   public static void invokeMyApplication(Activity parentActivity) { String actionName= "com.androidbook.intent.action.ShowBasicView"; Intent intent = new Intent(actionName); parentActivity.startActivity(intent); }             注:action名字的通常形式為:<你的包名>.intent.action.具體名稱。        一旦BasicViewActivity被喚醒,它就可以查看喚醒它的intent了。下面是重寫的可以處理喚醒BasicViewActivity的intent的代碼:   public class BasicViewActivity extends Activity  { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.some_view); Intent intent = this.getIntent(); if (intent == null) {     Log.d("test tag", "This activity is invoked without an intent"); } } }//eof-class               Android中的Intent        你可以通過測試來用intent喚醒Android自帶的一些應用。http://developer.android.com/guide/appendix/g-appintents.html頁面介紹了一些可以被喚醒的Android自帶應用。        注:這些名單可能根據Android發布版本不同而發生變化。        剩下的可被喚醒的應用包括:        浏覽器應用,用來打開浏覽器窗口。        打電話應用。        撥號鍵盤,用戶通過其撥打號碼。        地圖應用,用來顯式給定經緯度的地址。給定經緯度的地址。        谷歌街景應用。        Listing5-1介紹如何根據這些應用發布的intents來喚醒它們:   Listing 5–1. Exercising Android’s Prefabricated Applications public class IntentsUtils { public static void invokeWebBrowser(Activity activity) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("http://www.google.com")); activity.startActivity(intent); } public static void invokeWebSearch(Activity activity) { Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.setData(Uri.parse("http://www.google.com")); activity.startActivity(intent); } public static void dial(Activity activity) { Intent intent = new Intent(Intent.ACTION_DIAL); activity.startActivity(intent); } public static void call(Activity activity) { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:555–555–5555")); activity.startActivity(intent); }      public static void showMapAtLatLong(Activity activity) { Intent intent = new Intent(Intent.ACTION_VIEW); //geo:lat,long?z=zoomlevel&q=question-string intent.setData(Uri.parse("geo:0,0?z=4&q=business+near+city")); activity.startActivity(intent); } public static void tryOneOfThese(Activity activity) { IntentsUtils.invokeWebBrowser(activity); } }             只要你創建一個簡單的帶有菜單的activity,你就可以通過 tryOneOfThese(Activity activity)方法來測試上述代碼。創建一個簡單的菜單十分簡單,見Listing5-2:   Listing 5–2. A Test Harness to Create a Simple Menu public class MainActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tv = new TextView(this); tv.setText("Hello, Android. Say hello"); setContentView(tv); } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); int base=Menu.FIRST; // value is 1 MenuItem item1 = menu.add(base,base,base,"Test"); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == 1) { IntentUtils.tryOneOfThese(this);           } else {       return super.onOptionsItemSelected(item); }             return true; } }             注:可以參考第二章來創建Android工程,編譯並運行應用。你也可以通過第7章的前半部分來查看更多創建菜單的代碼。或者你可以通過本章末尾關於這部分的eclipse工程代碼鏈接下載代碼。不過,當你下載完代碼後,這個主要的activity可能有些不同,但是要表達的意思是相同的。在示例代碼中,我們還通過XML文件來創建菜單。        Intent的組成        另一個確定的可以更好的理解intent的方法就是查看intent對象的組成結構。一個intent包括action、data(通過URI表示)、一個用來存儲外部數據的鍵值對映射和一個顯式的類名(稱為組件名稱component name)。只要intent中至少包含上述內容的一個,其它的內容都是可選的。        注:如果一個intent包含一個組件名稱,那麼該intent被稱為顯式intent。如果intent不包含組件名稱,而是依賴於其它部分,如action、data,那麼該intent被稱為隱式intent。隨著我們進一步深入,你將會發現這兩種intent是有著細微的差別的。        Intents和數據URIs        現在我們已經看到了最簡單的intent,這種intent僅需要一個action名稱。Listing5-1的ACTION_DIAL就是一例。要喚醒撥號器,我們除了設置intent的aciton之外,不需要其他任何設置。   public static void dial(Activity activity) { Intent intent = new Intent(Intent.ACTION_DIAL); activity.startActivity(intent); }             與ACTION_DIAL不同,ACTION_CALL用來通過給定的號碼來進行呼叫,而給定的號碼則存儲在名為Data的參數中。這個參數指向一個URI,而這個URI又指向一個號碼。 public static void call(Activity activity) { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:555–555–5555")); activity.startActivity(intent); }         Intent的action是一個字符串或者字符串常量,通常將java的包名作為前綴。        Intent的data部分通常並不是一個data數據,而是指向data的引用。data部分通過字符串來表示URI。Intent的URI可以包含參數,與網頁URL類似。        每個被action喚醒的activity都應該指定URI的格式。本例中,“call”方法決定了需要什麼樣的數據URI。通過該URI,可以解析出電話號碼。        注:被喚醒的activity也可以使用這個URI作為一個數據指針,從而解析出數據並加以使用。對於媒體,如音頻、視頻和圖像來說正是如此。        通用Actions        Intent.ACTION_CALL和Intent.ACTION_DIAL會很容易誤導我們,讓我們認為action和其喚醒的對象之間存在著一對一的關系。為了反證這個觀點,我們看一個相反的例子,如Listing5-1中的代碼所示:   public static void invokeWebBrowser(Activity activity) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("http://www.google.com")); activity.startActivity(intent); }             請注意其action僅僅是簡單的定義為ACTION_VIEW。Android如何通過這個寬泛的action來確定要喚醒那個activity呢。這種情況下,Android就不僅僅依靠這個通用的action了,還需要依賴URI的特性。Android會查看URI的scheme部分,這部分恰好是http,然後查詢所有注冊的activities,看誰可以理解這個scheme。這樣就可以查詢哪個activity可以處理View動作並被喚醒。由於這個原因,browser activity就應該注冊一個View intent,並且包含http作為數據scheme。在manifest文件中聲明這種intent的方法如下:   <activity......> <intent-filter> <action android:name="android.intent.action.VIEW" /> <data android:scheme="http"/> <data android:scheme="https"/> </intent-filter> </activity>             你可以通過http://developer.android.com/guide/topics/manifest/data-element.html 來學習更多的關於數據節點的屬性。        Intent 過濾器節點中數據xml子節點的子元素或特性包括下面內容:        host      mimeType      path      pathPattern      pathPrefix      port      scheme        mimeType將是你經常用到的屬性。例如下面的例子表明展示notes列表的activity的intent filter的mimeType是一個包含多個notes的目錄。 <intent-filter>       <action android:name="android.intent.action.VIEW" />       <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter>         該intent filter可以讀作“喚醒該activity來浏覽notes集合”。        另一方法,如果只展示一個單獨note,其intent filter使用的MIME類型為單條目類型:   <intent-filter>       <action android:name="android.intent.action.VIEW" />       <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter>            該intent filter可以讀作“喚醒該activity來浏覽單個note”。         使用Extra信息            Intent除了action和數據等主要屬性外,還有一個名為extras的屬性。Extra可以為接收該intent的組件提供更多的信息。Extra數據時以鍵值對的形式存在:鍵名通常以包名開始,而值可以是基本數據類型或其他任意實現了android.os.Parcelable的對象。這個extra信息在Android中被稱為android.os.Bundle。        Intent類提供了兩種方法來訪問extra Bundle:        // 從intent獲取bundle      Bundle extraBundle = intent.getExtras();        // 將一個bundle放進intent中      Bundle anotherBundle = new Bundle();      // 為bundle填充鍵值對      。。。      // 將bundle設置僅intent中      intent.putExtras(anotherBundle);        getExtras()很簡單明了:就是返回intent所持有的bundle。putExtras()首先檢查當前intent是否已持有bundle,如果有,則將新的bundle中的鍵值對轉移到原有的bundle中,如果沒有,則先創建一個bundle,然後將新的bundle中的鍵值對復制到新創建的bundle中。        注:putExtra方法是復制原有bundle內容,而非引用。這樣,如果你稍後修改傳入的bundle也不會修改已經存入intnet的bundle。        你可以用很多方法來存儲基本數據類型到bundle中。下面是一些將簡單數據類型存入bundle中的方法:   putExtra(String name, boolean value);  putExtra(String name, int value); putExtra(String name, double value); putExtra(String name, String value);         還有一些不是太簡單的數據類型的例子:   //simple array support putExtra(String name, int[] values); putExtra(String name, float[] values);    //Serializable objects putExtra(String name, Serializable value);    //Parcelable support putExtra(String name, Parcelable value);    //Add another bundle at a given key //Bundles in bundles putExtra(String name, Bundle value);    //Add bundles from another intent //copy of bundles putExtra(String name, Intent anotherIntent);    //Explicit Array List support putIntegerArrayListExtra(String name, ArrayList arrayList); putParcelableArrayListExtra(String name, ArrayList arrayList); putStringArrayListExtra(String name, ArrayList arrayList);         在接收側,會有相應的獲取數據的方法。這些方法將鍵的名字作為參數。在下面網址可以查詢更多相關內容: http://developer.android.com/reference/android/content/Intent.html#EXTRA_ALARM_COUNT.          下面我們看一下該網址列舉的在發送email時涉及到的兩個例子:        EXTRA_EMAIL:你可以通過該鍵值獲取emal地址集合。該鍵值為android.intent.extra.EMAIL。它應該指向一個包含email文本地址的數組。        EXTRA_SUBJECT:你可以通過該鍵值獲取郵件的主題。該鍵值為android.intent.extra.SUBJECT.它指向一個包含主題的字符串。        使用組件直接喚醒Activity        你已經看到一些用intent喚醒activity的方法了。你見到了用一個顯式的action喚醒activity,也見到了用一個通用的action借助data URI的幫助來喚醒activity。Android還提供了一種更為直接的方法來喚醒activity:你可以直接通過組件名稱來實現,而組件名就是對象的包名和類名的一種抽象。下面是Intent類指定組件名的方法:   setComponent(ComponentName name);  setClassName(String packageName, String classNameInThatPackage); setClassName(Context context, String classNameInThatContext); setClass(Context context, Class classObjectInThatContext);             而最終這些方法都是setComponent(ComponentName name) 方法的簡寫。        組件名稱包含包名和類名。例如,下面就是一個喚醒模擬器自帶的cotacts activity的方法:   Intent intent = new Intent(); intent.setComponent(new ComponentName(      "com.android.contacts"      ,"com.android.contacts.DialContactsEntryActivity"); startActivity(intent);        請注意包名和類名是全稱,且在傳入intent之前首先用於構建ComponentName。        你也可以直接使用類名,而無需用組件名來喚醒activity。再次看下面的activity:   public class BasicViewActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.some_view); } }//eof-class                       你可以通過下面方法來喚醒此activity:   Intent directIntent = new Intent(activity, BasicViewActivity.class); activity.start(directIntent);        不管你用什麼方法來喚醒activity,你都有在AndroidManifest.xml文件中注冊這個activity:   <activity android:name=".BasicViewActivity"       android:label="Test Activity">                  注:如果通過類名來喚醒activity,則不需要任何intent filter。正如前面所說,這種intent稱為顯式intent。由於這種顯式的intent已經指明了要喚醒的組件的全稱,則不需要其他額外的部分。        理解intent category        你可以將activities劃分為多個類別,這樣你可以根據類別名來搜索它們。例如,啟動階段,Andorid會尋找category為CATEGORY_LAUNCHER的activity,然後將該activity的名字及圖表放在主界面上用於啟動。        還有一個例子:android會搜索一個category為CATEGORY_HOME的activity,在開始階段作為主屏幕顯示。類似的,如果activity的類別名為CATEGORY_GADGET,則其會作嵌入到其它activity中進行復用。        以CATEGORY_LAUNCHER為例,類別名的格式如下:        android.intent.category.LAUNCHER        你需要知道這些具體的字符串,因為category將作為intent filter的一部分寫入AndroidManifest.xml文件中。下面是一個例子:   <activity android:name=".HelloWorldActivity" 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可能有一些特定的屬性來進行限制或者使用,例如你是否想將activity嵌入到父activity中。這種activity屬性也是通過category來進行設定的。        下面我們看一下android預定義的category,以及如何使用它們:        Table 5–1.Activity Categories 及其描述   category名稱 描述 CATEGORY_DEFAULT 如果activity想被一個隱式的intent喚醒,那麼可以聲明為CATEGORY_DEFAULT。如果不定義該屬性,那麼activity需要顯式的intent進行喚醒。因此,你可以看到那些被通用的action或其他action喚醒的activity使用缺省的category說明 CATEGORY_BROWSABLE 用來想浏覽器聲明當其被喚醒時不會影響到浏覽器的安全需求 CATEGORY_TAB 該activity被嵌在一個父tabbed activity中 CATEGORY_ALTERNATIVE activity使用CATEGORY_ALTERNATIVE用來浏覽特定類型的數據。當你查閱文檔是,這些部分通常作為一個可選菜單。例如打印界面相對於其他界面可以稱之為alternative CATEGORY_SELECTED_ALTERNATIVE activity使用CATEGORY_ALTERNATIVE用來浏覽特定類型的數據。這與列出一系列的文本文件或html文件編輯器很類似。 CATEGORY_LAUNCHER activity使用CATEGORY_LAUNCHER屬性可以使其在launcher(桌面)中顯示 CATEGORY_HOME activity使用CATEGORY_HOME後可以作為主屏幕。特別的,應該只有一個activity具有該屬性,如果有多個,系統會讓你做出選擇。 CATEGORY_PREFERENCE activity使用CATEGORY_PREFERENCE屬性表明其為preference activity。這樣該activity將作為preference screen的一部分進行顯示 CATEGORY_GADGET activity使用CATEGORY_GADGET就可以嵌入到父activity中 CATEGORY_TEST 表明這是一個測試activity CATEGORY_EMBED 該屬性已被CATEGORY_GADGET取代,保留只為後向兼容        你可以在下面網址閱讀更多關於category的介紹:       http://developer.android.com/android/reference/android/content/Intent.html#CATEGORY_ALTERNATIVE.             當你喚醒一個activity時,你可以通過設置category來確定要喚醒什麼類型的activity。或者你可以搜索到滿足特定category的activity。下面是一個獲取與CATEGORY_LAUNCHER相匹配的activity的方法:   Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); PackageManager pm = getPackageManager(); List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);        PackageManager是一個可以讓你獲取到與特定category匹配的activity而又不用將其喚醒的關鍵類。你可以遍歷返回的activities,當遇到合適的activity可以再將其喚醒。下面是一個如何遍歷activities,以及如何喚醒其中相匹配的一個activity的例子,我們用了一個隨機的名字來進行測試:   for(ResolveInfo ri: list) { //ri.activityInfo. Log.d("test",ri.toString()); String packagename = ri.activityInfo.packageName; String classname = ri.activityInfo.name; Log.d("test", packagename + ":" + classname); if (classname.equals("com.ai.androidbook.resources.TestActivity")) { Intent ni = new Intent(); ni.setClassName(packagename,classname); activity.startActivity(ni); } }                      你也可以僅僅依靠category來喚醒一個activity,如CATEGORY_LAUNCHER.   public static void invokeAMainApp(Activity activity) { Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); activity.startActivity(mainIntent); }             會有不止一個activity與之匹配,那麼android會選擇哪一個呢?為了解決這個問題,Android彈出一個相匹配的activity列表(complete action using)對話框,這樣你就可以選擇其中一個運行。        下面是另一個去往home主頁的例子:   //Go to home screen Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_HOME); startActivity(mainIntent);        如果你不喜歡android默認的主頁,那麼你可以自己寫一個,並與category HOME進行標記。這樣,再使用前面的代碼,就會彈出選項,讓你選擇主頁。這是因為現在已經注冊有不止一個主頁了。   //Replace the home screen with yours <intent-filter> <action android:value="android.intent.action.MAIN" /> <category android:value="android.intent.category.HOME"/> <category android:value="android.intent.category.DEFAULT" /> </intent-filter>                  Intent喚醒組件的規則        到目前為止,我們已經討論了intent的許多方面。簡單總結一下就是:actions、data URIs、extra data和category。有了這些,android通過intent filter,依據多種策略來匹配最合適的activity。        最頂層是與intent相關聯的組件名。如果設置了這個,那麼intent就是顯式intent。對於的intent,只有組件名是重要的,其他的所有屬性都可以忽略。如果一個intent沒有指明組件名,那麼該intent被稱為隱式intent。而隱式intent的處理規則則有很多。intent。對於顯式的intent,只有組件名是重要的,其他的所有屬性都可以忽略。如果一個intent沒有指明組件名,那麼該intent被稱為隱式intent。而隱式intent的處理規則則有很多。        最基本的規則就是傳入的intent的action、category和data屬性必須與intent filter中聲明的屬性相匹配。Intent filter與intent不同,可以聲明多個actions、categories和data屬性。這表明,同一個intent filter可以匹配多個intent,也就是說一個activity可以對多個intent做出反應。不過,“匹配”這個概念在action、category和data中並不相同。下面我們看一下對於不同的屬性匹配規則有何不同。        Action        如果一個intent設定了action屬性,那麼intent的filter中必須有相同的action或者不包含任何action。所以,對於不定義任何action的intent filter可以與任何action相匹配。        如果一個intent filter定義了多個actions,那麼至少需要含有一個action與傳入intent的action相匹配。        Data        如果intent filter中沒有定義data屬性,那麼它將不會與任何定義了data屬性的intent相匹配。這說明該intent filter只尋找沒有定義data屬性的intent。        Filter中缺少data屬性和缺少action屬性是截然相反的。如果沒有action屬性,那麼所有的action都會匹配。如果沒有data屬性,那麼即使data只有1個bit,也不會匹配。        Data類型        為了匹配data的類型,傳入的intent的data的類型必須與intent filter定義的數據類型相同。Intent中的data類型必須在intent filter中列出。        出入的數據類型由兩種方式決定。第一種:如果data URI是一個content或者file URI,那麼content provider或者Android本身將會識別出其類型。第二種:檢查intent中顯式定義的數據類型。對於第二章,傳入的intent不應該設置data URI,因為當intent的setType方法調用時會自動處理data URI。        Android也允許其MIME類型的子類型用星號(*)來代替所有的子類型。        另外,data類型時大小寫敏感的。        Data Scheme        為了匹配data scheme,傳入的intente的data scheme必須與intent filter中的scheme屬性相匹配。也就是說傳入的data scheme必須存在於intent filter中。        傳入的data scheme是intent的data URI的第一部分。Intent中沒有設置scheme的方法。其僅能從傳入的data URI中(如http://www.somesite.com/somepath. )解析出來。        如果傳入的intent的data URI是content:或者file:,那麼就認為其與intent filter相匹配,而不必考慮scheme、domain和path。根據Android SDK,這樣的原因是所有的組件都被設計為知道如何從content或者file URLs中讀取數據。換句話說,所有的組件都被設計為支持這兩種類型的URLs。        Scheme也是大小寫敏感的。        如果intent filter中沒有指明authority屬性,那麼傳入的任何URI的authority(域名)都與之匹配。如果在filter中指定了authority,例如www.somesite.com,那麼其中一個scheme和一個authoriy必須與傳入的intent中的data URI相匹配。        例如,我們在intent filter中指定authority是www.somesite.com,scheme是https。那麼intent中的http://www.somesite.com將不會與之匹配。因為http並沒有被filter指定為支持的scheme。        Authority也是大小寫敏感的。        Data Path        如果intent filter中沒有定義data path,表示與任意的傳入的intent的data path相匹配。如果filter中指定了一個data path,例如somepath,那麼一個scheme,一個authority和一個path就應該與傳入的intent的data URI相匹配。        換而言之,scheme、authority和path一起來確定某個傳入的intent是否合法,例如http://www.somesite.com/somepath.所以,scheme、authority和path並不是獨立工作,而是一起工作。        Path也是大小寫敏感。        Intent Category        所有傳入的intent的category必須在intent filter中列舉出來。在filter中可以包含多個categories。如果filter中沒有設置category,那麼也只能匹配沒有設置category的intent。        不過,這裡有一個忠告。Android這樣處理傳入到startActivity中的隱式intent:默認intent至少包含一個category,也就是android.intent.category.DEFAULT。startActivity()中的代碼會尋找filter中包含DEFAULT Category的activities。所以,任何想要被隱式intent喚醒的activity都必須在intent filter中設定DEFAULT Category。        即使一個activity的intent filter中不包含default category,如果你知道其顯式的組件名稱,你也可以向laucher那樣啟動該activity。如果你不考慮default category而顯式的搜索與intent匹配的activity,你也可以用那種方法啟動這些activities。        這樣,DEFAULT category是根據startActivity()的實現的產物,而不是filter中固有的行為。        還有一個利好的消息:如果activity僅僅想被laucher喚醒,那麼default category並不是必須的。所以這些activities僅僅設置了MAIN和LAUCHER category。不過這些activities中也可以設置DEFAULT category。        以ACTION_PICK為例        到現在我們已經介紹了一些用來喚醒activity而不需要返回數據的intents或者actions。下面我們再介紹一種稍微復雜點的能夠在喚醒activity後返回數據的action。ACTION_PICK就是其中一個。        ACTION_PICK的目的是喚醒一個activity,該activity列出一系列條目,然後運行你選擇其中的某個條目。一旦用戶選中了某個條目,則該activity應該返回選中條目的URI給調用者。這樣就可以復用UI的功能來進行選擇了。        你應該通過MIME類型指明要選擇的條目集合,該MIME類型指向Android的content cursor。其URI的MIME類型應該類似於下面的形式:        vnd.android.cursor.dir/vnd.google.note        Activity負責從基於URI的content provider中提取數據。這也是為什麼需要盡可能的把數據封裝到content provider中的原因。        對於返回數據的action,我們不能使用startActivity(),因為startActivity()並不返回數據。startActivity()不返回數據的原因是該方法通過一個獨立的線程來啟動activity,而將主線程繼續用來處理事務。換句話說,startActivity()是一個異步的方法,且沒有回調函數,這樣就無法得知被喚醒的activity的狀態。如果你想要返回數據,那麼可以使用startActivity()的一個變形startActivityForResult(),該方法帶有一個回調函數。        讓我們看一下startActivityForResult()的定義:        public void startActivityForResult(Intent intent, int requestCode)        這個方法啟動一個activity,並且你想從該activity中獲取返回數據。如果這個activity退出後,原來的activity中的onActivityResult()方法將被調用,並且返回之前傳入的requestCode。該方法定義如下:        protected void onActivityResult(int requestCode, int resultCode, Intent data)        其中requestCode就是你傳入startActivityForResult()的參數。而resultCode可以是RESULT_OK, RESULT_CANCELLED或者用戶定義的數字。用戶自定義數字應該從RESULT_FIRST_USER開始。Intent參數包含activity返回的其他數據。在ACTION_PICK例子中,該intent返回指向某個條目的data URI。        Listing5-3是一個返回結果的activity的例子。        注:Listing5-3中的例子認為你已經安裝了android sdk包中的NotePad應用。我們後面會給出鏈接來告訴你如何下載該應用。   Listing 5–3. Returning Data After Invoking an Action public class SomeActivity extends Activity { ..... ..... public static void invokePick(Activity activity) { Intent pickIntent = new Intent(Intent.ACTION_PICK); int requestCode = 1; pickIntent.setData(Uri.parse( "content://com.google.provider.NotePad/notes")); activity.startActivityForResult(pickIntent, requestCode); } protected void onActivityResult(int requestCode ,int resultCode ,Intent outputIntent) { //This is to inform the parent class (Activity) //that the called activity has finished and the baseclass //can do the necessary clean up super.onActivityResult(requestCode, resultCode, outputIntent); parseResult(this, requestCode, resultCode, outputIntent); } public static void parseResult(Activity activity , int requestCode      , int resultCode , Intent outputIntent) { if (requestCode != 1) { Log.d("Test", "Some one else called this. not us"); return; } if (resultCode != Activity.RESULT_OK) { Log.d(Test, "Result code is not ok:" + resultCode); return;           } Log.d("Test", "Result code is ok:" + resultCode); Uri selectedUri = outputIntent.getData(); Log.d("Test", "The output uri:" + selectedUri.toString()); //Proceed to display the note outputIntent.setAction(Intent.ACTION_VIEW); startActivity(outputIntent); }          RESULT_OK, RESULT_CANCELED,和 RESULT_FIRST_USER常量都在Activity類中定義。其對應的數值為:        RESULT_OK = -1;       RESULT_CANCELED = 0;      RESULT_FIRST_USER = 1;         為了保證PICK操作能夠成功,實現者必須顯式的指明PICK需要什麼。我們看一下在google的NotePad例子中是如何實現的。當列表中的條目被選中時,會檢查傳入的用來喚醒activity的intent的action是否是ACTION_PICK.如果是,則選中項目的URI將被放入一個新的intent中並通過setResult()方法傳回。   @Override protected void onListItemClick(ListView l, View v, int position, long id) { Uri uri = ContentUris.withAppendedId(getIntent().getData(), id); String action = getIntent().getAction(); if (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action)) { // The caller is waiting for us to return a note selected by // the user. They have clicked on one, so return it now. setResult(RESULT_OK, new Intent().setData(uri)); } else { // Launch activity to view/edit the currently selected item startActivity(new Intent(Intent.ACTION_EDIT, uri)); } }         以GET_CONTENT Action為例        ACTION_GET_CONTENT與ACTION_PICK類似。在ACTION_PICK的例子中,你指定一個URI,使其指向多個條目的集合,例如多個notes的集合。你期待著選取一個條目然後將其返回給調用者。而在ACTION_GET_CONTENT例子中,你告訴Android你想要一個特定MIME類型的條目。Android會尋找那些能夠創建該MIME類型條目的activities或者已經存在該MIME類型的條目可以從中選擇的activities。        使用ACTION_GET_CONTENT,你可以通過下面的代碼從notes集合中選取一個NotePad應用所支持的note:   public static void invokeGetContent(Activity activity) { Intent pickIntent = new Intent(Intent.ACTION_GET_CONTENT); int requestCode = 2; pickIntent.setType("vnd.android.cursor.item/vnd.google.note"); activity.startActivityForResult(pickIntent, requestCode); }             請注意如何設置一個單條目的MIME類型。與ACTION_PICK不同,其輸入的是一個data URI:   public static void invokePick(Activity activity) { Intent pickIntent = new Intent(Intent.ACTION_PICK); int requestCode = 1; pickIntent.setData(Uri.parse( "content://com.google.provider.NotePad/notes")); activity.startActivityForResult(pickIntent, requestCode); }         對於負責響應ACTION_GET_CONTENT的activity,應該在intent  filter中注冊相應的MIME類型。下面是sdk中NotePad應用如何實現這一點的方法:   <activity android:name="NotesList" android:label="@string/title_notes_list"> ...... <intent-filter> <action android:name="android.intent.action.GET_CONTENT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter> ...... </activity>             至於如何處理onActivityResult,與ACTION_PICK是完全相同的。如果有多個activities都可以返回相同的MIME類型,android會彈出選擇菜單供你選擇。               Pending Intents介紹         Android有一個intent的變種稱為Pending intent。該intent運行android的組件在某個位置將intent存儲起來比便在將來使用,這樣該組件可以被再次喚醒。例如,在鬧鐘管理器中,你想要在鬧鐘關閉後再開啟一個服務。Android先創建一個pending intent將對應的普通intent包裝起來,然後存儲在某個地方,這樣即使調用的進程被殺死後,這個intent也可以被傳遞給目標。在pending intent創建時,android會存儲足夠的原始進程的信息,這樣在分配或喚醒時,可以查看其安全證書。        我們看一下如何創建pending intent:   Intent regularIntent; PendingIntent pi = PendingIntent.getActivity(context, 0, regularIntent,...);        注:  PendingIntent.getActivity()方法中的第二個參數叫做requestCode,本例中我們設置為0.如果兩個pending intent所包裝的intent一樣,這個參數就可以用來區分這兩個pending intent。這方面內容我們會在第20章具體討論pending intent和鬧鐘時具體介紹。        對於 PendingIntent.getActivity()的名字有很多奇怪之處。這裡的activity到底扮演什麼角色?為什麼我們在創建pending intent的時候不使用create這個詞,而是使用get?        為了理解第一點,我們需要在進一步了解一下普通的intent的用途。一個普通的intent可以用來喚醒一個activity,service或broadcast receiver。(後面你會學到service和broadcast receiver)用intent來喚醒不同種類的組件本質是不同的,為了實現這個目的,android context(activity的父類)提供三種不同的方法:   startActivty(intent) startService(intent) sendBroadcast(intent)        由於有這幾個變形,那麼如果我們要想存儲一個intent以後使用,android如何知道這個intent是用來喚醒activity、service還是broadcast receiver呢?這也就是為什麼我們要在創建pending intent之前先指定其用途,這樣就解釋了下面三個方法為何如此命名:   PendingIntent.getActivity(context, 0, intent, ...) PendingIntent.getService(context, 0, intent, ...) PendingIntent.getBroadcast(context, 0, intent, ...)        現在我們解釋一下為什麼用“get”。Android存儲intent並進行復用。如果你用同一個intent請求pending intent兩次,你也只能得到一個pending intent。        這樣,你看到PendingIntent.getActivity()的全部定義後就會稍微清晰一些了。   PendingIntent.getActivity(Context context, //originating context int requestCode, //1,2, 3, etc Intent intent, //original intent int flags ) //flags         如果你的目的是獲取一個不同的pending intent復本,你就要提供一個不同的requestCode。我們在第20章介紹alarm時會更詳細的介紹這方面內容。如果兩個intents中,處理extra bundle部分,其它都相同,那麼會認為是同一個intent。如果你必須要對這樣的兩個其它部分一樣的intents進行區分,那麼就提供不同的requestCode。這樣,創建的pending intent就會不同,及時其底層intent是相同的。  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved