1. 程式人生 > >Pro Android學習筆記 十一 瞭解Intent 中

Pro Android學習筆記 十一 瞭解Intent 中

Intent的構成

Intent可以帶有action,data(由URI表達),extra data(key/value map,鍵值對),指定的類名(成為component name)。一個intent至少攜帶上述的一個內容。

Action。Action名,在上一筆記中已經給出兩種例子,一種是系統自帶的,如Intent.ACTION_DAIL,一種是開發者通過AndroidManifest.xml進行註冊的,在建立intent時給出:Intent intent=new Intent(String action_name);。action_name字串的名字字首是類名。

Data。由不同action決定有效的URI的格式。intent.setData(Uri.parse(”xxxxxx”

));。被喚起的activity可以同activity.getIntent()來獲得intent,然後通該intent的getData()來獲取資料。

//觸發方。  Intent i = new Intent(actionName); String uriStr = "wei://www.flowingflying.com";i.setData(Uri.parse(uriStr));this.startActivity(i);

//被觸發方。需要在AndroidManifest.xml中在intent-filter中註冊data資訊,後文詳細說明Intent intent = this.getIntent(); String data = intent.getData
();

通用的action即資料傳遞。Action和喚起的並非是一對一的,例如Intent.ACTION_VIEW根據data,喚起不同的應用。這種一對多,也就是通用的action,在manifest中註冊時,需要宣告資料(URI)的要求。具體可以參考:http://developer.android.com/guide/topics/manifest/data-element.html。ACTION_VIEW是通過schema進行區分。通過class名字、action名字等方式進行指定的稱為explicit intent,可以一對多的稱為implicit intent。

<activity......>     <intent-filter>

           <action android:name="android.intent.action.VIEW" />         <data android:scheme="http"/>          <data android:scheme="https"/>     </intent-filter> </activity>

如果我們自己的intent要傳遞data,也必須要在manifest中註冊data,否則intent會喚起失敗,報告ActivityNotFoundException的異常。

比較常用的還有MIME type,例如註冊<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />表示要檢視notes的集合(即目錄),而註冊<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />則表示檢視具體item,即檢視具體的note。

擴充套件資料(Extra data)傳遞。uri的方式限制了資訊的傳遞量。Extra的格式key/value對,key名字通常以包名開頭,而value可以是任何基礎資料型別,或者是實現android.os.Parcelable的物件。

在IntentTestDemo.java(通過intent i喚起IntentBasicViewActivity)通過下面傳遞extra資料。1、extra資料以bundle的方式在intent中存放,如果已經有bundle,將新的鍵值對加入其中,如果沒有bundle,則建立一個。未來確保key的唯一性2、key名通常以包名開頭,本例以常量方式,實際為:cn.flowingflying.android.pro.extra.string。3、下面是最最簡單據型別,還可以是array,例如putExtra(String name, int[] values);       可以是Serializable物件,如putExtra(String name, Serializable value);以及Parcelable物件,如putExtra(String name,Parcelable value); 。      可以將bundle進行傳遞,如putExtra(String name, Bundle value);,       可以將Intent進行傳遞,如putExtra(String name, Intent anotherIntent);      可以支援Array list:如putIntegerArrayListExtra(String name, ArrayList arrayList); putParcelableArrayListExtra(String name, ArrayList arrayList);和putStringArrayListExtra(String name, ArrayList arrayList)     【注意】在intent中傳遞的不是reference(指標),而是copy一份,我們修改物件,並不會影響已經copy進intent中bundle的資料,這點需要非常注意。i.putExtra(IntentBasicViewActivity.EXTRA_FLOWINGFLYING_STRING, "Hello, Intent! ");在IntentBasicViewActivity.java中通過以下程式碼獲取extra資料。Intent intent = this.getIntent(); Bundle b = intent.getExtras();  String s =  b.getString(EXTRA_FLOWINGFLYING_STRING));

使用Component來喚起Activity

之前例子,我們通過action name來喚起Activity。對於explicit intent,即明確指定呼叫哪個activity,可以通過給出該Component的的package name和class name來進行呼叫。下面我們試驗採用四種類似的方式進行呼叫,這四種方式本質沒有區別。

