c2. App Components-Intents and Intent Filters
来源:互联网 发布:易语言30000源码图片 编辑:程序博客网 时间:2024/06/06 00:08
Intent是一个消息载体, 可以用来对其他App 组件请求相关的操作。组件间通过Intent来通信有几种方法,比较基础的有三种,分别是:
1. 通过Intent来启动一个Activity:
一个Activity 表示一个简单的界面,你可以把一个Intent传递给startActivity()函数来启动一个新的Activity,当然事先你得配置好这个Intent。
如果你想启动后的新的Activity 返回结果回原来的Activity,那么你可以将一个Intent传递给函数startActivityForResult(),通过在原来的Activity的回调函数onActivityResult()来接收新的Activity返回的结果(结果封装在一个新的Intent里面)。
2. 通过Intent来启动一个Service:
一个Service是一个在后台运行的组件,没有用户界面。你可以把一个Intent传递给函数startService()来启动一个Service(比如下载文件),当然事先你得配置好这个Intent。
如果Service是设计成客服端/服务器模式,那么可以将一个Intent传递给函数bindService()来绑定一个Service。
3. 通过Intent来发送一个Broadcast
一个Broadcast是一个任何App都能接收的消息。当发生系统事件时,系统会派发各种各样的Broadcast给各个App,比如当系统开机,设备开始充电等。你可以把一个Intent传给sendBroadcast(), sendOrderedBroadcast(), sendStickyBroadcast()来派发一个broadcast。
Intent 种类
在Android中有两种Intent:
显式Intent: 通过指明组件的名字(类完整的名字)来启动activity。典型的做法是你会通过一个显式的Intent在你自己的App启动一个组件,因为你实现知道这个组件(activity,service)的类名。比如启动一个activity来响应用户的动作或者启动一个后台service来下载文件。
隐式Intent: 不指明特定组件的名字,而是通过声明一个普遍的动作来实现,这样可以使得在其他App的组件来处理。比如,如果你想要给用户在地图上展示一个位置,那么你可以通过隐式的Intent来先请求那些有map的app来完成这个任务。
当你创建一个显式的Intent来启动一个Activity或者Service时,系统马上启动指定的组件。
当你创建一个隐式的Intent,android通过比较比较此时创建的Intent与其他App中的manifest file中intent-filter声明来启动那些符合要求的组件,如果有多个组件符合要求,那么系统将符合要求的组件显示给用户选择。
Intent-filter是在app中的manifest file 中的组件指定什么样的intent可以接收。比如,通过声明在你的组件上声明一个intent-filter,你可以使得其他的apps通过符合条件的intent来启动你的组件。如果你没有在你的组件上声明intent-filter,那么其他的组件只能通过显式的intent来启动你的组件。
注意:为了保证你的app是安全的,记得用显式的intent来启动service,并且不要再service中声明intent-filter,使用隐式的intent来启动service是不安全的,因为你不能保证那个service会响应,而且你不能看到哪个service被启动。
创建一个Intent
Intent包含android系统用于决定启动那个组件的信息(比如组件的类名或者应该接收这个intent的组件类别),除了这些,还包括一些用于指导接收组件的动作的信息(包括接收组件该执行的动作和需要的数据)。
包含在intent的主要信息有一下几个:
组件名称
需要启动的组件的名称
这个是可选的,但是对于显式的intent是必须的,指定好组件的类名后,intent只能去启动指定的组件。如果没有包括组件的类名,这样的intent是隐式的,系统根据其他信息来决定启动哪个组件(比如接下来提到的动作,数据,类别)。所以如果你需要在你的app中启动一个指定的组件,那么你应该在intent中指定组件的类名。
注意:当启动一个service,你必须指定组件的名称,不然你不能保证哪个service会响应你的intent,而且用户不能看到哪个service启动。
组件名称对应在intent的是ComponentName,必须将目标组件的全类名赋值给给这个变量,包括组件所在的包。比如com.example.ExampleActivity。你可以通过函数setComponent(), setClass(), setClassName()或者Intent的构造函数来设置目标组件的类名。
动作(Action)
一个字符串变量,用来指定需要执行的动作。
如果这是个用于broadcast的intent,那么action这个变量用来表示此时被通报的发生的动作。这个动作决定了intent其他信息的组织,特别是包含在data和extras部分的信息。
你可以在你的apps里面指定你要用到的动作(或者用于其他apps要调用你的组件时),但你必须使用intent类或者其他框架类定义的动作常量。下面是一些用来启动activity常用的动作:
ACTION_VIEW:在要传人startActivity()的intent中使用合格动作,表示你在这个activity有些信息要展示给用户,比如在一个相册apps中查看图片或者在一个地图app中显示地址。
ACTION_SEND:又名分享intent,当你有些数据要与其它app共享,比如与电子邮件app或者社交分享app,必须在传给startActivity()的intent包含这个信息。
可以查看Intent类中更多关于动作的常量。其它动作定义在android 框架的其它地方,比如在Settings中系统Settings app用于打开指定屏幕的动作。
你可以通过setAction()来指定要执行的动作,或者通过intent的构造函数。
如果你想定义自己的actions,一定要保证将你的app的包名用于前缀。比如
static final String Action_TIMETRAVEL = "com.example.action.TIMETRAVEL";
数据(Data)
包含要被执行的引用URI或者数据的MIME类型。数据的类型通常是由intent的动作决定的,例如,当动作是ACTION_EDIT,那么数据就应该包括要编辑文档的URI。
当创建一个intent,一定要记得指定数据的类型(它的MIME类型)和它的URI。比如,一个能够展示图片的activity可能不会播放音频文件,虽然音频文件和图片的URI格式可能相识。所以指定你数据的MIME类型有助于android系统找到最合适的组件来接收你的intent。但是,MIME类型有时候可以从URI推断出来,特别当数据是content: URI,表明数据在设备上,并且通过ContentProvider控制,数据的MIME类型对系统是可见的。
调用setData()来设置数据的URI。调用setType()来设置MIME类型。必要的话,你可以调用setDataAndType()来设置URI和MIME类型。
注意:如果你想要同时设置URI和MIME类型,不要同时调用setData()和setType(),因为它们导致前面设置的参数无效。所以当你需要同时设置这两个,调用函数setDataAndType().
类别(Category)
一个包含关于哪个组件来接收当前intent的额外信息的字符串。可以在一个intent放置任意数目的类别,但是大部分的intent不需要类别这个域。下面是一些通用的类别:
CATEGORY_BROWSABLE
目标activity允许自己被网页浏览器启动去展示有链接引用的数据,比如图片或者电子邮件消息。
CATEGORY_LAUNCHER
一开始被启动的activity,在系统的应用lancher列表上。
详细请见Intent类关于categories的描述。
可以通过 函数addCategory().
上面列出的组件名字,动作,数据,类别是关于怎么去定义一个intent。通过这些参数,android系统可以解析这些参数去决定调用哪个app组件。
除了这些外,intent还可以包含其它与解析那个app组件被
调用无关的信息Extras
携带额外信息去完成要求的动作的键值对。就像一些动作需要特殊的数据URI,一些动作也需要使用额外的extras。
可以通过函数putExtra()来添加extra数据,这个函数包含两个参数:键(key)和值(value).你可以创建一个包含extra数据的Bundle对象,然后通过函数putExtra()将bundle对象添加到intent.
例如:当创建一个intent来发送邮件,指明动作ACTION_SEND,你可以指定通过EXTRA_EMAIL键来指定接收者(to)。通过EXTRA_SUBJECT键值来指明标题。
intent类指定了需要EXTRA_*的常量用于标准数据类型。如果你需要声明你自己的extra keys, 确保用你的报名作为前缀,例如:
static final String EXTRA_GIGAWAITS = "com.example.EXTRA_GIGAWAITS";
FLags(标示)
Flags定义了intent类怎么作为metadata。flags可以引导android系统怎么launch一个activity(比如,activity属于哪个task)和launched后怎么处理这个activity(例如,它是否属于最近的activity列表)
更详细的请查看setFlags()函数。
显式intent例子
显式的intent用来启动指定的app组件,比如一个特殊的activity或者service。创建一个显式的intent,定义intent对象的组件名字,其他intent属性根据情况定义。
比如,如果你创建一个名为DownloadService的service在你的app中,即从web下载文件,你可以通过下面的代码启动这个service:
//Executed in an Activity, so 'this' is the Context//The fileUrl is a string URL, such as "http://www.example.com/image.png"Intent downloadIntent = new Intent(this, DownloadService.class);downloadIntent.setData(Uri.parse(fileUrl));startService(downloadIntent);
Intent(Context, Class)构造函数以app的Context和组件的类名为参数。这个intent显式的启动了DownloadService类。
关于service的更多内容,请查阅Services
隐式intent的例子
隐式的intent指定要执行的动作,以便调用设备上的其它app可以执行这个动作。当你的app不能执行某个动作时,就应该使用隐式intent。如果有多个app可以执行相应的动作,那么用户可以选择哪个app。
比如,如果你想和其他人共享你的内容,那么你可以创建一个带有动作ACTION_SEND的intent,可以添加一些额外的内容。当你调用startActivity(Intent)是,你就可以选择一个app来共享这些内容。
注意:在android系统中,有可能没有符合此隐式intent条件的app,如果发生这种情况,那么你的app将会crash。所以事先得验证是否有符合条件的app来接收这个intent,可以调用resolveActivity(Intent)来判断是否有符合条件的app,如果返回是非空,那么说明有符合条件的app,可以继续调用startActivity()。如果返回结果是空,那么不要使用这个intnet,而且以后不要用到类似intent提到的特性。
//Create the text message with a stringIntent sendIntent = new Intent();sendIntent.setAction(Intent.ACTION_SEND);sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);sendIntent.setType(HTTP.PLAIN_TEXT_TYPE);if(sendIntent.resolveActivity(getPackageManager()) != null) {startActivity(sendIntent);}
注意:在这种情况下,没有用到URI,但是intent的数据类型通过在extras中指定内容来声明。
当startActivity()函数被调用,系统会检查所有的apps来决定哪些可以接收这个intent。如果只有一个app可以接收这个intent,那么这个app将会立即被启动。如果多个app符合条件,那么系统会显示一个对话框让用户选择。
强制一个app选择器
当有多个app可以接收这个隐式的intent时,用户可以选择一个app来执行,并设置这个app为默认执行的app。这种选择方式对于用户接下来一直会用这个app是有好处的,比如用户通常会选择一个浏览器来浏览网页。
但是,当用户希望每次用不同的app来接收这个intent时,必须显示一个选择对话框。通过选择对话框,用户选择一个app来执行。比如,当你的app使用ACTION_SEND来执行分享功能时,用户可能想根据现在的情况使用不同的app来分享。所以必须老是使用选择对话框。
通过将原来的intent作为参数传入到createChooser()来创建一个新的intent,然后将这个新的intent传给startActivity()函数。
Intent intent = 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);Intent chooser = Intent.createChooser(intent, title);//verify the intent will resolve to at least one activityif(chooser.resolveActivity(getPackageManager()) != null) {startActivity(chooser);}
上面会显示一个选择对话框,里面的app符合intent的条件。
接收一个隐式的Intent
你可以在你的app组件中的<intent-filter>中声明相应的条件来介绍隐式的intent。每个intent filter根据intent的action, data, category来设定。如果某个intent符合其中的一项,系统会派发一个隐式的intent给你的app组件。
注意:一个显式的intent会被派发到指定的app组件,不论这个组件声明了哪些intent-filter。
每个app组件应该为其每个任务都声明独立的filter。例如,一个相册的app的一个activity有两个filters: 一个用来浏览图片,一个用来编辑图片。当这个activity启动后, 这个activity会根据Intent携带的信息来执行相关的动作(比如是否显示编辑菜单)。
每个intent filter通过<intent-filter>来定义,内嵌在每个<activity>中。你可以通过下面的元素来设定接收的intent类型:
<action>
声明接收的intent的action,name的值必须是一个action的字符名,不能是类的常量。
<data>
声明接收的数据类型,使用多个属性来指定不能的数据URI (scheme, host, port, path,等)和MIME类型。
<category>
声明接收的intent类型。在name的值必须是字符串,不是类常量。
注意:为了接收隐式的intents,必须在intentfilter中的category包含CATEGORY_DEFAULT。如果组件定义了CATEGORY_DEFAULT类型,那么函数startActivity()和函数startActivityForResult()将会派发给这个组件intent。如果你的组件中没有声明这个类型(CATEGORY_DEFAULT),那么你的组件将不会接收到任何隐式intent.
下面例子是在一个activity中声明一个intent filter来接收包含ACTION_SEND的intent。
<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>
在一个filter里面可以包含多个<action><data><category>。如果你这样做,确保这个组件可以处理任何这些限制条件组合的所有情形。
但你需要处理多个不同类型的intents,但是是这些action,data, category的特殊组合,那么你必须创建多个独立的intent-filter。
一个隐式的intent通过与filter里面的action,data, category比较来选择启动的组件。一个intent必须通过action,data, category这三个限制条件才能被组件接收。一旦不符合其中一项,android系统将不会将这个intent派发给不符合的组件。但是在一个组件中有多个filter,一个intent可能不通过某个filter,但是符合其他filter。更多详细信息请参照Intent Resolution.
注意:为了防止不经意启动不同app的Service,一定要使用显式的intent来启动你自己的service。而且在对应的service不要添加filter.
注意:对于所有的activities,你必须在manifest文件中声明相应的filter。但是对于广播接收者可以通过调用registerReceiver()函数来动态的注册。可以广汉市unregisterReceiver()来解除。这样做可以让你的app在特定的时间段监听特殊的广播信号。
filter例子
为了更好的理解一些intent filter,下面代码段是一个社交分享app的filter.
<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" action 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>
第一个activity MainActivity是app的入点,即用户打开app看到的第一个activity:
其中的ACTION_MAIN表明这是app的入点,不希望其他intent数据。
其中的CATEGORY_LAUNCHER表明这个activity的图标在系统app的launcher。如果<activity>元素中没有指明图标,那么系统将使用<application>中指定的图标。
这两个元素必须成对出现,以便activity出现在app的launcher。
第二个activity,ShareActivity,是为了分享文本和多媒体内容。用户可能通过MainActivity进入到这个组件,也有可能通过其他app进入到这个组件中。其它app必须通过声明一个隐式的intent匹配对应的两个filter。
注意:MiME类型:application/vnd.google.panorama360+jpg是Google panorama中一种特殊的panoramic图片数据类型。
使用一个Pending的intent
一个PendingIntent对象是intent对象的封装。PendingIntent是为了保证外界的app能够使用已经包含的Intent,正如在自己的进程中执行一样。
对于pending intent的主要用处包括:
声明一个intent当用户在你的通知下执行一个动作执行(android系统的NotificationManager执行Intent)
声明一个intent当用户用你的app widget执行一个动作是被执行(home screen app 运行intent)
声明一个intent在特定的时间呗执行(android系统的alarmManager运行intent)
因为每个intent对象是被用于启动其他组件的(Activity, Service, BroadcastReceiver)。所以PendingIntent也应该这样。当使用一个pending intent是,你的app不会通过调用startActivity()来启动这个PendingIntent,而是通过下列函数来创建一个PendingIntent:
通过PendingIntent.getActivity()用于intent启动一个activity
通过PendingIntent.getService()用于intent启动一个service
通过PendingIntent.getBroadcast()用于intent启动一个broadcastReceiver
除非你的app从其他的app接收pending intent。.否则上面的方法是所有你用于创建PendingIntent的方法。
每个函数以单签的app的Context, 你想要的Intent和一个活多个指定这个intent会怎么被用(比如一个intent是否可以被用多次)作为参数。
更多关于使用pending intents的信息请参照Notifications和App widgets.
Intent Resolution
但系统接收一个隐式的intent来启动一个activity是,它会通过比较intent的内容和组件中intent-filter中的action,data, category来找到最合适的activity:
下面内容描述了一个intent怎么匹配到合适的组件
Action测试:
为了指定接收的intent的动作,一个intent-filter可以声明多个<action>,例如:
<intent-filter><action android:name="anroid.intent.action.EDIT" /><action android:name="android.intent.action.VIEW" />....</intent-filter>
为了通过这个filter,在intent声明的action必须匹配filter <action>其中的一个actions
如果filter中没有包含action,那么将没有intent可以匹配。但是如果一个intent没有声明action,那么这个intent将通过所有filter的action部分(但filter至少含有一个<action>.
Category test
组件通过在intent-filter中的<category>声明适合的category。例如:
<intent-filter><category android:name=“android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" />...</intent-filter>
对于想要通过category测试的intent,它里面的每个category必须匹配filter的category。反过来则不是,在filter可以声明多个categories, 这些可以在intent中没有。因此,一个没有包含category的intent可以通过所有的category测试。
注意:android自动在每个传给函数startActivity()和startActivityForResult()的intent添加CATEGORY_DEFAULT。所以如果你想让你的组件接收隐式intents,这个组件必须包含CATEGORY_DEFAULT这个category,即在intent-filter中的category声明android.intent.category.DEFAULT
Data test
通过在intent-filter中<data>声明data属性,比如
<intent-filter><data android:mimeType="video/mpeg" adnroid:scheme="http" .../><data android:mimeType="audio/mpeg" android:scheme="http" .. />...</intent-filter>
每个<data>指定一个URI结构和数据类型(MIME类型).对于URI,有多个部分:
<scheme>://<host>:<port>/<path>
比如:
content://com.example.project:200/folder/subfolder/etc
在这个URI中,content是scheme,com.example.project是host,200是port, folder/subfolder/etc是path。
这些元素在<data>都是可选的。但是有线性相关性:
如果scheme没有指定,那么host忽略
如果host没有指定,那么port忽略
如果scheme和host都没有指定,那么path忽略
当一个Intent的URI与filter的URI进行比较时,只是进行部分比较:
如果在filter只指定scheme,那么包含这个scheme都匹配这个filter。
如果在filter指定一个scheme和一个authority,但是不包含path,那么所有跟这个scheme和authority都符合这个filter,不管path。
如果在filter指定一个scheme,一个authority和一个path,那么只有相同的scheme,authority,path才符合这个filter。
注意:在path中可以包含*来部分匹配。
data测试将intent中的URI和MIME与filter中对应项进行比对,规则如下:
一个不包含URI和MIME类型的intent只能通过没有指定URI和MIME的filter
一个只含有URI的intent只能通过匹配的URI的filter,并且这个filter没有指定MIME类型。
一个只含有MIME的intent只能通过相同的MIME的filter,并且这个filter没有指定URI。
一个既包含URI和MIME的intent,其中的MIME类型必须是filter中MIME类型的一种,其中的URI要么跟filter中的URI匹配,要么intent的URI中有content或者file元素,而且filtr没有指定URI。换句话说,只有MIME的组件默认支持content::和file:类型的URI。
最后一个规则表明组件可以从一个file或者content provider获取数据。因此,它们的filter可以仅仅列出MIME类型,而不用特别的指定content和file元素。下面例子表明这个组件可以从一个content provider获取并显示图片:
<intent-filter><data android:mimeType="image/*" />...</intent-filter>
另外一种常见的配置是filter中包含一个scheme和data类型。比如下面的例子组件可以通过网络接收视频来执行相应的动作。
<intent-filter><data android:scheme="http" android:type="video/*" /><intent-filter>
Intent 匹配
Intent不仅用来发现目标组件以便激活,而且可以用来发现设备上的组件信息。比如,Home app通过找出在组件的filter包含action有ACTION_MAIN和category有CATEGORY_LAUNCHER的组件来启动。
同样,你的app也可以使用类似的组件匹配方式。PackageManager有一系列的query...()方法来返回符合一个特定intent的组件,也有一系列类似的resolve...()函数来决定哪个组件最适合响应对应的intent。例如,queryIntentActivities()返回符合特定intent的组件列表,类似queryIntentServices()返回service列表。这两个函数都没有启动组件,仅仅是列出符合的组件,类似,queryBroadcastReceiver()列出broadcast receiver列表。
- c2. App Components-Intents and Intent Filters
- Android API Guides 之 App Components(2) - Intents and Intent Filters - Common Intents
- Android API Guides 之 App Components(1) - Intents and Intent Filters
- Android官方文档之App Components(Intents and Intent Filters)
- Intents and Intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- intents and intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- Intents and Intent Filters
- 解析wifi共享精灵怪现象 ——其他正常,电脑打不开网页
- SMARTCLIENT入门教程之六——Hello World
- hibernate框架学习笔记
- codeforces 225 div2
- 如何修改12306的默认时间
- c2. App Components-Intents and Intent Filters
- 完全卸载oracle11g步骤(不积跬步,无以至千里)
- Gesture - Tap单击
- 身居乱世之中,重新审视“活法
- eclipse 编码设置 tomcate启动超时 连接到类的快捷键设置
- Linux下apache日志分析与状态查看方法
- 60个开发者不容错过的免费资源库
- Linux文件系统介绍
- C++实现RTMP协议发送H.264编码及AAC编码的音视频,摄像头直播