Android Intent和Intent filter

来源:互联网 发布:java学生宿舍管理系统 编辑:程序博客网 时间:2024/05/18 22:13

概述:

Intent是一种消息对象, 用于向其它组件发送一个动作请求. 就是告诉别的组件, 请帮我点儿啥, intent相当于一个口信. Intent有三个典型的作用:

1.      启动一个activity. intent在这里发挥的作用有两个: 指定要启动的activity类, 指定需要携带的信息. 然后通过startActivity()传入intent参数就可以启动一个activity了.

2.      启动一个service. 跟启动一个activity类似, 只需要给startService()传递一个填好内容的intent对象就可以启动一个service了.

3.      发送一个boardcast. 这是一个整个系统都可以收到的广播, 我们可以通过调用sendBroadcast(), sendOrderedBroadcast(), 或sendStickyBroadcast()方法, 并传入intent参数, 就可以发送一条广播. 系统在某些特定的条件下也会发送广播, 比如系统启动完毕或者设备开始充电等.


Intent类型:

有两种类型的Intent:

1.      显式intent, 需要通过intent直接指定需要启动的组件的类名. 显式intent用于启动APP内部的组件时使用, 因为是内部组件, 所以我们知道完整的类名, 只需指定名字即可.

2.      隐式intent, 在不知道类名的情况下使用, 通常用于跨APP启动组件. 比如你需要用浏览器类的APP打开一个网页.

当我们通过显式intent启动组件的时候, Android会直接根据intent指定的名字来启动一个组件; 而当我们通过隐式intent启动的时候, 情况就略微复杂些, Android会搜索其它APP中的Manifest文件, 查找跟intent内容相匹配的intent filter, 然后启动该组件, 如果有多个可以使用的APP可以启动, 那么Android会显示一个对话框列表让用户选择想要启动哪个.

intent filter是Manifest文件中, 对组件想要接收的intent的描述, 就好像一个美女的征婚信息,写着要求器大活好, 有车有房, 只有满足这样要求的物种才能被她响应. intent filter就是这样的一个”要求”. 满足要求的intent, 这个组件才会响应. 对于不指定intent filter的组件, 就只能被显示intent调用了. 隐式intent响应过程如图:


[1]Activity A创建了一个带有action的intent, 并作为参数传给startActivity().[2]Android搜索所有的APP, 以找到一个可以满足该intent的intent filter. [3] 当找到这么一个intent filter之后, Android启动该activity并且通过onCreate()方法将intent传递给这个Activity B.

为了确保service的安全性, 应当使用显式intent启动自己的service, 隐式启动service会存在安全隐患, 因为你不知道是不是有别的意料之外的service会响应你的intent, 不同于activity毕竟用户看不见service被响应. 从Android 5.0(API21)开始, 通过隐式intent调用BingService()方法的时候将会抛出一个异常.

Intent的创建:

Intent可以携带很多信息, 比如组件名称, 组件种类(category),数据等, intent可以包含的基本信息如下:

组件名称: 想要启动哪个组件, 就用哪个组件的名字咯. 该项是可选的, 但是是显式intent的标志, 指定了名字就明确了想要启动的组件. 没有名字的intent就是隐式intent了, 需要根据intent内部的信息(比如action, category, data等)才能确定需要启动的组件是哪个. 所以如果想要启动一个APP内部指定的组件, 那么请指定组件名称.

当启动一个Service的时候应该总是指定一个组件的名称, 原因上面已经提到了.

组件名称是一个ComponentName对象, 可以指定一个完整的包含包名的类名, 比如com.example.ExampleActivity, 可以使用intent的setComponent(),setClass(), setClassName()方法指定组件的名称, 也可以通过Intent的构造方法来指定.

 

Action: 一个字符串, 指定要执行的操作.在广播和隐式启动Activity的时候最常使用, 可以定义自己的action, 也可以使用系统定义的action. 下面是两个常用于activity的action:

ACTION_VIEW: 当使用startActivity()启动一个activity,传递的intent由带有这个action的话, 意思是intent中包含一些可以展示给用户的信息, 比如一张图片, 或者一个需要在地图上显示的坐标等.

ACTION_SEND: 当你有一些可以与其它APP共享的数据的时候可以使用该action(通过startActivity),比如发个邮件或者社交共享类的APP.

除了Intent之外还有些系统定义的action保存在framework的其它类中, 比如Setting, Settings.ACTION_SETTINGS //系统设置界面,Settings.ACTION_WIFI_SETTINGS //wifi设置界面. 我们可以使用setAction()方法或者Intent的构造方法来设置action.如果需要自定义action, 请保证加上包名作为action的前缀:

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";

