繼續我們的Android之路吧。今天我要介紹的是Android的Intent。
對於基於組件的應用開發而言,不僅需要構造和尋找符合需求的組件,更重要的是要將組件有機的連接起來,互聯互通交換信息,才能夠最終提供應用所期望的服務。而為了能夠更好地實現組件復用,充分地利用每一個組件的能力,就需要這些組件連接的模式足夠靈活和統一,並且可以進行動態地擴展。所以Android提供了利用Intent對象建立連接並實現組件通信的模式。Intent對象是Android組件連接的核心,集中體現了整個組件連接模型的設計思想。
一、Intent對象
Android的Intent機制最核心的設計思想,就是引入了組件管理服務作為連接組件的管理者。該服務可以通過組件的配置信息了解系統中每個組件的類別和功能,從而幫助調用組件尋找符合其需求的實現組件,將調用者與實現者徹底解耦。
一般的Intent實現的流程圖:
- Intent對象的構成
Intent對象的這些作用,都是通過它的實現和設計體現出來的。從數據結構來看,Intent類的實現非常簡單,它並沒有包含復雜的邏輯功能,只是包含着若干個數據項。
(1)Action項 在Intent中用來表示動作,可以通過Intent的setAction和getAction來進行操作。在Android中定義了很多標准的動作,比如:Intent.ACTION_VIEW等。這些動作約定了 Android組件間的通信規范,保證了組件系統的可擴展性。
(2)Data項,當發起請求時,調用組件如果有明確的數據對象,通常就會用Data項來存儲表示。Data數據也是用字符串進行存儲,它的格式符合URI標准。所以可以通過URI來描述數據來增強數據的作用范圍。
(3)Type項, Type是MINE格式的字符串數據,用於描述組件能夠處理的請求類型,或者補充說明Data數據的類型,它可以通過通配符*來表示整個類別的信息。注意:Data項和Type項在很多時候是互斥使用的,當調用Intent.setType時,之前設定的Data信息就會被清空,反之亦然。但可以通過intent.setDataAndType()來同時設置。
(4)Category項 Category表示約束。每個Intent對象可包含多個categroy項。而一個組件需要支持全部的Category才可能處理請求。如一個組件需要啟動其他應用,且希望進入的是這個應用的入口組件,這時就需要添加Intent.CATEGORY_LAUNCHER作為約束。
(5)Component項 它指的是目標組件的類型信息,可以通過Intent.setComponent方法利用類名進行設置,也可以通過Intent.setClass方法利用類型對象信息進行設置。
(6)Extras項 Intent中數據傳輸的載體,負責將數據從調用組件傳遞到實現組件。
(7)Flags項 Flags是一個整數型,由一系列的標志位匯集而成。
在組件開發中,應該遵循SDK或第三方擬定的相關規范。當一個組件需要支持特定的Action時,就需要同支持相關的Extras項和Data項相關內容。協議的完整性和正確性是整個Android Intent機制的根基。 - Intent對象的解析
從實現組件的選擇來看,Intent對象可以分為兩類:精確描述的Intent和模糊描述的Intent。
(1)精確描述的Intent,指的是所有帶有Component信息的Intent對象,調用組件可以通過Intent.setComponent或Intent.setClass等方法進行設置。當組件管理服務收到Intent對象時,會先校驗其Component數據項,如果含有Component信息,組件管理服務只需要依照Component構造對應的實現組件。平時我們用的Intent的跳轉都是用的精確Intent.
(2)除此之外,所有不包含Component信息的Intent對象,都歸類於模糊描述的Intent.
二、Intent Filter對象
除了Intent對象,在Android的Intent機制中還有一個重要的角色:Intent Filter對象。Intent Filter對象,指的是android.content.IntentFilter類的對象,它是Intent對象的“姊妹對象”。與Intent對象類似,Intent Filter對象也包含Action、Type、Data、Catagory等數據項,每個數據項的結構和含義,與Intent的數據項也一一對應。
- Intent Filter的用途
Intent Filter是各個組件用於描述其功能的,通過組件的Intent Filter信息,Android的組件管理應用就可以了解和掌握各個組件所具備的能力和能夠處理的請求。當組件管理服務接收到調用組件發送來的基於模糊描述的Intent對象時,會與所有組件的Intent Filter信息進行匹配計算,尋找符合需求的實現組件。
每個組件都可以有任意數量的Intent Filter。組件包含的Intent Filter對象越多,說明他能接受Intent請求范圍越廣,同時,其實現也會越復雜。 - Intent Filter的范例
<activity android:name="com.example.activitydemo.SecondActivity"> <intent-filter android:icon="" android:label="" android:priority=""> <action android:name=""></action> <category android:name=""></category> <data android:host="" android:mimeType="" android:path="" android:pathPattern="" android:port="" android:scheme=""/> <!-- 可以繼續添加相關的actio、category和data項 --> </intent-filter><!-- 可以繼續添加相關的intent-filter項 --> </activity>
三、Intent 匹配算法
當組件管理服務接受到請求組件的Intent對象后,會先查看Intent對象是否包含了目標實現組件的Component信息。如果不包括,則會從應用管理服務中獲取所有組件的Intent Filter信息,並與Intent對象相比較,選擇符合需求的實現組件。其中涉及到的就是Intent的匹配算法了。
算法輸入的是進行比較的Intent對象和Intent Filter對象;如果返回值為正,則表示匹配成功,並且,正值越大意味着匹配程度越高,流程圖如下。
其中,Data與Type信息是Intent Filter中最復雜的數據項,其比較算法是決定Intent與Intent Filter對象匹配程度的關鍵。
如果Intent的對象包含Type信息,就必須要求Intent Filter的Type信息與之對應,否則,匹配也將以失敗告終;然后,如果Intent對象中含有Data項,則會將該Data項的URI信息拆分為Scheme和Authority等部分,逐一與Intent Filter對象中對應的部分進行比較,只要兩者有任何不符的地方,匹配都會失敗。
以Android原生的郵件應用為例,它配置了如下的Intent Filter信息:
<activity android:name=".SecondActivity.MessageCompose"> <intent-filter > <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.SENDTO" /> <data android:scheme="mailto"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <!-- 可以繼續添加相關的actio、category和data項 --> </intent-filter> <!-- 可以繼續添加相關的intent-filter項 --> </activity>
當調用者發出如下請求時,就會觸發一次意圖匹配:
Intent intent = new Intent(Intent.ACTION_SENDTO); intent.putExtra(Intent.EXTRA_TEXT, ""); //其中,mailto這部分是URI的scheme部分 intent.setData(Uri.parse("mailto:xxx@oo.com")); startActivity(intent);
開始匹配檢查。ACTION包含OK—>data包含OK—>Category檢查(通過Context.startActivity函數發出請求,組件管理服務會自動為其添加上Intent.CATEGORY_DEFAULT)OK—>匹配成功。
四、匹配組件的選擇
如果有多個Intent Filter對象與調用組件發出的Intent對象都相匹配,就需要在所有符合條件的Intent Filter對象中進行篩選,選出最符合調用組件和用戶需求的實現組件,這就是匹配組件的選擇。而我們可以設定Intent Filter對象的優先級來進行設定,即通過<intent-filter>配置項中的android:priority屬性進行變更,或者通過IntentFilter.setPriority函數進行動態地修改。范圍是-1000至1000,默認為0。
在Android的Intent機制中,通過引入第三方組件管理服務,降低了調用組件與實現組件之間的耦合,提高了整個系統的靈活性及組件的復用性,使得應用開發變得更為簡單快捷。但同時,正是由於第三方服務地介入增加了組件間連接的成本,可能會使組件間的調用不夠流暢。因此,在組件管理服務中,系統對組件的匹配和選擇過程進行了大量的優化,以提高組件調用的效率,其中包括:
(1)索引
組件管理服務通過哈希表,為所有Intent Filter對象的Action、Type等數據項建立索引。每個索引項對應着一組Action相同,Type相同或者其他數據項相同的Intent Filter對象。Intent先與索引項比較,快速地選擇出可能與Intent相匹配的Intent Filter對象。這樣,就加快了匹配速度。
(2)緩存
所謂緩存,是將Intent與Intent Filter的匹配結果記錄下來,當再碰到相同Intent的調用時,可直接返回上次記錄的結果,從而跳過匹配的過程,加速組件的調用。
五、Intent使用的一個小例子
- 一個ActivityDemo工程,用以啟動其他應用的Activity。其中的代碼為
public class ChangeActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); Button button2 = (Button) findViewById(R.id.button2); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); //因為是精確啟動,這些不加都是沒問題的 //intent.addCategory(Intent.CATEGORY_LAUNCHER); // intent.setAction(Intent.ACTION_MAIN); ComponentName cn = new ComponentName( "com.example.demo",//另一個應用的包名 "com.example.demo.MainActivity");//要啟動的另一個應用的類名 //intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setComponent(cn); Log.d("TEST", intent.toString()); startActivity(intent); //精確啟動的另一種方法 /*Intent LaunchIntent = getPackageManager() .getLaunchIntentForPackage("com.example.demo"); Log.d("TEST", LaunchIntent.toString()); startActivity(LaunchIntent);*/ } }); button2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //只能獲得系統級的應用組件 //action為MAIN,應用入口界面 Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); startActivity(intent); //獲取所有符合的組件,包括第三方安裝的應用 //List<ResolveInfo> activities = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); //自定義對話框 //ShowSelectActivityDialog(activities); } }); } }
- 另一個應用,即包名為com.example.demo的應用,其中配置文件部分為:
<activity android:name="com.example.demo.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
- 安裝以上兩個應用,運行效果圖:
點擊精確Intent,成功跳轉到另一個應用中。
點擊模糊Intent,出現組件選擇列表,如下圖:
PS:其中關於PackageManager的詳細用法,可以參考這篇文章Android隨筆—PackageManager詳解
六、結束語
看到這里,大家是不是對Intent的傳遞原理有所了解了呢。形象一點地說,Intent好比是插頭,而Intent Filter就好比是插座,插座的孔只要滿足插頭的孔腳數量就能工作了,多了沒事,但少了就不行。
Intent是Android應用模型的核心,解決了組件間的連接問題。通過組件管理服務提供的Intent匹配策略,降低了組件間的耦合度,提高了平台的靈活性,增強了組件的復用性,從根本上減輕了應用開發的負擔。
參考文章:(1) android Intent機制詳解 http://www.oschina.net/question/565065_67909
(2)android intent和intent action大全 http://www.apkbus.com/android-72361-1-1.html
========================================
作者:cpacm
地址:http://www.cpacm.net/2015/03/26/Android開發日記(八)——Android的Intent機制/