3.5 Intent属性、过滤器和传递数据

无论是启动Activity、启动Service或者启动BroadcastReceiver等某个组件,Android使用统一的Intent来封装对某个组件的“启动意图”,以利于高层次的解耦。此外,Intent还是组件之间通信的重要媒介。

3.5.1 Intent属性

Intent是连接应用程序的三个核心组件——Activity、Service和BroadcastReceiver的桥梁,Intent负责对应用中操作的动作、动作涉及数据及附加数据进行描述。

Intent类定义在android.content.Intent包中,Intent对象包含Component、Action、Data、Category、Extra及Flag等6种属性。

1.Component

Component(组件)属性用于指定Intent的目标组件,一般由相应组件的包名与类名组合而成。指定了Component属性值之后,Intent的其他属性值都是可选的,此时该Intent就是一个显式Intent。如果不指定Component属性值,则在Androidmanifest中,通过使用IntentFilter来找到一个与之匹配的目标组件,则该Intent就是个隐式Intent。

通过setComponent()方法设置组件属性,通过setClass()、setClassName()方法设置将要启动的组件对应的类,通过getComponent()方法读取组件名。

2.Action

Action(行动)属性用来指明要实施的动作是什么,其属性值是Intent即将触发动作名称的字符串。

Intent定义了用大写字母和下画线组成的动作常量,如表3.1所示。

表3.1 常用动作常量

Action属性可通过setAction()方法来设置,通过getAction()方法来读取。

3.Data

Data(数据)属性用于完成对Intent消息中数据的封装,描述Intent动作所操作数据的URI(Uniform Resource Identifier,通用资源标识符)及MIME(多用途互联网邮件扩展)。

Type(数据类型)属性用于指定URI对应的MIME。

URI格式为:scheme://host:port/path。

获取一个URI的语句格式为:

Uri uri=Uri.parse(<字符串>);

创建一个Intent对象的语句格式为:

Intent intent=new Intent(<动作>,<内容>);

代码