private void basicTest1(){     Intent intent = new Intent();    //方式1:setComponent(ComponentName name);    intent.setComponent(new ComponentName("cn.flowingflying.android.pro",                                                                                 "cn.flowingflying.android.pro.IntentBasicViewActivity"));    startActivity(intent); }

//要寫完整的class Name,不能寫IntentBasicViewActivity或者.IntentBasicViewActivity,否則報告找不到Activity的錯誤private void basicTest2(){     Intent intent = new Intent();      //方式2:setClassName(String packageName, String classNameInThatPackage);    intent.setClassName("cn.flowingflying.android.pro",                                           "cn.flowingflying.android.pro.IntentBasicViewActivity");    startActivity(intent); } //要寫完整的class Name,不能寫IntentBasicViewActivity或者.IntentBasicViewActivity,否則報告找不到Activity的錯誤private void basicTest3(){     Intent intent = new Intent();    //方式3:setClassName(Context context, String classNameInThatContext);    intent.setClassName(this,"cn.flowingflying.android.pro.IntentBasicViewActivity");    startActivity(intent); }

private void basicTest4(){     Intent intent = new Intent();     //方式4:setClass(Context context, Class classObjectInThatContext);   intent.setClass(this,IntentBasicViewActivity.class);     startActivity(intent); }

Intent的Category屬性

在AndroidManifest.xml中,我們可以設定intent的category,例如:

<activity ……>     <intent-filter>         <action android:name="android.intent.action.MAIN" />         <category android:name="android.intent.category.LAUNCHER" />     </intent-filter> </activity>

在應用啟動是,將尋找Activity標記為CATEGORY_LAUNCHER來載入。Android定義了多個Category,具體可以在http://developer.android.com/reference/android/content/Intent.html#CATEGORY_ALTERNATIVE中查閱。例如CATEGORY_HOME可以作為該應用的home screen,而CATEGORY_GADGET適合嵌入到某個activity中。

下面是兩個例子。例子1指明瞭action name,由於存在多個匹配,系統將列出來,供使用者進行選擇,如下。

例子2在例子1的基礎上增加了intent.addCategory(Intent.CATEGORY_LAUNCHER);,增加對Category名字的匹配,可以看到系統進行了進一步的過濾。:

我們可以通過PackageManager在程式碼中,可以不喚起intent就獲得匹配的activity資訊。如下

Intent intent = new Intent(Intent.ACTION_MAIN,null); intent.addCategory(Intent.CATEGORY_LAUNCHER); PackageManager pm = getPackageManager(); List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); for(ResolveInfo ri : list){      String packageName = ri.activityInfo.packageName;     String className = ri.activityInfo.name;      ... ....     //有了packageName和className,就可以進行分析,然後通過Intent i= new Intent(packaname,className); startActivity(i);喚起我們所需的acitivity。 }

然而,通過PackageManager獲得匹配的Activity的數量多於例子中系統提供給使用者選擇的activities的數量,為何?特別是沒有將本應用顯示出來。通過category篩選屬於implicit intent的呼叫方式,不屬於指定軟體包名及類名的explicit intent的精確呼叫方式,對於implicit intent呼叫需要進行宣告,如下:

<activity  android:name="……"  android:label="@string/app_name" >    <intent-filter>         <action android:name="android.intent.action.MAIN" />         <category android:name="android.intent.category.LAUNCHER" />         <category android:name="android.intent.category.DEFAULT"/>     </intent-filter> </activity>

activity的intent-filter中可以有多個category描述,其中CATEGORY_DEFAULT表示可以使用implicit intent呼叫,當我們增加此項聲明後,本應用就出現在匹配的activity列表中。同樣,對於通過action name來呼叫的,不指定包名和類名的,也屬於implicit intent,同樣需要進行CATEGORY_DEFAULT的宣告,否則會出現ActivityNotFoundException的錯誤。如果activity沒有在intent fliter中設定為CATEGORY_DEFAULT,我們可以用PackageManager獲取匹配的activities的資訊,分析後得到確切的包名和類名,通過explicit的方式喚起該activity。

此外Android說如果從launcher screen喚起時不需要DEFAULT,也就是此時acitivty只需MAIN和LAUNCHER,當然我們也可以DEFAULT設上。Android在DEFAULT上似乎有些繁雜,簡單說如果我們不希望App被其他App通過implicit呼叫,我們就不要設定DEFAULT。

在category中有一個有趣的類別:<category android:name="android.intent.category.HOME"/>,我們在MainActivity以及另外一個Activity增加該類別。

<application ...... >     <activity ...... >         <intent-filter>             <action android:name="android.intent.action.MAIN" />             <category android:name="android.intent.category.HOME"/>             <category android:name="android.intent.category.LAUNCHER" />            <category android:name="android.intent.category.DEFAULT"/>         </intent-filter>     </activity>     <activity android:name=".IntentBasicViewActivity" android:label="@string/intent_basic_test">        <intent-filter>                 <action android:name="android.intent.action.MAIN" />             <category android:name="android.intent.category.HOME"/>             <category android:name="android.intent.category.DEFAULT"/>         </intent-filter>     </activity>     ...... </application>

左圖在程式碼中通過PackageManager來檢視匹配CATEGORY_HOME的資訊;中圖通過StartActivity(intent)來喚起匹配CATEGORY_HOME的Activities時,系統給予使用者的選擇,如果在應用中按Home鍵,有同樣效果;右圖為退出應用,按Home鍵,要求進入Home UI時,系統給予使用者的選擇。