Android組件間通信--深入理解Intent與IntentFilter
Intent(意圖)在Android中是一個(gè)十分重要的組件,它是連接不同應(yīng)用的橋梁和紐帶,也是讓組件級(jí)復(fù)用(Activity和 Service)成為可能的一個(gè)重要原因。Intent的使用分為二個(gè)方面一個(gè)是發(fā)出Intent,另一個(gè)則是接收Intent用官方的說(shuō)法就是Intent Resolving。本主將對(duì)Intent和IntentFilter進(jìn)行一些介紹。
Intent和IntentFilter是Android和一種消息通信機(jī)制,可以讓系統(tǒng)的組件之間進(jìn)行通信。信息的載體就是Intent,它可以是一個(gè)要完成的動(dòng)作請(qǐng)求,也可以一般性的消息廣播,它可以由任意一個(gè)組件發(fā)出。消息,也就是Intent,最終也是要被組件來(lái)進(jìn)行處理和消化。消息由組件發(fā)出,通常在消息的里面也會(huì)有會(huì)標(biāo)記有目標(biāo)組件的相關(guān)信息,另外目標(biāo)組件也需要告訴系統(tǒng),哪些消息是它所感興趣的,它需要設(shè)置一些過(guò)濾器,以過(guò)濾掉無(wú)關(guān)的消息。
其實(shí)這里就好比學(xué)校里的廣播,廣播有時(shí)會(huì)播放通知,但有時(shí)也會(huì)播放要執(zhí)行的動(dòng)作,比如打掃衛(wèi)生。消息中通常都會(huì)說(shuō)明消息的目標(biāo)對(duì)象,可能是計(jì)算機(jī)學(xué)院,可能是老師,也可能是英語(yǔ)系的人才需要關(guān)注。而每個(gè)人,或是學(xué)院組織,也只關(guān)心與他們有關(guān)的消息,當(dāng)然這里就要他們自己去判斷哪些是與他們有關(guān)的消息了。在Android當(dāng)中消息就是Intent,過(guò)濾器就是IntentFilter。發(fā)出消息的組件可以在消息中設(shè)置目標(biāo)組件的相關(guān)信息,目標(biāo)組件也可以設(shè)置過(guò)濾器,然后系統(tǒng)會(huì)進(jìn)行過(guò)濾,只把組件所感興趣的消息,傳遞給組件。這里假設(shè)讀者已經(jīng)了解Intent和IntentFilter的基本使用方法,且并不會(huì)進(jìn)行全面的介紹,如果不了解,可以先讀讀官方文檔,這里重點(diǎn)講講IntentFilter在使用時(shí)的一些注意事項(xiàng)。
Intent消息機(jī)制通常有二種,一個(gè)是顯式Intent(Explicit Intent),另一個(gè)是隱式Intent(Implicit Intent)。
•顯式Intent--需要在Intent中明確指定目標(biāo)組件,也就是在Intent中明確寫(xiě)明目標(biāo)組件的名稱(chēng)(Component name),需要指定完整的包名和類(lèi)名。因?yàn)閷?duì)于本程序以外的其他應(yīng)用程序,你很難知道它的組件名字,所以顯式的Intent通常用于應(yīng)用程序內(nèi)部通信,更確切的說(shuō),顯示Intent是用于應(yīng)用程序內(nèi)部啟動(dòng)組件,通常又是Activity或Service。還沒(méi)有見(jiàn)用顯式Intent來(lái)給BroadcastReceiver發(fā)送廣播的。
•隱式Intent--也就是不在Intent中指定目標(biāo)組件,在Intent中不能含有目標(biāo)的名字。系統(tǒng)是根據(jù)其他的信息,比如Data,Type和Category去尋找目標(biāo)組件。
隱式Intent通常用于與應(yīng)用程序外部的組件進(jìn)行通信。應(yīng)用程序級(jí)別的組件復(fù)用也主要是靠隱式Intent來(lái)完成的。而IntentFilter也是只有隱式Intent才用的著,顯式Intent都是直接把Intent傳遞給目標(biāo)組件,根本不會(huì)理會(huì)組件的IntentFilter。
顯式Intent(Explicit Intent)
顯示Intent使用起來(lái)比較簡(jiǎn)單,只需要在Intent中指定目標(biāo)組件的名字即可,也就是通過(guò)Intent的方法設(shè)置Component屬性。如前所述,顯式Intent通常用于應(yīng)用程序內(nèi)部啟動(dòng)Activity或Service。事實(shí)上,并不完全局限在應(yīng)用程序內(nèi)部,對(duì)于外部應(yīng)用的Activity和Service,也可以用顯式Intent來(lái)啟動(dòng),但你必須知道它們的名字。
設(shè)置組件的名字的方法有:
public Intent setComponent(ComponentName component);
public Intent setClass(Context packageContext, Class<?> cls);
public Intent setClassName (Context packageContext, String className);
public Intent setClassName (String packageName, String className);
事實(shí)上雖然設(shè)置的方法有這么多,但I(xiàn)ntent內(nèi)部標(biāo)識(shí)目標(biāo)組件的屬性只有一個(gè)Component,所以這么設(shè)置方法的目的也只是設(shè)置目標(biāo)組件的Component,事實(shí)上有這么多的方法原因在于ComponentName的構(gòu)造是多重載了的。在解析Intent時(shí),系統(tǒng)也是取得這個(gè)Component屬性,然后去啟動(dòng)它。
ComponentName Intent.getComponent();
對(duì)于應(yīng)用程序內(nèi)部啟動(dòng)Activity通常是這樣子設(shè)置Intent:
Intent i = new Intent();
// Select one of them
i.setComponent(new ComponentName(getApplication(), ViewStubDemoActivity.class));
i.setComponent(new ComponentName(getApplication(), ViewStubDemoActivity.class.getName()));
i.setComponent(new ComponentName(getApplication().getPackageName(), ViewStubDemoActivity.class.getName()));
i.setClass(getApplication(), ViewStubDemoActivity.class);
i.setClassName(getApplication(), ViewStubDemoActivity.class.getName());
i.setClassName(getApplication().getPackageName(), ViewStubDemoActivity.class.getName());
startActivity(i);
因?yàn)閼?yīng)用程序內(nèi)部的組件類(lèi),都是可以訪問(wèn)到的,所以要盡可能少寫(xiě)字串常量,以減少拼寫(xiě)錯(cuò)誤,如果一定要使用包名和類(lèi)名,也要注意,類(lèi)名必須是全稱(chēng),也就是從包名開(kāi)始,如“com.hilton.networks.WifiManagerActivity"。
但是對(duì)于外部應(yīng)用程序的Activity,通常只能通過(guò)以下方法:
Intent i = new Intent();
// select one of them
i.setComponent(new ComponentName("com.hilton.networks", "com.hilton.networks.WifiManagerActivity"));
i.setClassName("com.hilton.networks", "com.hilton.networks.WifiManagerActivity");
startActivity(i);
首先,帶有Context為參數(shù)的是不能夠用的,因?yàn)橥ǔD銦o(wú)法拿到其他應(yīng)用程序的Context,你只能拿到你所在應(yīng)用程序的Context,所以用你所在的應(yīng)用程序的Context去啟動(dòng)外部的Activity肯定會(huì)報(bào)錯(cuò)的。其次,不參再像上面那樣通過(guò)Class.getName()去指定類(lèi)名,你為你無(wú)法導(dǎo)入外部的類(lèi),會(huì)有編譯錯(cuò)誤的。另外,千萬(wàn)要注意不要拼錯(cuò),否則會(huì)有RuntimeException拋出的。
對(duì)于Service組件,也是一樣,Intent的寫(xiě)法與Activity組件一致,但是對(duì)于BroadcastReceiver組件通常都用顯式Intent。
隱式Intent的消息過(guò)濾器--IntentFilter
IntentFilter是用來(lái)解析隱式Intent(Implicit Intent)的,也就是說(shuō)告訴系統(tǒng)你的組件(Activity, Service, BroadcastReceiver)能夠處理哪些隱式的Intent。在使用的時(shí)候我們通常是這樣子的:
<manifest ...>
<receiver ...>
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
<action android:name="android.intent.action.MEDIA_SHARED"/>
<action android:name="android.intent.action.MEDIA_REMOVED"/>
<action android:name="android.intent.action.MEDIA_EJECT"/>
<data android:scheme="file" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
<data android:scheme="package" />
</intent-filter>
</receiver>
</manifest>
在Manifest中使用IntentFilter時(shí)要注意以下三點(diǎn):
1. 千萬(wàn)注意拼寫(xiě)錯(cuò)誤
這里有一個(gè)需要十分小心和注意的地方那就是對(duì)于IntentFilter里面的Action和Data字串常量不要寫(xiě)錯(cuò),因?yàn)檫@個(gè)在編譯時(shí)是不會(huì)被檢查,在運(yùn)行時(shí)又不會(huì)拋出異常,如果你拼寫(xiě)錯(cuò)了,比如大小寫(xiě)拼錯(cuò)了,在編譯時(shí)和運(yùn)行時(shí)都不會(huì)有錯(cuò)誤,但是你的程序卻不能正常工作,你的程序無(wú)法收到相應(yīng)的Intent。曾有一個(gè)同事在IntentFilter中寫(xiě)了一些Action,但把其中一個(gè)的大小寫(xiě)拼錯(cuò)了,結(jié)果花了他一個(gè)下午的時(shí)間來(lái)調(diào)試,最后還是另外一個(gè)同事到他那聊天才發(fā)現(xiàn)了是大小寫(xiě)的拼寫(xiě)錯(cuò)誤。
這里也可以發(fā)現(xiàn)Android在Manifest文件中的IntentFilter這塊的封裝性很差。如果,僅僅是如果,這些Action常量也可以通過(guò)引用的方式來(lái)寫(xiě),就可以在編譯時(shí)做些檢查和匹配,可以大大的減少出錯(cuò)的機(jī)率,同時(shí)也加強(qiáng)了封裝和信息隱藏。比如,對(duì)于上面的可以寫(xiě)成這樣:
<manifest ...>
<receiver ...>
<intent-filter>
<action android:name="@android:action/AppWidgetManager.APPWIDGET_UPDATE" />
<action android:name="@android:action/AppWidgetManager.APPWIDGET_ENABLED" />
<action android:name="@android:action/AppWidgetManager.APPWIDGET_DISABLED" />
<action android:name="@android:action/AppWidgetManager.APPWIDGET_DELETED" />
</intent-filter>
</receiver>
</manifest>
雖然這種拼寫(xiě)錯(cuò)誤很低級(jí),但是因?yàn)樗图?jí)所以當(dāng)程序不能正常工作時(shí)沒(méi)有人會(huì)想到是因?yàn)槠磳?xiě)錯(cuò)誤,所以這種拼寫(xiě)錯(cuò)誤通常會(huì)耗費(fèi)不少的調(diào)試時(shí)間。另外一種避免此種錯(cuò)誤的方法就是在代碼中通過(guò)Context.registerReceiver(BroadcastReceiver,IntentFilter)來(lái)注冊(cè)BroadcastReceiver,就可以直接寫(xiě)入常量,而非具體字串。但這只能是接收Broadcast的時(shí)候,對(duì)于那些想作為公開(kāi)接口的組件,還是需要在Manifest里面聲明,比如Email,它要能處理Intent.ACTION_SEND_TO,就需要在Manifest中聲明。
2. 要注意Data字段除了上面討論的之外,對(duì)于IntentFilter還有另外的一點(diǎn)需要注意,就是對(duì)于某些Action是需要加上Data字段信息,否則有可能接收不到。比如:
<manifest ...>
<receiver ...>
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
<action android:name="android.intent.action.MEDIA_SHARED"/>
<action android:name="android.intent.action.MEDIA_REMOVED"/>
<action android:name="android.intent.action.MEDIA_EJECT"/>
<data android:scheme="file" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
<data android:scheme="package" />
</intent-filter>
</receiver>
</manifest>
對(duì)于手機(jī)外部存儲(chǔ)卡的狀態(tài)變化的Broadcast,在注冊(cè)監(jiān)聽(tīng)器的時(shí)候就需要加上DataScheme,否則就會(huì)接收不到。這個(gè)也花費(fèi)了我?guī)讉€(gè)小時(shí)的調(diào)試時(shí)間,改在代碼中用Context.registerReceiver(BroadcastReceiver,IntentFilter)注冊(cè)也不行,最后參考了Music中的做法,加上了DataScheme才能在onReceive()中接收到Intent。同樣對(duì)于后面的Package相關(guān)的Broadcast,也是要加上DataScheme否則也是接收不到Broadcast??杀氖菍?duì)于像這樣的系統(tǒng)公共的Broadcast
Intent,在Intent的文檔中并沒(méi)有說(shuō)明如何使用,如果沒(méi)有參考事例,相信需要一定的時(shí)間才能夠找出為什么接收不到Intent。
除了DataScheme還有一個(gè)是MimeType,這個(gè)對(duì)于系統(tǒng)公共接口是必須加上的,比如Email要處理Intent.ACTION_SENTTO,就需要這樣聲明:
<manifest ...>
<activity android:name="EmailComposer">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<data android:mimeType="image/*" />
</intent-filter>
</activity>
</manifest>
3. 同時(shí)也要注意Category字段
如果沒(méi)有對(duì)IntentFilter寫(xiě)正確的Category字段,也是收不到Intent。比如:
<manifest ...>
<receiver ...>
<intent-filter>
<action android:name="com.hilton.controlpanel.action.BUTTON_ACTION" />
<category android:name="com.hilton.controlpanel.category.SELF_MAINTAIN" />
</intent-filter>
</receiver>
</manifest>
如果把Category去掉,死活也接收不到Intent,當(dāng)然這要取決于Intent是如何發(fā)出的,如果Intent發(fā)出時(shí)沒(méi)有加Category,那就沒(méi)有必須在IntentFilter加上Category。
總之,對(duì)于Intent,要保證發(fā)出和接收完全一致,否則系統(tǒng)就無(wú)法找到相應(yīng)的匹配,程序也就無(wú)法接收Intent。
有關(guān)于 DEFAULT category,也要注意,如果是針對(duì)Activity的Implicit Intent隱式Intent,如果在沒(méi)有其他Category的情況下,一定要加上DEFAULT Category。因?yàn)橄到y(tǒng)會(huì)在Context.startActivity(Intent)和Context.startActivityForResult(
Intent)時(shí)給Intent加上DEFAULT category。而對(duì)于Context.sendBroadcast(Intent),Context.sendOrderedBroadcast(Intent),Contxt.sendStickyBroadcast(Intent)和Context.bindService(Intent)Context.startService(Intent)就不會(huì)加DEFAULT Category。
另外要注意,盡量把Action進(jìn)行合并寫(xiě)進(jìn)一個(gè)IntentFilter中。因?yàn)閷?duì)于每個(gè)IntentFilter標(biāo)簽都會(huì)創(chuàng)建一個(gè)IntentFilter對(duì)象,所以如果寫(xiě)幾個(gè)就會(huì)有幾個(gè)對(duì)象在那,不但耗費(fèi)資源而且在匹配的時(shí)候也會(huì)耗費(fèi)更多的時(shí)間,因?yàn)樵诓樵?xún)匹配的時(shí)候是要一個(gè)IntentFilter對(duì)象接著一個(gè)IntentFilter對(duì)象進(jìn)行檢查的。直到找到最佳匹配或是到所有的IntentFilter都檢查完為止。
IntentFilter的匹配規(guī)則
1. 通過(guò)Action字段來(lái)匹配這個(gè)是Intent中比較基本的一個(gè)字段,也比較簡(jiǎn)單,就是一個(gè)字串,如果相等就匹配成功,否則證明還沒(méi)找到目標(biāo)。但要注意,如果IntentFilter沒(méi)有指定Action,那么它不會(huì)匹配到任何的隱式Intent,它只能被顯式的Intent匹配上。反過(guò)來(lái),如果Intent自己沒(méi)有指定Action,那么它能匹配上含有任何Action的IntentFilter,但不能匹配上沒(méi)有指定Action的IntentFilter。對(duì)于Action,平時(shí)要注意拼寫(xiě)錯(cuò)誤,因?yàn)樵贏ndroidManifest文件中聲明Action都是字串,并且在編譯時(shí)不會(huì)做檢查,運(yùn)行時(shí),如果Action拼錯(cuò)了導(dǎo)致匹配不上,要么是程序不能正常工作,要么會(huì)有異常拋出。
2. 通過(guò)Category字段來(lái)匹配對(duì)于Activity來(lái)講,如果想處理隱式Intent,并且除了Intent.ACTION_MAIN以外,必須指定Category為DEFAULT,否則不會(huì)被匹配到。因?yàn)镃ontext.startActivity()和Context.startActivityForResult()會(huì)自動(dòng)加上DEFAULT Category。其他情況,Service和BroadcastReceiver則不會(huì),對(duì)于Service和BroadcastReceiver,如果Intent中沒(méi)有指定Category,那么在其IntentFilter中也不必指定。
3. 通過(guò)Data字段來(lái)匹配這個(gè)相對(duì)來(lái)講比較復(fù)雜,通常Data包含uri, scheme(content, file, http)和type(mimeType)對(duì)于Intent來(lái)講有二個(gè)方法:
Intent.setData(Uri); //一個(gè)Uri,Scheme包含在其中
Intent.setType(String); //指定MimeType,比如'image/jpeg', 'audio/mpeg'等
Intent.setDataAndType(Uri, String); //上面二個(gè)方法的簡(jiǎn)便調(diào)用方式,一起搞進(jìn)去
對(duì)于IntentFilter來(lái)講,需要設(shè)置的是Scheme和Type,Scheme是對(duì)Uri的限制,目標(biāo)需要限制Scheme是因?yàn)镾cheme能告訴目錄能從哪里拿到Uri所指向的文件,Type是MimeType對(duì)類(lèi)型的限制。
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" android:mimeType="image/*" />
</intent-filter>
Data匹配時(shí)的規(guī)則一共有四條:
a.如果Intent沒(méi)有指定Data相關(guān)的字段,只能匹配上沒(méi)有指定Data的IntentFilter。也就是說(shuō)如果一個(gè)Intent沒(méi)有指定任何的Data(Uri和Type),它只能匹配到?jīng)]有指定任何Data(Scheme和Type)的IntentFilter。
b.如果一個(gè)Intent只指定了Uri但是沒(méi)有Type(并且Type也不能夠從Uri中分析出)只能匹配到僅指定了相應(yīng)Scheme且沒(méi)有指定Type的IntentFilter。實(shí)際的例子有如果一個(gè)Intent是想要發(fā)郵件,或是打電話(huà),它們的Intent是類(lèi)似這樣的:"mailto:someone@sb.com"和"tel:1234567"。換句話(huà)說(shuō),這些Uri本身就是數(shù)據(jù),而不再是一個(gè)指向數(shù)據(jù)的地址。比如:Phone中的Dialer就有如下的IntentFilter:
<intent-filter>
<action android:name="android.intent.action.CALL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
再如,要處理SD狀態(tài)變化的IntentFilter:
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
<action android:name="android.intent.action.MEDIA_SHARED"/>
<action android:name="android.intent.action.MEDIA_REMOVED"/>
<action android:name="android.intent.action.MEDIA_EJECT"/>
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
</intent-filter>
再如,要處理Package狀態(tài)變化的IntentFilter:
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package" />
<intent-filter>
但是注意,對(duì)于想對(duì)數(shù)據(jù)進(jìn)行操作的Intent,最好不要只指定Uri,而不指定類(lèi)型。因?yàn)槿绻@樣做通常會(huì)匹配到一大堆
c. 如果一個(gè)Intent只指定了Type,但是沒(méi)有指定Uri,它只能匹配到只指定了相應(yīng)Type且沒(méi)有指定Scheme的IntentFitler
d. 如果一個(gè)Intent即有Uri又有Type,那么它會(huì)匹配上:1).Uri和Type都匹配的IntentFilter;2).首先Type要匹配,另外如果Intent的Uri是content:或file:,且IntentFilter沒(méi)有指定Scheme的IntentFilter。因?yàn)閷?duì)于Android來(lái)講content和file這二種Scheme是系統(tǒng)最常見(jiàn)也是用的最多的,所以就當(dāng)成缺省值來(lái)對(duì)待。
另外需要注意,Type,因?yàn)槭荕imeType,所以是允許使用通配符的,比如'image/*',能匹配上所有以'image'為開(kāi)頭的類(lèi)型,也說(shuō)是說(shuō)能匹配上所有的圖像。
根據(jù)Data匹配的例子
假如系統(tǒng)中有四個(gè)Activity,A的IntentFilter是這樣子的:
<activity ...>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" android:mimeType="image/*" />
</intent-filter>
</activity>
這表明A可以發(fā)送一切圖片類(lèi)型,并且內(nèi)容必須是由ContentProvider提供的,也就是Uri必須是以"content://"開(kāi)頭的
而另外一個(gè)Activity B是這樣子聲明的:
<activity ...>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" android:mimeType="image/*" />
</intent-filter>
</activity>
這表明B可以發(fā)送一切圖片,但內(nèi)容必須是單獨(dú)的一個(gè)文件,也就是Uri必須是由"file://"開(kāi)頭的
還有一個(gè)C是這樣子聲明的:
<activity ...>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
這表明C只能接收那些沒(méi)有指定任何Uri和Type的Action是SEND的Intent。
而D是這樣子聲明的:
<activity ...>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
這表明D可以發(fā)送一切圖片,無(wú)論是數(shù)據(jù)庫(kù)內(nèi)的(content),還是單獨(dú)的文件(file)。
如果一個(gè)Intent是這樣寫(xiě)的:
Intent share = new Intent(Intent.ACTION_SEND);
startActivity(share);
那么它只能匹配C,因?yàn)镃沒(méi)有指定數(shù)據(jù)和類(lèi)型,Action是SEND,根據(jù)規(guī)則a,它只能匹配Activity A。但如果給Intent加上額外的條件
share.setDataAndType(uri,"image/jpeg");
那么如果uri是數(shù)據(jù)庫(kù)內(nèi)容,它會(huì)匹配到A,如果它是一個(gè)文件,會(huì)匹配到B。但無(wú)論是content還是file都會(huì)匹配到D,因?yàn)樗芴幚硪匀魏涡问酱鎯?chǔ)的圖片。但始終不會(huì)匹配到C,因?yàn)镃沒(méi)有聲明Data字段,所以不會(huì)匹配上。
所以,通常想把組件作為系統(tǒng)公用接口時(shí)都是這樣子來(lái)寫(xiě):
<activity ...>
<intent-filter>
<!-- implement public actions such as View, Edit, Pick or Send -->
<action android:name="android.intent.action.SEND" />
<!-- never forget default category, otherwise your activity never receives intents -->
<category android:name="android.intent.category.DEFAULT" />
<!-- specify mimeType to constrain data type, receive data from both content provider and file -->
<data android:mimeType="image/*" />
<!-- specify scheme to constrain data source, if necessary -->
<data android:shceme="http" />
</intent-filter>
</activity>
Intent和IntentFilter對(duì)于組件Activity來(lái)講注意事項(xiàng)比較多,但是對(duì)于Service和BroadcastReceiver來(lái)說(shuō)就沒(méi)有那么多的注意事項(xiàng)了,因?yàn)閷?duì)于Service和BroadcastReceiver通常都不用設(shè)置Category和Data。但也有例外,比如前面所講到的SD相關(guān)廣播和應(yīng)用程序安裝相關(guān)廣播。
另外要注意,如果使用Context.startActivity()或Context.startActivityForResult(),Context.bindService()和Context.startService(),如果系統(tǒng)沒(méi)有為Intent匹配到目標(biāo)Activity和Service那么會(huì)有RuntimeException(ActivityNotFoundException)拋出;如果有多個(gè)目標(biāo)同時(shí)匹配,會(huì)以列表的方式來(lái)讓用戶(hù)選擇使用哪個(gè)。
使用IntentFilter匹配來(lái)進(jìn)行查詢(xún)可用的組件
Intent和IntentFilter不但可以用來(lái)進(jìn)行組件復(fù)用,還可以用于查詢(xún)系統(tǒng)內(nèi)都有哪里組件能做哪些事情。比如Launcher上面會(huì)列出很多的應(yīng)用,其實(shí)這種說(shuō)法不準(zhǔn)確,應(yīng)該是上面列出了所有的能啟動(dòng)一個(gè)應(yīng)用的組件(比如,Dialer和Contacts同屬于一個(gè)應(yīng)用程序Contacts中,但是在Launcher里面卻有二個(gè),一個(gè)是Dialer一個(gè)是Contacts。那么Launcher是如何做到的呢?它不可能是去檢查系統(tǒng)文件,看看哪些應(yīng)用程序文件存在,然后再列出來(lái)。它是通過(guò)查詢(xún)Intent的方式,把所有含有"android.intent.action.MAIN"和"android.intent.category. LAUNCHER"的Activity的相關(guān)信息都取出來(lái),然后列出它們的名稱(chēng)和Icon。同樣,我們也可這樣來(lái)獲得具體相應(yīng)特征的組件
相關(guān)文章
Java中的final關(guān)鍵字詳解及實(shí)例
這篇文章主要介紹了Java中的final關(guān)鍵字詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-01-01ListView實(shí)現(xiàn)頂部和底部?jī)?nèi)容指示器的方法
這篇文章主要介紹了ListView實(shí)現(xiàn)頂部和底部?jī)?nèi)容指示器的方法,需要的朋友可以參考下2015-09-09android?studio實(shí)現(xiàn)上傳圖片到j(luò)ava服務(wù)器
這篇文章主要為大家詳細(xì)介紹了android?studio實(shí)現(xiàn)上傳圖片到j(luò)ava服務(wù)器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08Android 實(shí)例開(kāi)發(fā)基于ArcSoft實(shí)現(xiàn)人臉識(shí)別
人臉識(shí)別,是基于人的臉部特征信息進(jìn)行身份識(shí)別的一種生物識(shí)別技術(shù)。用攝像機(jī)或攝像頭采集含有人臉的圖像或視頻流,并自動(dòng)在圖像中檢測(cè)和跟蹤人臉,進(jìn)而對(duì)檢測(cè)到的人臉進(jìn)行識(shí)別的一系列相關(guān)技術(shù),通常也叫做人像識(shí)別、面部識(shí)別2021-11-11Android利用Sensor(傳感器)實(shí)現(xiàn)水平儀功能
這篇文章主要為大家詳細(xì)介紹了Android利用Sensor傳感器實(shí)現(xiàn)水平儀功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Pagerslidingtabstrip菜單標(biāo)題欄制作方法
這篇文章主要為大家詳細(xì)介紹了Pagerslidingtabstrip菜單標(biāo)題欄的制作方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10Android UI自定義ListView實(shí)現(xiàn)下拉刷新和加載更多效果
這篇文章主要介紹了Android UI自定義ListView實(shí)現(xiàn)下拉刷新和加載更多效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android TextView實(shí)現(xiàn)跑馬燈效果的方法
這篇文章主要介紹了Android TextView跑馬燈效果實(shí)現(xiàn)方法,涉及Android布局文件中相關(guān)屬性的設(shè)置技巧,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01