Uri uri1=Uri.parse(content://contacts/1);

Intent intent=new Intent(Intent.ACTION_VIEW,uri1);

说明

在上面的代码中,uri1是一个Uri变量,其值为:content://contacts/1,指向手机联系人信息集中的第一个联系人,创建的对象intent显示标识符为“1”的联系人的详细信息。

通过setData()方法设置URI,通过getData()方法读取URI。

4.Category

Category(类别)属性用于描述目标组件额外的附加类别信息,其属性值是一个字符串。

一个Intent中可以包含多个Category。如果没有设置Category属性值,Intent会与在Intent filter中包含“android.category.DEFAULT”的Activity匹配。

Intent定义了类别常量,如表3.2所示。

表3.2 常用类别常量

通过addCategory()方法添加一个Category,通过emoveCategory()方法删除一个Category,通过getCategories()可以获取当前对象的所有Category。

5.Extra

Extra(附加信息)属性用于在多个Action之间进行数据交换。

Extra可以被当作一个Bundle对象,存入多组key-value对(键-值对),这就可以通过Intent在不同Action之间进行数据交换了。

Intent通过调用putExtras()方法来添加一个新的键-值对,而在目标Activity中调用getExtras()方法来获取Extra属性值。

6.Flag

Flag(标志)属性是一些有关系统如何启动组件的标志。指导Android系统启动一个Activity以及Activity启动后对其进行处理。

3.5.2 启动Activity

启动Activity分为显式启动和隐式启动两种。显式启动,必须在Intent中指明启动的Activity对应的类。隐式启动不指明启动的Activity对应的类,系统会根据Intent指定的规则去启动符合条件的Activity。

1.显式启动

使用Intent显式启动Activity,在创建一个Intent后,指定当前的应用程序上下文以及要启动的Activity,把创建好的这个Intent作为参数传递给startActivity()方法。

【例3.3】 显式启动Activity示例。

【解题思路】

在应用项目ExplicitStart中,包含两个Activity,一个是ExplicitStartActivity,另一个是SecondActivity。

程序默认启动的Activity是ExplicitStartActivity,进入ExplicitStartActivity界面,当用户单击“启动Activity”按钮后,程序使用Intent显式启动的Activity是SecondActivity,显式启动Activity的代码如下:

【开发步骤和程序分析】

(1)在Eclipse中创建一个ExplicitStart应用项目,包名为com.application.explicitstart。

(2)在src/com.application.explicitstart包下的ExplicitStartActivity.java文件中,使用Intent显式启动SecondActivity。

在该文件中编辑代码如下:

第19行至第21行(加黑部分),在单击事件onClick(View view)方法中,首先,使用Intent构造方法创建一个实例intent,其中的第一个参数是应用程序上下文ExplicitStartActivity,第2个参数是接收Intent的目标组件SecondActivity,这里使用显式启动方式,直接指明了需要启动的Activity,然后,使用startActivity(intent)方法显式启动SecondActivity。

【运行结果】

应用项目ExplicitStart运行结果如图3.8和图3.9所示。

图3.8 默认启动ExplicitStartActivity

图3.9 显式启动SecondActivity

2.隐式启动

隐式启动不指明启动的Activity对应的类,Android系统会根据Intent指定的属性:Action、Data、Category去启动符合条件的Activity。

隐式启动的优点是不必指明需要启动哪一个Activity,而由系统来决定,这样有利于降低组件之间的耦合度,提高Android组件的可复用性。

【例3.4】 隐式启动Activity示例。

【解题思路】

在应用项目ImplicitStart中,需要启动网页http://www.baidu.com。

在隐式启动Activity中,Intent的动作是Intent.ACTION_VIEW,数据是Web地址,使用Uri.parse(urlString)方法。Android系统在匹配Intent时,根据动作Intent.ACTION_VIEW和数据提供的是Web地址http://www.baidu.com,判定Intent需要启动具有网页浏览功能的Activity。

隐式启动Activity的代码如下:

【开发步骤和程序分析】

(1)在Eclipse中创建一个ImplicitStart应用项目,包名为com.application.implicitstart。

(2)在src/com.application.implicitstart包下的ImplicitStart.java文件中,使用Intent隐式启动Activity,提示用户在http://后输入Web地址,以启动具有网页浏览功能的Activity。

在该文件中编辑代码如下:

第22行至第25行(加黑部分),在单击事件onClick(View view)方法中,首先,使用Intent构造方法创建一个实例intent,其中第一个参数的动作是显示数据Intent.ACTION_VIEW,第2个参数的数据是Web地址,使用Uri.parse(urlString)方法,这里使用隐式启动方式,提示用户在http://后输入Web地址,当用户输入完成Web地址http://www.baidu.com并单击“浏览网页”按钮后,启动具有网页浏览功能的Activity,显示百度页面。

【运行结果】

应用项目ExplicitStart运行结果如图3.10和图3.11所示。

图3.10 提示用户在http://后输入Web地址

图3.11 隐式启动Web页面

3.5.3 Intent过滤器

Intent过滤器(Intent Filter)是一个包含Intent对象的action、data、category属性限制条件的集合,Intent Filter要检测隐式Intent的action、data、category这三个属性,其中任何一项失败,Android系统都不会传递Intent给此组件。

一个组件可以有多个Intent Filter,Intent只要通过其中的某个Intent Filter检测,就可以调用此组件。

Intent过滤器在AndroidManifest.xml文件中进行声明,Intent过滤器使用<intent-filter>子标签来进行声明。

Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有Intent Filter及其中定义的Intent属性,最终找到匹配的Intent。

(1)Action检查:一个Intent只能设置一种Action,而一个Intent Filter可以设置多个Action。如果Intent指明定了action,则目标组件的Intent Filter的action列表中就必须包含有这个action,否则不能匹配;如果Intent没有指定action,将自动通过检查。

(2)Category检查:在一个Intent Filter中,可以设置多个Category。如果Intent指定了一个或多个category,这些类别必须全部出现在组件的category列表中。

(3)Data检查:对数据的检查有两部分,一是对数据URI进行检查,一是对数据类型进行检查,对数据URI的检查包括schema、authority和path。

【例3.5】 Intent过滤器示例。

【解题思路】

在应用项目IntentFilterExample中,在AndroidManifest.xml文件<intent-filter>节点的<action>标签、<category>标签和<data>标签,分别定义Intent过滤器的“动作”“类别”和“数据”,以进行相关检查,最终找到匹配的Intent。

【开发步骤和程序分析】

(1)在Eclipse中创建一个IntentFilterExample应用项目,包名为com.application.IntentFilterExample。

(2)在AndroidManifest.xml文件中,分别定义了UserActivity1和UserActivity2的Intent过滤器,包括动作、类别和数据等。

在该文件中编辑代码如下:

① 在第10行到第17行,定义了第1个Activity及其Intent过滤器,第1个Activity名为UserActivity1,第13行到第16行是第1个Activity的Intent过滤器(加黑部分),动作为android.intent.action.MAIN,类别为android.intent.category.LAUNCHER,由此得出,第1个Activity是应用程序启动后显示的默认用户界面。

② 在第18行到第25行,定义了2个Activity及其Intent过滤器,第2个Activity名为UserActivity2,第20行到第24行是第2个Activity的Intent过滤器(加黑部分),过滤器的动作是android.intent.action.VIEW,表示根据Uri协议,以浏览的方式启动相应的Activity;类别是android.intent.category.DEFAULT,表示数据的默认动作;数据的协议部分是android:scheme=“schemodemo”,数据的主机名称部分是android:host=“com.application”。

(3)在src/com.application.IntentFilterExample包下的UserActivity1.java文件中,定义的Intent动作和数据分别与UserActivity2的Intent过滤器定义的动作、数据要求相匹配,该Intent用于启动UserActivity2。

在该文件中编辑代码如下:

① 第21行至第22行,定义了一个Intent用来启动另一个Activity,这个Intent与Activity设置的Intent过滤器是完全匹配的。

② 在第21行定义的Intent(加黑部分),动作为Intent.ACTION_VIEW,Uri是“schemodemo://edu.hrbeu/path”,其中的协议部分为“schemodemo”,主机名部分为“edu.hrbeu”,分别与Intent过滤器定义的动作、数据要求完全匹配。因此,当代码第18行定义的Intent,在Android系统与Intent过滤器列表进行匹配时,会与AndroidManifest.xml文件中UserActivity2定义的Intent过滤器完全匹配。

【运行结果】

应用项目IntentFilterExample运行结果如图3.12和图3.13所示。

图3.12 进入UserActivity1界面

图3.13 进入UserActivity2界面

3.5.4 Activity组件之间通过Intent通信

下面通过一个例题说明Activity组件之间通过Intent通信。

【例3.6】 Activity组件之间通过Intent通信举例。

两个Activity:FirstActivity和SecondActivity,界面上首次进入的Activity为FirstActivity,通过单击按钮来实现FirstActivity和SecondActivity的相互跳转,使用Intent对象实现两个Activity之间的通信。

【解题思路】

在应用项目ActivityIntentExample中,FirstActivity的布局文件为first_main.xml,SecondActivity的布局文件为second_main.xml,FirstActivity的Java代码文件为FirstActivity.java,SecondActivity的Java代码文件为SecondActivity.java。

在FirstActivity和SecondActivity组件中,使用了按钮控件,因此在相应的布局文件中,需要声明按钮控件,其标签为<Button>。

在Java代码文件中,对按钮控件设置监听,使用方法setOnClickListener(),如果监听到按钮被单击,则执行onClick()事件方法定义的操作。

在两个Activity调用中,使用显式启动,两个Activity调用需要返回信息,使用startActivityForResult()方法发送Intent对象,startActivityForResult()方法的格式如下:

如果是从A发送B,然后从B返回到A,并且需要传递信息,则在A代码中使用startActivityForResult()方法发送Intent到B,并且重写onActivityResult()方法用于处理返回的数据;在B代码中使用setResult()方法准备好回传的数据,并且使用finish()方法将打包好的数据发回给A,并运行A中的onActivityResult()部分代码。

【开发步骤和程序分析】

(1)在Eclipse中创建一个ActivityIntentExample应用项目,包名为com.application.activityintentexample,有两个Activity:FirstActivity和SecondActivity。

(2)设计布局。

布局文件为first_main.xml和second_main.xml。

在res/layout目录中,编写FirstActivity的布局文件first_main.xml,定义了一个“进入SecondActivity”按钮。

在该文件中编辑代码如下:

第7行至第10行,定义了一个按钮,其中,第7行定义该按钮的id变量名为button1,并添加到R.java文件中,为Java代码提供调用,第10行定义该按钮显示文本内容为“进入SecondActivity”。

在res/layout目录中,编写SecondActivity的布局文件second_main.xml,定义了一个“返回FirstActivity”按钮。

在该文件中编辑代码如下:

第5行至第8行,定义一个按钮,其中,第5行定义该按钮的id变量名为button2,并添加到R.java文件中,第8行定义该按钮显示文本内容为“返回FirstActivity”。

(3)在包com.application.activityintentexample下的FirstActivity.java文件中,加载first_main.xml布局文件,使用startActivityForResult()方法发送Intent1到SecondActivity,并重写onActivityResult()方法用于处理返回的数据。

在该文件中编辑代码:

① 第17行至第27行(加黑部分),创建一个监听listener1,同时定义一个onClick事件,在事件中定义了当监听到按钮被单击,则进行相应的操作。其中:

  • 第20行创建一个Intent对象,对象名为intent1,其动作值为FirstActivity.this,数据值为SecondActivity.class,这是使用Intent显式启动Activity。
  • 第23行向intent1中添加附加信息:一组键-值对的Bundle信息,其名为“firstactivity”,值为“从FirstActivity进入--”。
  • 第25行使用startActivityForResult()方法发送intent1对象,同时发送一个请求码REQUEST_CODE给SecondActivity。

② 第35行至第49行(加黑部分)重写onActivityResult()方法,通过判断请求码值和返回码值,来确定是否正确获得回传数据,如果是正确的,则取出回传数据并显示在标题栏中。

(4)在包com.application.activityintentexample下的SecondActivity.java文件中,加载second_main.xml布局文件,使用setResult()方法准备好回传的数据,并且使用finish()方法将打包好的数据发回给FirstActivity。

在该文件中编辑代码:

① 第18行至第35行(加黑部分),创建一个监听listener1,同时定义一个onClick()方法,在方法中定义了当监听到按钮被单击,则进行相应的操作。其中:

  • 第21行和第24行创建一个Bundle对象,对象名为bundle,并将一组键-值对保存到其中,其名为“store”,其值为“自SecondActivity返回--”。
  • 第26行创建一个Intent对象,对象名为intent2。
  • 第28行向intent2中添加附加信息,此附加信息是已保存在bundle中的键-值对信息。
  • 第30行是打包回传的数据,包括返回码值RESULT_OK和intent2对象。
  • 第33行将打包好的数据发回给FirstActivity,并且运行FirstActivity.java中onActivityResult()里面的代码。

② 第40行至第45行(加黑部分)从FirstActivity的intent1对象中取出附加信息赋值给extras,如果其键-值对非空,则取出名为“firstactivity”的对应值给字符串变量data,并将data的值显示在标题栏中。

(5)编写根目录下的AndroidManifest.xml文件的代码,增加Activity组件SecondActivity的声明。

【运行结果】

应用项目IntentFilterExample运行结果如图3.14~图3.16所示。

图3.14 首次进入“查看信息内容页面”

图3.15 单击“进入”按钮后进入“显示信息内容页面”

图3.16 单击“返回”按钮后返回“查看信息内容页面”