Data: 有时候intent需要携带一些数据, data就是它携带的数据. data本身是一个URI(一个Uri对象), 或者外加一个MIME类型. data的类型通常由intent的action决定, 先决定做什么, 然后才决定需要什么才能做, 买菜拿钱, 打架出拳. 比如如果action是ACTION_EDIT, 那么data就应该包含一个文档的URI. 我们可以通过setData()方法只设置data的Uri, 通过setType()方法只设置MIME类型, 如果两个都需要设置那么必须调用setDataAndType()来一次设置它俩. 这里需要注意的是如果两个值都需要设置, 那么不能分开调用setData()和setType(), 因为它俩会互相覆盖.


Category: 一个字符串, 描述应该用来处理该intent的组件的类型. 一个intent可以包含多个category, 但是一般情况下用不着. 下面是一些典型的category:

CATEGORY_BROWSABLE: 表示该activity可以被浏览器通过一个连接启动用于显示数据, 比如用于显示图片和Email信息等.

CATEGORY_LAUNCHER: 表示这个activity是用于初始化任务的activity,它应该出现在系统的APP列表中.

可以通过addCategory()方法指定category.

 

上述四个属性为Android提供了选择组件的依据, Android因为这些属性才知道这个intent对象应该发给谁. intent还可以添加其它的数据作为额外的数据而不影响Android如何处理它. 包括Extras和Flags.


Extras: 自定义的键值对, 用于保存数据. 有些特别的action需要用这种形式传递数据, 更多的时候是用户自定义. 我们可以通过putExtra()方法来添加要传递的数据, 参数有两个, 一个是key, 一个是value, 也可以通过传递一个Bundle对象来存放所有的数据. 比如当我们想要用为ACTION_EMAIL的action来发送一封Email的时候, 通过putExtra(EXTRA_EMAIL, “name”)来指定给谁发邮件, 通过putExtra(EXTRA_SUBJECT,“subject”)来指定Email的主题. Intent类包含很多EXTRA_*这种格式的标准数据类型, 如果需要声明自己的extra keys, 那么请确认使用包名作为前缀:

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";


Flags: intent的元数据, 可以告诉Android应当如何启动activity, 启动之后如何对待这个activity, 可以使用setFlags()方法设置Flags. 功能比较繁杂, 比如FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_CLEAR_TOP之类的, 告诉Android如何对待该activity和task的关系.

 

隐式Intent例子(显式的就不写了,太简单):

这个也比较常见, 但是有一个需要注意的地方, 当我们使用startActivity()隐式启动一个activity的时候, 可能根本没有任何一个activity可以接收我们的intent, 这个时候我们的APP就会挂掉, 为了避免这种情况的出现, 那么在使用隐式Intent之前需要先对其进行”测试”看有人愿意接收不, 我们可以调用resolveActivity()实现这一功能, 如果这个方法返回的值不为空, 那么意味着有至少一个activity可以被我们的intent启动, 栗子:

// Create the text message with a stringIntent sendIntent = new Intent();sendIntent.setAction(Intent.ACTION_SEND);sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);sendIntent.setType("text/plain");// Verify that the intent will resolve to an activityif (sendIntent.resolveActivity(getPackageManager()) != null) {    startActivity(sendIntent);}

当栗子中的startActivity()调用了之后, Android会检查所有的APP, 看哪一个可以处理action为Intent.ACTION_SEND, 并且DATA为”text/plain”类型的intent, 如果只找到一个, 那么会直接启动它并且将intent传给它的onCreate()方法, 如果找到多个, Android会显示一个列表, 让用户自己选择使用哪个.


强制使用一个APP选择器:

当有多个APP可以处理同一个action的时候, 用户可以选择其中一个作为默认处理这个action的APP, 但是有些时候呢我们需要不同的APP来处理这个action, 比如: 


不见得每次都想将视频分享到同一个APP, 那么我们需要每次都弹出一个APP选择器, 为了实现这个操作, 我们需要使用Intent的createChooser()方法, 并它传给startActivity(). 栗子:

Intent sendIntent = 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);// Create intent to show the chooser dialogIntent chooser = Intent.createChooser(sendIntent, title);// Verify the original intent will resolve to at least one activityif (sendIntent.resolveActivity(getPackageManager()) != null) {    startActivity(chooser);}

接收一个隐式Intent:

为了接收一个隐式Intent, 必须在Manifest文件中对该activity指定<intent-filter>, 每个<intent-filter>指定了可以接收的intent的action, data和category, 只有intent和intent filter匹配的时候, 系统才会将intent发送给这个activity. 每个filter应该只对应一项任务, , <intent-filter>可以包含下子标签, 以供Android筛选的时候查看:

<action>: 指定intentfilter对应的action, 通过name属性指定名字.

<data>: 指定可以接收的data的URI和MIME类型.

<category>: 指定可以接收的category,需要通过name属性指定名字. 为了接收隐式Intent, 必须指定CATEGORY_DEFAULT这个category.

每个activity可以指定多个<intent-filter>, 每个intent filter也可以指定多个action,category和data. 如果指定了<intent-filter>又不想其它的APP访问我们的activity, 那么只需要使用android:exported=”false”即可.

栗子:

<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" actions 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>

