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);事实上虽然设置的方法有这么多,但Intent内部标识目标组件的属性只有一个Component,所以这么设置方法的目的也只是设置目标组件的Component,事实上有这么多的方法原因在于ComponentName的构造是多重载了的。在解析Intent时,系统也是取得这个Component属性,然后去启动它。
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);因为应用程序内部的组件类,都是可以访问到的,所以要尽可能少写字串常量,以减少拼写错误,如果一定要使用包名和类名,也要注意,类名必须是全称,也就是从包名开始,如“com.hilton.networks.WifiManagerActivity"。
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为参数的是不能够用的,因为通常你无法拿到其他应用程序的Context,你只能拿到你所在应用程序的Context,所以用你所在的应用程序的Context去启动外部的Activity肯定会报错的。其次,不参再像上面那样通过Class.getName()去指定类名,你为你无法导入外部的类,会有编译错误的。另外,千万要注意不要拼错,否则会有RuntimeException抛出的。
<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时要注意以下三点:
<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>虽然这种拼写错误很低级,但是因为它低级所以当程序不能正常工作时没有人会想到是因为拼写错误,所以这种拼写错误通常会耗费不少的调试时间。另外一种避免此种错误的方法就是在代码中通过Context.registerReceiver(BroadcastReceiver,IntentFilter)来注册BroadcastReceiver,就可以直接写入常量,而非具体字串。但这只能是接收Broadcast的时候,对于那些想作为公开接口的组件,还是需要在Manifest里面声明,比如Email,它要能处理Intent.ACTION_SEND_TO,就需要在Manifest中声明。2. 要注意Data字段除了上面讨论的之外,对于IntentFilter还有另外的一点需要注意,就是对于某些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>对于手机外部存储卡的状态变化的Broadcast,在注册监听器的时候就需要加上DataScheme,否则就会接收不到。这个也花费了我几个小时的调试时间,改在代码中用Context.registerReceiver(BroadcastReceiver,IntentFilter)注册也不行,最后参考了Music中的做法,加上了DataScheme才能在onReceive()中接收到Intent。同样对于后面的Package相关的Broadcast,也是要加上DataScheme否则也是接收不到Broadcast。可悲的是对于像这样的系统公共的Broadcast
<manifest ...> <activity android:name="EmailComposer"> <intent-filter> <action android:name="android.intent.action.SEND"/> <data android:mimeType="image/*" /> </intent-filter> </activity> </manifest>3. 同时也要注意Category字段
<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,当然这要取决于Intent是如何发出的,如果Intent发出时没有加Category,那就没有必须在IntentFilter加上Category。
1. 通过Action字段来匹配这个是Intent中比较基本的一个字段,也比较简单,就是一个字串,如果相等就匹配成功,否则证明还没找到目标。但要注意,如果IntentFilter没有指定Action,那么它不会匹配到任何的隐式Intent,它只能被显式的Intent匹配上。反过来,如果Intent自己没有指定Action,那么它能匹配上含有任何Action的IntentFilter,但不能匹配上没有指定Action的IntentFilter。对于Action,平时要注意拼写错误,因为在AndroidManifest文件中声明Action都是字串,并且在编译时不会做检查,运行时,如果Action拼错了导致匹配不上,要么是程序不能正常工作,要么会有异常抛出。
2. 通过Category字段来匹配对于Activity来讲,如果想处理隐式Intent,并且除了Intent.ACTION_MAIN以外,必须指定Category为DEFAULT,否则不会被匹配到。因为Context.startActivity()和Context.startActivityForResult()会自动加上DEFAULT Category。其他情况,Service和BroadcastReceiver则不会,对于Service和BroadcastReceiver,如果Intent中没有指定Category,那么在其IntentFilter中也不必指定。
3. 通过Data字段来匹配这个相对来讲比较复杂,通常Data包含uri, scheme(content, file, http)和type(mimeType)对于Intent来讲有二个方法:
Intent.setData(Uri); //一个Uri,Scheme包含在其中 Intent.setType(String); //指定MimeType,比如'image/jpeg', 'audio/mpeg'等 Intent.setDataAndType(Uri, String); //上面二个方法的简便调用方式,一起搞进去对于IntentFilter来讲,需要设置的是Scheme和Type,Scheme是对Uri的限制,目标需要限制Scheme是因为Scheme能告诉目录能从哪里拿到Uri所指向的文件,Type是MimeType对类型的限制。
<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匹配时的规则一共有四条:
a.如果Intent没有指定Data相关的字段,只能匹配上没有指定Data的IntentFilter。也就是说如果一个Intent没有指定任何的Data(Uri和Type),它只能匹配到没有指定任何Data(Scheme和Type)的IntentFilter。
b.如果一个Intent只指定了Uri但是没有Type(并且Type也不能够从Uri中分析出)只能匹配到仅指定了相应Scheme且没有指定Type的IntentFilter。实际的例子有如果一个Intent是想要发邮件,或是打电话,它们的Intent是类似这样的:"mailto:[email protected]"和"tel:1234567"。换句话说,这些Uri本身就是数据,而不再是一个指向数据的地址。比如: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状态变化的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状态变化的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>
但是注意,对于想对数据进行操作的Intent,最好不要只指定Uri,而不指定类型。因为如果这样做通常会匹配到一大堆
c. 如果一个Intent只指定了Type,但是没有指定Uri,它只能匹配到只指定了相应Type且没有指定Scheme的IntentFitler
d. 如果一个Intent即有Uri又有Type,那么它会匹配上:1).Uri和Type都匹配的IntentFilter;2).首先Type要匹配,另外如果Intent的Uri是content:或file:,且IntentFilter没有指定Scheme的IntentFilter。因为对于Android来讲content和file这二种Scheme是系统最常见也是用的最多的,所以就当成缺省值来对待。
另外需要注意,Type,因为是MimeType,所以是允许使用通配符的,比如'image/*',能匹配上所有以'image'为开头的类型,也说是说能匹配上所有的图像。
<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可以发送一切图片类型,并且内容必须是由ContentProvider提供的,也就是Uri必须是以"content://"开头的
<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可以发送一切图片,但内容必须是单独的一个文件,也就是Uri必须是由"file://"开头的
<activity ...> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>这表明C只能接收那些没有指定任何Uri和Type的Action是SEND的Intent。
<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可以发送一切图片,无论是数据库内的(content),还是单独的文件(file)。
Intent share = new Intent(Intent.ACTION_SEND); startActivity(share);那么它只能匹配C,因为C没有指定数据和类型,Action是SEND,根据规则a,它只能匹配Activity A。但如果给Intent加上额外的条件
share.setDataAndType(uri,"image/jpeg");那么如果uri是数据库内容,它会匹配到A,如果它是一个文件,会匹配到B。但无论是content还是file都会匹配到D,因为它能处理以任何形式存储的图片。但始终不会匹配到C,因为C没有声明Data字段,所以不会匹配上。
<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对于组件Activity来讲注意事项比较多,但是对于Service和BroadcastReceiver来说就没有那么多的注意事项了,因为对于Service和BroadcastReceiver通常都不用设置Category和Data。但也有例外,比如前面所讲到的SD相关广播和应用程序安装相关广播。