2010年10月21日 星期四

Android Intent Filter-判斷intent傳遞對象

一.Intent接收原理
當使用者發送一個intent出來,要求元件去執行動作,如果這個intent裡有很清楚的設定了ComponentName,那麼intent就會直接被送到指定的元件,並啟動該元件,如果沒有設定,則會由Android系統自動去判斷該把這intent送到哪個元件上啟動他。

大多數在本身專案內元件可以處理的動作,intent通常都會直接指明要給哪個元件處理,如果沒有指定元件名稱的intent大多是用來啟動其他Application上的元件

二.Intent Filter的作用
Android系統如何判斷哪個元件可以接收哪個intent,就是依靠在ManiFest檔案內,宣告元件(Activity,Service)時所加入的Intent Filter設定,每個Activity內可以設定0~多組的intent filter,每一組的Intent Filter都是一份比對規則

當intent發出來時,系統會去檢查Manifest內各元件內的intent filter,而啟動適合的元件,若元件沒有設定filter,那就只能接收到有清楚指定component的intent

三.Intent Filter
Intent filter內會設定的資料包括action,data與category三種。也就是說filter只會與intent裡的這三種資料做比對動作,而在每個filter內可以同時存在著多個data action與category

雖然每個Intent Filter都屬於IntentFilter類別,但因Android系統在元件啟動錢就必需測試其相容性,所以Intent Filter都是以XML方式寫在AndroidManifest.xml檔內,而不以Java Code去產生。唯一的例外是broadcast receivers的intent filter可以透過Context.registerReceiver()來動態設定

Context.registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
在註冊時,把filter動態加入

Intent Filter內要設定的值,包括action,data與category都定義在Intent Class內,也就是說在Intent物件內設定這3種資料的值與Intent Filter內要設定這3種資料的值都定義在Intent Class內,在JavaCode裡,可以透過Class引用參數,但在Manifest內設定,需要設定字串值,就要去查Class內參數實體的字串值

例:Manifest.xml設定filter
 <intent-filter . . . >  
      <category android:name="android.intent.category.DEFAULT" />  
      <category android:name="android.intent.category.BROWSABLE" />  
      . . .  
 </intent-filter>  

JavaCode設定intent Object的Category
intent.setCategory(Intent.CATEGORY_DEFAULT);

四.Intent Test 找到要接收intent的元件
當intent發出來時,會與元件在Manifest內所定義的intent filter做比對,比對時需進行3個test,當這3個test都通過時,才會確認這元件可以接手處理這Intent。若同時有不只一個的Activity或Service通過Test,那系統會問你要啟動哪一個,若都不符合,則會發生錯誤

五.Intent Test-Action Test
1.當一個intent object若沒有指定Action,則這項pass
2.filter內至少要有一項action,若都沒有就沒有任何intent
  可以通過這filter


 <intent-filter . . . >  
  <action android:name="com.example.project.SHOW_CURRENT" />  
  <action android:name="com.example.project.SHOW_RECENT" />            
      . . .  
 </intent-filter>  

六.Intent Test-Category Test
1.若intent有設定Category,必須intent object可以通過所有filter設定
  的Category,Test才會pass
2.若intent沒有設定Category,則設定pass
3.當執行startActivity()而使用沒有明確設定目標的intent,都會當作他
  至少會有一個Category: Intent.CATEGORY_DEFAULT
  (也就是"android.intent.category.DEFAULT")    

因此若Activity要能夠接收到這intent,則在Mainfest內定義這Activity的intent filter部份需要有Category為"android.intent.category.DEFAULT"的值,才會有可能接收到,也就是說在Manifest內的Activity,至少要有Intent.CATEGORY_DEFAUL(也就是"android.intent.category.DEFAULT")的值,而在已經設定為啟動的Activity內多設定Intent.CATEGORY_DEFAUL,也可以但並無用處

在filter內設定category值,需設定完整字串值,這部份查Intent Class,因為在xml tag接受的值是字串,而在Java Code內設定intent的Category就可以用Class的常數去書寫

七.Intent Test-Data Test
每個data tag都包含了URI與data type (MIME media type)
而URI會以scheme, host, port,與 path來表示
也就是可以把一個完整的URI看成
scheme://host:port/path
若host沒指定,則port可略
data type可以用*代表允許任何型態的資料
如text/*" or "audio/*"

URI為
content://com.example.project:200/folder/subfolder/etc

則設定Filter的寫法為
 <intent-filter . . . >  
      <data android:mimeType="video/mpeg" android:scheme="content" android:host="com.example.project" android:port="200" android:path="folder/subfolder/etc"/>   
      <data android:mimeType="audio/mpeg" android:scheme="http" . . . />  
      . . .  
 </intent-filter>  

data在filter與intent內data比對的原則是
1.當filter內沒有設定任何data tag,而intent內也沒有設定URI與data type,
  那這test就通過
2.當filter有設定URI,沒有設定data type,而intent object也是只有URI沒有
  data type,這時候通過,這通常只會發生在mailto: 與tel: 時
3.當filter內只有設定data type 沒有設定URI,而intent object有設定相同
  的data type無URI則通過
4.當filter內設定的data type與URI都與intent Object內設定的對應即通過
  若filter內只有設定data type而URI不設定,只有在intent Object內的
  data type有對應,而URI為content: 或file:才會通過

八.Example
1.設定元件可以顯示local端 任何Image檔案
 <data android:mimeType="image/*" />  
當要由local contentProvider顯示檔案,這樣寫即可,不需再指定URI部份用content:或file:
2.設定允許播放來自internet的任何影片檔
 <data android:scheme="http" android:type="video/*" />  
當使用者透過web page連結要開啟一個資源,會先使用html page開啟看看,若不行,則會與未指定對象
的intent一樣,去查找哪一個Activity適合去開啟這個資源,若不知道,則會開啟Download Manager去下載
3.設定啟動預設執行的Activity
 <intent-filter . . . >  
      <action android:name="code android.intent.action.MAIN" />  
      <category android:name="code android.intent.category.LAUNCHER" />  
 </intent-filter>  
不需透過任何intent去觸發,在開啟App即會自動執行這個Activity

九.Intent與Intent Filter
Intent Filter的作用,不僅是找出intent所要啟動的元件,還包括了設定元件該出現在Device的相關訊息
例如在Filter內設定了
"android.intent.action.MAIN"與"android.intent.category.LAUNCHER"
這元件會出現在device最上層的Launch清單上

若元件的filter設定了android.intent.category.HOME"
那這元件就會顯示在Home screen上

利用PackageManager Class的相關method針對指定intent找出適合的元件
透過query..() method可以讓你找到可以接收指定intent的component
透過resolve..()可以找到最適合接收指定intent的component
例如queryIntentActivities()可以取得所有適合的Activity清單