標籤:
Android應用程式框架鼓勵開發人員在開發應用時重用組件,本文將闡述如何用組件構建應用程式以及如何用intent將組件聯絡起來。
如需閱讀官方原文,請您點擊這個連結:
《App Components》。
您還可以參考這些博文:
以及這些Training:
Intent 與 Intent Filters(Intents and Intent Filters)
Intent是一個傳遞訊息的對象,您可以為Intent指定action來啟動其他應用組件,Intent使組件之間通訊更加便利,並且通訊方式有很多,這裡列舉了主要的三點:
啟動Activity:
您可以將intent作為參數調用startActivity()方法啟動一個activity。該intent描述了將要啟動的目標activity的特性並攜帶必要的資料資訊。您還可以調用startActivityForResult()方法回傳資訊。
啟動Service:
Service用於在後台執行任務,不與使用者互動。您可以使用startService()方法執行一次性的操作(比如後在台下載一個檔案),這需要Intent參數。
如果組件之間需要向CS結構一樣通訊,您可以把Service想成一端,並調用bindService(),這同樣需要Intent參數。
傳遞一個broadcast:
broadcast是一種可以被任何應用程式截獲的廣播機制。系統會基於當前發生的事件發出各式broadcast(比如裝置開機時、開始充電時等),您可以調用sendBroadcast(),、sendOrderedBroadcast()、 sendStickyBroadcast()方法發送一條廣播,這需要傳入Intent參數。
Intent的種類(Intent Types)
當您隱式地啟動一個service或activity時,Intent會根據其中的內容,匹配其他組件中manifest檔案的Intent-filter,啟動合格組件,並把Intent中的參數傳過去,如果有多個intent-filter滿足條件,那麼系統會彈出一個對話方塊,由使用者決定啟動哪個組件。下面是intent與intent-filters配合啟動組件的:
見:
1、首先Activity A利用傳入的Intent調用startActivity();
2、系統會根據該Intent的條件搜尋Android系統中所有匹配的組件;
3、若找到了匹配intent的intent-filters所屬的組件(Activity B),則啟動該組件,並回調onCreate()方法,同時將Intent傳遞過去。
intent-filters是manifest檔案中組件內部的一個標籤,該標籤描述了組件具備什麼特性,如果您未配置intent-filters,那個該組件只能被顯式啟動。
建立Intent對象(Building an Intent)
Intent中包含了目標組件需滿足的特性。Intent中應包含以下資訊:
Component name:目標組件的名字。對於顯式啟動,這是不可預設的,您可以使用Intent的構造方法傳入組件名稱,也可以調用setComponent(), setClass(), setClassName()這些方法傳入組件名;若是隱式啟動,這是可選的,但intent應包含其他資訊(action、category、data);
Action:是一個可以指明目標組件行為的字串。action很大程度上決定了category和data中應傳入的資訊;您也可以在自己的應用程式組件中指定action,以便讓其他應用程式啟動自己的組件。對應action中字串,不建議使用硬式編碼形式,而應在所屬組件的類中設定為常量。
常見的action有:
- ACTION_VIEW:用ACTION _VIEW啟動的activity一般可以向使用者展示一些資訊,比如啟動一個相簿APP中展示圖片的activity,或是啟動一個地圖APP中展示地址資訊的activity。
- ACTION_SEND:一般需要向 通過ACTION _SEND啟動的activity 附帶著發送一些資訊,這些資訊由由目標activity決定該發送給誰,比如社交類APP或是發送郵件的APP。
您可以將action作為參數傳入Intent的構造方法或setAction()方法中。
如需定義在自己的組件中定義action,應以應用的包名作為首碼,比如:
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Data:一個URI對象是一個引用的data的表現形式,或是data的MIME類型;data的類型由Intent的action決定,比如說若action是ACTION_EDIT,那麼data的URI應指向一個可編輯的檔案。當建立一個Intent時,除了為data指定URI以外,還應該指定data的MIME類型,比如說,一個用於展示圖片的activity是不能用來放音樂的,如果您要啟動這個activity,就需要將data的MIME類型指定為”image/png”、”image/jpeg”等。有些時候,從data的URI中就能推斷出MIME的類型,比如當一個URI的schema是”content://”時,表明該URI指向了裝置內部的一個檔案並由ContentProvider管理著,系統可以根據該檔案推斷出data的MIME類型。
您可以調用setData()方法設定URI,調用setType()方法設定MIME類型,或調用setDataAndType()方法同時設定URI和MIME類型。
!請注意:如果您需要同時設定URI和MIME類型,只能調用setDataAndType()方法,而不能分別調用setData()和setType(),因為調用setData()時會首先將setType()中的內容置空,反之亦然( they each nullify the value of the other)
Category:是一個字串,表示目標組件的附加資訊,大部分intent不需要category。以下是依稀而常用的category:
- CATEGORY_BROWSABLE:表示目標activity可以被網頁上的某個連結啟動,片activity或e-mail資訊activity。
- CATEGORY_LAUNCHER:目標activity是任務棧的第一個activity,也就是應用程式的啟示activity。
您可以將category參數傳入addCategory()方法中。
上述的參數(component name, action, data, and category)代表了intent的屬性,通過這些參數,系統可以篩選出合格目標組件。除此之外,intent還可以包含下列參數,與上面的參數不同的是,系統不會使用這些參數來篩選目標組件:
- Extras:一些intent可以攜帶的附加資訊,以索引值對的形式儲存。可以使用putExtra()方法將索引值對資訊傳入,也可以將索引值對資訊放在Bundle對象中,再通過將Bundle對象傳入putExtra()中。
Intent類中封裝了許多 ” EXTRA_* “形式的標準extra,如果想封裝自己的extra鍵,請您以應用程式的報名作為首碼,比如:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
- Flags:該參數可以為intent添加中繼資料(meta-data),flag可以指導系統以何種方式啟動一個activity、是否將啟動的activity放在該應用的任務棧中,等等。
隱式Intent的例子(Example implicit intent)
!請注意:若系統中沒有滿足隱式Intent的目標組件,則應用將崩潰(crash),所以首先應判斷,在調用startActivity()。
以下是一個通過隱式intent啟動一個“發送資訊的Activity”的例子:
// Create the text message with a stringIntent sendIntent = new Intent();sendIntent.setAction(Intent.ACTION_SEND);sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);sendIntent.setType("text/plain");// Verify that the intent will resolve to an activityif (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(sendIntent);}
如果有一個目標組件滿足intent,則啟動該組件;若有多個滿足intent的目標組件,則系統彈出一個列表以供選擇。
使用應用選取器(Forcing an app chooser)
正如向上面說,系統中可能存在多個目標組件滿足隱式intent,這時會彈出一個列表供使用者選擇,有些時候,使用者希望每次都啟動一個相同的組件(比如使用者每次都想啟動chrome瀏覽器而不是系統內建的瀏覽器),這時只需要勾選“不再詢問”選項就行了,下次再啟動時,列表將不再彈出;還有些時候,使用者每次都需要對列表中的Activity進行篩選,比如啟動用於分享的Activity,使用者希望每次分享到不同的平台,這時需要調用Intent.createChooser()方法以保證每次都彈出挑選清單,如下所示:
Intent sendIntent = new Intent(Intent.ACTION_SEND);...// Always use string resources for UI text.// This says something like "Share this photo with"String title = getResources().getString(R.string.chooser_title);// Create intent to show the chooser dialogIntent chooser = Intent.createChooser(sendIntent, title);// Verify the original intent will resolve to at least one activityif (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(chooser);}
接收隱式Intent(Receiving an Implicit Intent)
通過在manifest檔案中配置intent-filter標籤中的action, data, and category,可以設定篩選資訊,只有同時符合上述三個標籤設定的篩選資訊,Intent才能開啟您的應用程式組件:
action標籤:
可匹配Intent中的action參數。
data標籤:
可匹配Intent中的data參數(URI地址以及MIME 類型)。
category標籤:
可匹配Intent中的category 參數。
!請注意:如組件需要被隱式啟動,必須配置CATEGORY_DEFAULT
如想隱式啟動一個分享的Activity,則目標Activity如下配置:
<activity android:name="ShareActivity"> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter></activity>
一個intent-filter中可以包含多個 action, data, category 標籤。
若組件僅希望通過本應用啟動,可將組件中的exported屬性設為false。
!請注意:為了避免隱式intent匹配上了您的Service組件,請不要在service中配置intent-filter(Service必須顯式啟動)
intent-filter舉例(Example filters)
下面是一個社交APP的manifest檔案樣本:
<activity android:name="MainActivity"> <!-- This activity is the main entry, should appear in app launcher --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter></activity><activity android:name="ShareActivity"> <!-- This activity handles "SEND" actions with text data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND_MULTIPLE"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.google.panorama360+jpg"/> <data android:mimeType="image/*"/> <data android:mimeType="video/*"/> </intent-filter></activity>
action為 “android.intent.action.MAIN”表示該Activity是應用的主入口,且無需配置data。
category為 “android.intent.category.LAUNCHER”表示該activity的啟動表徵圖(通過icon屬性配置)應添加到系統的launcher中,若未配置icon,則會使用application標籤下的icon。
以上兩個屬性應成對出現。
如需隱式啟動ShareActivity,僅需匹配一個intent-filter就行了。
使用Pending Intent(Using a Pending Intent)
PendingIntent是一個封裝Intent的類,主要用於實現Intent的延時啟動,PendingIntent的主要使用場合:
- 封裝一個Notification的啟動Intent;
- 封裝一個App Widget的Intent(按Home鍵啟動的activity);
- 封裝一個延時啟動的activity(如AlarmManager)。
使用PendingIntent啟動的activity無需使用startActivity()就能啟動,您應當使用對應組件的方法啟動相應組件:
- 通過PendingIntent.getActivity()啟動一個activity;
- 通過PendingIntent.getService()啟動一個Service;
- 通過PendingIntent.getBroadcast()啟動一個BroadcastReceiver;
解析Intent(Intent Resolution)
目標組件通過以下三點匹配相應的Intent:
- The intent action;
- The intent data (both URI and data type);
- The intent category。
匹配Action(Action test)
intent filter可定義零到多個action標籤:
<intent-filter> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.VIEW" /> ...</intent-filter>
intent需要匹配上其中一個action標籤。如果intent-filter中沒有action標籤,則intent無需action就能匹配。
匹配Category(Category test)
intent filter可定義零到多個category標籤:
<intent-filter> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> ...</intent-filter>
intent中的定義的每一個category都需要匹配上intent-filter中的category標籤,反之不成立(intent-filter中的category標籤可能比intent中的定義的category多)。所以無論intent-filter中是否定義了category標籤,未添加category的intent總能匹配上該intent-filter。
!請注意:通過startActivity()或startActivityForResult()方法隱式啟動的intent中,將自動被添加一個CATEGORY_DEFAULT的category,所以若您希望自己的activity能夠被隱式啟動,則需要在intent-filter中添加一個android.intent.category.DEFAULT的category標籤。
匹配Data(Data test)
intent filter可定義零到多個data標籤:
<intent-filter> <data android:mimeType="video/mpeg" android:scheme="http" ... /> <data android:mimeType="audio/mpeg" android:scheme="http" ... /> ...</intent-filter>
每個data標籤都能設定mimeType和URI 結構,其中URI可分成四部分:scheme, host, port 和 path;其結構如下:
<scheme>://<host>:<port>/<path>
比如:
content://com.example.project:200/folder/subfolder/etc
其中
- scheme為content;
- host為com.example.project;
- port為200;
- path為folder/subfolder/etc。
每一部分在data標籤中都不是必須定義的,但存在一個線性依賴:
- 若scheme 未指定,則host被忽略;
- 若host未指定,則port被忽略;
- 若scheme和host均未指定,則path被忽略;
在intent中添加的data只需要匹配一部分intent-filter中的data(URI匹配):
- 若filter只定義了scheme,則intent的data定義的URI中只要包含了相同的scheme,就能匹配;
- 若filter只定義了scheme和host,則intent的data定義的URI中只要包含了相同的scheme和host,就能匹配;
- 若filter只定義了scheme、host和port,則intent的data定義的URI中只要包含了相同的scheme、host和port,就能匹配;
intent-filter匹配intent的data中URI和mimeType類型的規則如下:
- 如果intent-filter中未指定data,則未添加data的intent可以匹配;
- 如果intent-filter中指定了URI,但未指定mimeType,則按照上一段的規則匹配(intent中也應未指定mimeType);
- 如果intent-filter中指定了mimeType,而未指定URI,則可以匹配intent中指定了相同mimeType,而未指定URI的組件;
- 如果intent-filter中同時指定了mimeType和URI,則:
- intent中添加的mimeType只要能匹配上intent-filter中的某一個mimeType,就能匹配上mimeType部分;
- intent中添加的URI則按照上一段中的URI匹配規則,就能該匹配上URI部分;特別地:若intent中的URI的scheme 指定為content: 或者 file:,那麼即便intent-filter中未定義URI,也能匹配成功。換句話說:包含content: 或者 file: 的URI總是能匹配上只定義了mimeType的intent-filter
Android官方文檔之App Components(Intents and Intent Filters)