關(guān)于Android隱式啟動Activity的分析和說明 Android開發(fā)當(dāng)中我們知道,每個組件可以有一個或者多個intent filter。提到Intent filter我們就來了解下Intent filter,Intent filter有三個部分構(gòu)成,分別是action,data和category。Intent filter是在Android的主配置文件AndroidManifest.xml中注冊,主要用來指明Activity, Service, Broadcast reciver這三個組件可以響應(yīng)哪些隱式intents。
下面通過代碼分析每個部分的功能。 <intent-filter android:label="@string/Asen's blog"> <action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.EDIT" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.ALTERNATIVE" /> <data android:mimeType="video/mpeg" android:scheme="http".../> <data android:mimeType="audio/mpeg" android:content="com.example.project:200/folder/subfolder/etc"/> </intent-filter> 每個action, category, data都是一行,如果有多個就寫多行。"android.intent.action.MAIN" and "android.intent.category.LAUNCHER"這兩個是程序入口點(diǎn)的filter必須部分。比如說短信這個應(yīng)用,當(dāng)你點(diǎn)擊MMS的圖標(biāo)程序啟動后,映入使用者的一個界面(所有收到的短信列表),這個短信列表界面就是程序的入口點(diǎn),通俗的講就是一個application啟動后顯示的第一個界面。另外,"android.intent.category.DEFAULT"這個category是用來指明組件是否可以接收到隱式Intents,所以說除了程序入口點(diǎn)這個filter不用包含DEFAULT category外,其余所有intent filter都要有這個category。 data有兩部分構(gòu)成,一個是數(shù)據(jù)類型,另一個是URI。每個URI包括四個屬性參數(shù)(scheme,host, port, path),形如:scheme://host:port/path Intent filter和Intent相互配合,實(shí)現(xiàn)了Android系統(tǒng)四大組件之間的信使功能。
詳細(xì)例子說明
舉個例子 content://com.examplproject:e.200/folder/subfolder/etc 這個列子中scheme是content,host是com.examplproject,port是200,path是folder/subfolder/etc
更多例子請參考:http://hi.baidu.com/wishwingliao/blog/item/0a38ccfce06f39e8fc037f85.html 隱式啟動Activity的intent到底發(fā)給哪個activity,需要進(jìn)行三個匹配,一個是action,一個是category,一個是data,可以是全部或部分匹配 同樣適用于Service和BroadcastReceiver,下面是以Activity為例 MainActivity.java --主Activity TestActivity.java --需要隱式啟動的Activity (1) 根據(jù)Action和Category來進(jìn)行匹配 <activity android:name=".TestActivity" android:label="TestActivity"> <intent-filter > <action android:name="cc.android/myaction.leo"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> 在MainActivity.java里啟動它: intent.setAction( "cc.android/myaction.leo"); //不加下面這行也行,因?yàn)閕ntent的這個屬性默認(rèn)值即系Intent.CATEGORY_DEFAULT intent.addCategory(Intent.CATEGORY_DEFAULT); startActivity( intent ); a.在某個Activity里用startActivity()方法發(fā)送一個intent,這個intent設(shè)定了一些條件,比如用方法setAction(),addCategory()設(shè)定了兩個屬性, 發(fā)送了這個intent之后,android會去系統(tǒng)里保存的MainManifest.xml清單(假設(shè)這個系統(tǒng)存放全部apk清單的文件為MainManifest.xml)里查找符合這兩個屬性的activity,然后啟動它。 查找過程是怎樣的呢? 我猜測:在安裝某個apk的時候,android系統(tǒng)會把這個apk的清單文件里內(nèi)容復(fù)制一份至系統(tǒng)的某個清單文件里(假如這個系統(tǒng)存放全部apk清單的文件為MainManifest.xml) 當(dāng)某個Activity用startActivity(intentOther)方法向系統(tǒng)發(fā)送了一個intent(假如為intentOther),那么android系統(tǒng)會去查找這個MainManifest.xml里注冊的<intent-filter >屬性, 查找到符合這個 intentOther 的就啟動這個Activity,如果有多個這樣的Activity符合條件的話,就跳出一個對話框讓用戶選擇究竟要啟動哪一個 上面那個自定義的Action字符串("cc.android/myaction.leo",當(dāng)然也可以寫成這樣"cc.android.myaction.leo",同時AndroidManifest.xml里也要寫成這樣)是系統(tǒng)唯一的, 所以系統(tǒng)很容易就能匹配到。 b.任何一個需要隱式啟動的Activity都必須要有這項(xiàng):<category android:name="android.intent.category.DEFAULT"/> 例外情況是:android.intent.category.MAIN和android.intent.category.LAUNCHER的filter中沒有必要加入android.intent.category.DEFAULT,當(dāng)然加入也沒有問題 c.假如有兩個Activity,它們的在AndroidManifest.xml里配置如下: <activity android:name="MyActivityOne" android:label="@string/activityOne"> <intent-filter> <action android:name="hello.leo.liao" /> <action android:name="hello.leo.leo" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name=".MyActivityTwo" android:label="@string/activityTwo"> <intent-filter> <action android:name="hello.leo.liao" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> 在MainActivity.java里發(fā)送一個intent: intent.setAction( "hello.leo.liao"); //不加下面這行也行,因?yàn)閕ntent的這個屬性默認(rèn)值即系Intent.CATEGORY_DEFAULT intent.addCategory(Intent.CATEGORY_DEFAULT); startActivity( intent ); 這樣的話,android系統(tǒng)會跳出一個對話框讓你選擇啟動哪一個Activity(MyActivityOne還是MyActivityTwo) 如果把上面的intent.setAction( "hello.leo.liao");改為intent.setAction( "hello.leo.leo");的話,就自動匹配到MyActivityOne 就是說如果category和action都相同的話,會跳出一個對話框讓用戶選擇要啟動哪一個activity; 如果category相同,而action不相同,就可以匹配到相應(yīng)的activity d.單單靠添加addCategory屬性不能匹配,如: Intent intent = new Intent(); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.addCategory("android.intent.category.hello"); startActivity(intent); <activity android:name=".MyActivityTwo" android:label="@string/activityTwo"> <intent-filter> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.hello"></category> </intent-filter> </activity> e.當(dāng)匹配不上任何Activity的話,會發(fā)生異常,跳出對話框:很抱歉...某某應(yīng)用程序意外停止,請重試。 f.Service和BroadcastReceiver 同理 (2) 根據(jù)Action和Data匹配 <activity android:name=".MyActivityTwo" android:label="@string/activityTwo"> <intent-filter> <action android:name="android.intent.action.leo"></action> <category android:name="android.intent.category.DEFAULT"></category> <data android:scheme="x-id"></data> </intent-filter> </activity> //Uri uri = Uri.parse("x-id://www.google.com/getDetails?id=123");//這個也可以 //Uri uri = Uri.parse("x-id");//這個不行 //Uri uri = Uri.parse("x-id://");這個可以 Uri uri = Uri.parse("x-id:");//這個可以 Intent in = new Intent(); in.setAction("android.intent.action.leo");//去掉這行不行,單靠data不能匹配 in.addCategory(Intent.CATEGORY_DEFAULT);//可以去掉這行,因?yàn)閕ntent的默認(rèn)category值即系Intent.CATEGORY_DEFAULT in.setData(uri);//去掉這行不行 startActivity(in); 總結(jié):如果在AndroidManifest.xml里面指定了<data>這行,那么,需要匹配到它的話,在代碼里必須要設(shè)置intent的data,如上面的in.setData(uri) Data的語法: <data android:host="string" android:mimeType="string" android:path="string" android:pathPattern="string" android:pathPrefix="string" android:port="string" android:scheme="string" /> Uri的格式:scheme://host:port/path or pathPrefix or pathPattern 如果scheme沒有指定,那其它的屬性均無效; 如果host沒有指定,那么port,path,pathPrefix,pathPattern均無效; 如果在manifest里這樣寫:<data android:scheme="something" android:host="project.example.com" /> 那么Uri uri = Uri.parse("something://project.example.com"); 才可以匹配 <data android:scheme="something" android:host="project.example.com" android:port="80"/> 等同于這樣寫: <data android:scheme="something"/> <data android:host="project.example.com"/> <data android:port="80"/> 那么Uri uri = Uri.parse("something://project.example.com:80"); 才可以匹配 不知為何,下面這個不行: <data android:scheme="content" android:host="com.example.project" android:port="200" android:path="folder/subfolder/etc"/> Uri uri = Uri.parse("content://com.example.project:200/folder/subfolder/etc") 下面這樣也不行 <data android:scheme="content" android:host="com.example.project" android:port="200" android:path="folder"/> Uri uri = Uri.parse("content://com.example.project:200/folder") 可以有多個data,只需匹配其中一個即可 <activity android:name=".MyActivityTwo" android:label="@string/activityTwo"> <intent-filter> <action android:name="android.intent.action.leo"></action> <category android:name="android.intent.category.DEFAULT"></category> <data android:scheme="x-id"/> <data android:scheme="something"/> </intent-filter> </activity> Intent in = new Intent(); in.setAction("android.intent.action.leo"); in.addCategory(Intent.CATEGORY_DEFAULT); in.setData(Uri.parse("something:"));//或者用這個亦可in.setData(Uri.parse("x-id:")); startActivity(in); (3) 根據(jù)action和data的mimeType屬性匹配 <activity android:name=".MyActivityTwo" android:label="@string/activityTwo"> <intent-filter> <action android:name="android.intent.action.VIEW"></action> <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> </activity> 在java代碼里這樣寫就可以匹配到這個activity: Intent in = new Intent(); in.setAction("android.intent.action.VIEW"); in.addCategory(Intent.CATEGORY_DEFAULT);//可去掉,因?yàn)镃ategory默認(rèn)值即系Intent.CATEGORY_DEFAULT in.setType("vnd.android.cursor.dir/vnd.google.note"); startActivity(in); 單靠data的mimeType屬性不能匹配,就算這個mimeType是唯一的也不行(比如in.setType("leo.android.cursor.dir/vnd.google.leo");),需要有一個action配合 可以有多個mimeType,在java代碼里只需匹配其中一個即可: <activity android:name=".MyActivityTwo" android:label="@string/activityTwo"> <intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW"></action> <data android:mimeType="leo.android.cursor.dir/vnd.google.leo" /> <data android:mimeType="leo.android.cursor.dir/vnd.google.liao" /> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> </activity> 這樣可以啟動MyActivityTwo這個Activity: Intent in = new Intent(); in.setAction("android.intent.action.VIEW"); in.addCategory(Intent.CATEGORY_DEFAULT);//可去掉,因?yàn)镃ategory默認(rèn)值即系Intent.CATEGORY_DEFAULT in.setType("leo.android.cursor.dir/vnd.google.liao"); startActivity(in); 或者這樣也可以啟動MyActivityTwo這個Activity: Intent in = new Intent(); in.setAction("android.intent.action.VIEW"); in.addCategory(Intent.CATEGORY_DEFAULT);//可去掉,因?yàn)镃ategory默認(rèn)值即系Intent.CATEGORY_DEFAULT in.setType("leo.android.cursor.dir/vnd.google.leo"); startActivity(in); scheme和mimeType只能有其中一個,下面這樣通不過 AndroidManifest.xml里: <data android:scheme="something" android:host="project.example.com" android:port="80" android:mimeType="leo.android.cursor.dir/vnd.google.leo" /> <data android:scheme="something" android:host="project.example.com" android:port="80" /> <data android:mimeType="leo.android.cursor.dir/vnd.google.leo" /> java代碼里: 匹配不上: Intent in = new Intent(); in.setAction("android.intent.action.VIEW"); Uri uri = Uri.parse("something://project.example.com:80"); in.setData(uri); in.setType("leo.android.cursor.dir/vnd.google.leo"); startActivity(in); 這樣還是匹配不上: Intent in = new Intent(); in.setAction("android.intent.action.VIEW"); // Uri uri = Uri.parse("something://project.example.com:80"); // in.setData(uri); in.setType("leo.android.cursor.dir/vnd.google.leo"); startActivity(in); 這樣還是匹配不上: Intent in = new Intent(); in.setAction("android.intent.action.VIEW"); Uri uri = Uri.parse("something://project.example.com:80"); in.setData(uri); // in.setType("leo.android.cursor.dir/vnd.google.leo"); startActivity(in); (4) 一個Activity里可以有多對<intent-filter></intent-filter> 只要匹配其中一對,即可啟動這個Activity <activity android:name=".MyActivityTwo" android:label="@string/activityTwo"> <intent-filter> <action android:name="android.intent.action.VIEW"></action> <data android:scheme="something" android:host="project.example.com" android:port="80" /> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW"></action> <data android:mimeType="leo.android.cursor.dir/vnd.google.leo" /> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> <intent-filter> <action android:name="hello.hi.liao"></action> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> </activity> java代碼里: 匹配第一對<intent-filter> 可以啟動MyActivityTwo這個Activity: Intent in = new Intent(); in.setAction("android.intent.action.VIEW"); in.addCategory(Intent.CATEGORY_DEFAULT);//可去掉,因?yàn)镃ategory默認(rèn)值即系Intent.CATEGORY_DEFAULT Uri uri = Uri.parse("something://project.example.com:80"); in.setData(uri); startActivity(in); 匹配第二對<intent-filter> 也可以啟動MyActivityTwo這個Activity: Intent in = new Intent(); in.addCategory(Intent.CATEGORY_DEFAULT);//可去掉,因?yàn)镃ategory默認(rèn)值即系Intent.CATEGORY_DEFAULT in.setAction("android.intent.action.VIEW"); in.setType("leo.android.cursor.dir/vnd.google.leo"); startActivity(in); 匹配第三對<intent-filter> 也可以啟動MyActivityTwo這個Activity: Intent in = new Intent(); in.setAction("hello.hi.liao"); in.addCategory(Intent.CATEGORY_DEFAULT);//可去掉,因?yàn)镃ategory默認(rèn)值即系Intent.CATEGORY_DEFAULT startActivity(in); 全部總結(jié): 1. <action/>包含在 <intent-filter></intent-filter> 標(biāo)簽對里,而且是必不可少的!不管以哪一種方式來匹配,都不可缺少這個<action/> ,可以有多個,至少要有一個。 如有多個的,話只需要匹配其中一個即可找到這個activity <action>里的屬性值大多數(shù)是在Intent里定義的,比如<action android:name="android.intent.action.VIEW"/>里的屬性值就等于 Intent.ACTION_VIEW, 在這個Intent類里以ACTION開頭定義的常量都是。當(dāng)然,也可以自定義。 2. 任何一個需要隱式啟動的Activity都必須要有這項(xiàng):<category android:name="android.intent.category.DEFAULT"/> 例外情況是:android.intent.category.MAIN和android.intent.category.LAUNCHER的filter中沒有必要加入android.intent.category.DEFAULT,當(dāng)然加入也沒有問題 <category>里的屬性值大多數(shù)是在Intent里定義的,比如 <category android:name="android.intent.category.DEFAULT"/>里的屬性值就等于 Intent.CATEGORY_DEFAULT, 在這個Intent類里以CATEGORY開頭定義的常量都是。當(dāng)然,也可以自定義。 3.一個Activity里可以有多對<intent-filter></intent-filter> 只要匹配其中一對,即可啟動這個Activity 4.在<intent-filter></intent-filter>里可以有多個<data android:mimeType="xxxx"/>,只需匹配其中一個即可.注意:不可以同時出現(xiàn)第5點(diǎn)的標(biāo)簽對,即下面這條。 5.在<intent-filter></intent-filter>里可以有多個<data android:scheme="xxxx" android:host="yyyy" android:port="uuu"/>,只需匹配其中一個即可。 <data android:host="string" android:mimeType="string" android:path="string" android:pathPattern="string" android:pathPrefix="string" android:port="string" android:scheme="string" /> 可以分開寫,如: <data android:scheme="something" android:host="project.example.com" android:port="80"/> 等同于這樣寫: <data android:scheme="something"/> <data android:host="project.example.com"/> <data android:port="80"/> 在java代碼里,Uri的格式:scheme://host:port/path or pathPrefix or pathPattern 注意:不可以同時出現(xiàn)第4點(diǎn)的標(biāo)簽對,即上面那條。 6.在<intent-filter></intent-filter>里可以有多個<action android:name="xxxx"> ,只需匹配其中一個即可。 7.當(dāng)匹配不上任何Activity的話,會發(fā)生異常,跳出對話框:很抱歉...某某應(yīng)用程序意外停止,請重試。 8.上面所說的全部適用于Service和BroadcastReceiver,只需把<activity ...></activity>換成<service ...></service>或<receiver ...></receiver>即可。 9.剛參考了一下packages\apps\HTMLViewer\AndroidManifest.xml ,第4和第5條應(yīng)該是不沖突才對,但是實(shí)際測試中卻是沖突,暫時未到找原因。匹配方式請看:用于打開HTML文件的intent 在被啟動的Activity(本例為MyActivityTwo)里接收數(shù)據(jù): Intent intent = getIntent(); String intentCategories = intent.getCategories() String intentType = intent.getType(); Uri uri = intent.getData(); String uriScheme = uri.getScheme(); String uriPath = uri.getPath(); String uriHost = uri.getHost(); String uriEncodedPath = uri.getEncodedPath(); 請參考:packages\apps\HTMLViewer\src\com\android\htmlviewer\HTMLViewerActivity.java
|