MainActivity是APP的主入口, ACTION_MAIN指定了可以接收这个action的intent filter是APP的主入口并且不需要接收intent实例. CATEGORY_LAUNCHER说明这个activity的icon应该出现在应用程序的列表中, 如果activity没指定icon, 那么将会使用<application>的icon. ACTION_MAIN和CATEGORY_LAUNCHER必须成对出现以指定这是一个APP的入口activity.

ShareActivity意在用于分享文字和媒体资源, 用户可以选择直接从MainActivity通过显式Intent启动它也可以在别的APP中用隐式Intent启动它, 只要满足其任何一个<intent-filter>就可以启动这个activity.

Intent解析:

当系统接收到一个隐式Intent的时候, 会查找符合的action, data和category, 下面分别描述Android如何匹配这三个标签:

Action: 一个intent filter可以声明0个或者多个action, 比如:

<intent-filter>    <action android:name="android.intent.action.EDIT" />    <action android:name="android.intent.action.VIEW" />    ...</intent-filter>

intent带有的action必须匹配其中一个action才可以启动这个intentfilter对应的组件.

两种特殊情况:

如果一个<intent-filter>不包含任何的action, 那就没有intent可以满足匹配, Android会自动退出这次匹配.

如果一个intent不带有任何的action, 那么它可以匹配所有有action的<intent-filter>.

Category: 一个intent filter可以声明0个或者多个category, 比如:

<intent-filter>    <category android:name="android.intent.category.DEFAULT" />    <category android:name="android.intent.category.BROWSABLE" />    ...</intent-filter>

对于想要通过的intent, 这个intent中的每个category必须对应一个intent filter中的category. 反之则不必要—intent filter可以包含比intent中携带的category多, 这不影响intent的通过. 所以不带category的intent总是可以通过…不管intent filter中声明了什么category. 一个挺奇怪的设定. .

Android会自动为传入startActivity()和startActivityForResult()方法中的intent添加CATEGORY_DEFAULT,所以如果我们的activity想要接收隐式Intent, 那么必须包括一个"android.intent.category.DEFAULT".

Data: <intent-filter>可以包括0个或者多个data标签. data标签用来指定隐式Intent想要接收的数据类型. 比如:

<intent-filter>    <data android:mimeType="video/mpeg" android:scheme="http" ... />    <data android:mimeType="audio/mpeg" android:scheme="http" ... />    ...</intent-filter>

每个<data>可以指定一个URI和一个MIME类型. 对于每一个URI又有独立的属性可以指定:

scheme, host, port, 和 path. 对应的URI格式是:

<scheme>://<host>:<port>/<path>

比如: content://com.example.project:200/folder/subfolder/etc

在这个URI里面, scheme是content, host是com.example.project, port是200, path是folder/subfolder/etc.

这些属性在<data>里都是可选的, 但是它们有依赖关系: 如果不指定scheme, 那么host将会被忽略; 如果没指定host, 那么port将会被忽略; 如果scheme和host都没指定, 那么path将会被忽略.

URI匹配规则:

  •   如果filter仅指定了一个scheme,那么所有具备该scheme的URI都会匹配.
  •   如果filter仅指定了scheme, authority(大概相当于host+port),那么Android会忽略path, 直接匹配scheme和authority相同的URI.
  •   如果filter指定了完整的URI, 那么必须scheme, authority和path完整匹配才能匹配.
  •   path可以通过”*”来部分匹配.

在匹配data的时候, 除了URI, 还会匹配MIME类型, 规则如下:

  •   如果intent既没有URI也没有MIME类型, 那么它只能通过不指定URI和MIME类型的filter.
  •   一个只包含URI而没有MIME(既不明确也不能从URI推理)的intent, 只有在匹配filter的URI并且filter没有指定MIME的情况下才算通过.
  •   一个只包含MIME而没有URI的intent, 只有在filter指定了同样的MIME并且没指定URI的时候才能通过.
  •   一个包含URI和MIME(明确指定或者从URI推理)的intent, MIME部分如果在filter的列表里就可以通过.URI部分如果intent的URI和intent filter的URI可以匹配, 那当然通过, 还有一种情况就是intent指定了content:或者file:的URI并且filter没有指定URI. 换句话说就是当intent filter只提供MIME的时候, 组件默认是支持content:和file:类型的URI的.

根据最后一条规则, 因为data默认是可以支持content:和file:的, 所以我们声明的时候可以省略这两种URI的scheme, 比如写成下面这样来告诉Android组件可以从content provider获取图片数据并显示:

<intent-filter>    <data android:mimeType="image/*" />    ...</intent-filter>

因为大部分的data是由content provider分配, 仅指定一个data type而不是URI成了最常用的方法.

另一种常用的配置是用scheme和data type来指定一个filter. 如下的栗子告诉Android这个组件可以处理从网络过来的视频数据:

<intent-filter>    <data android:scheme="http" android:type="video/*" />    ...</intent-filter>


参考: http://developer.android.com/guide/components/intents-filters.html



0 0
原创粉丝点击