Android开发实战:从学习到产品
上QQ阅读APP看书,第一时间看更新

2.3 Intent与Activity之间的跳转

一个Android应用不可能只有一个页面,也就意味着不可能只有一个Activity。既然存在多个Activity,那么多个Activity之间是如何通信的呢?在Android中提供了Intent机制来完成组件之间的通信,本节只讲述如何使用Intent完成Activity之间的通信,其他组件之间的通信等讲述到具体内容时再做叙述。

2.3.1 Intent简介

Intent的中文意思是“意图,意向”,在Android中提供了Intent机制来协助应用间的交互与通信,Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则负责根据此Intent的描述找到对应的组件,将Intent传递给调用的组件,并完成组件的调用。Intent不仅可用于应用程序之间,也可用于应用程序内部的Activity/Service之间的交互。因此,可以将Intent理解为不同组件之间通信的“媒介”,专门提供组件互相调用的相关信息。

归纳起来,Intent的应用场合主要有以下3种:

• 启动一个Activity。第一种方法是Activity调用startActivity(Intent intent)直接开启一个Activity,第二种方法是通过Activity调用startActivityForResult(Intent intent,int requestCode)启动一个带请求码的Activity,当该Activity结束时将回调原Activity的onActivityResult()方法,并返回一个结果码。

• 启动一个Service,等讲述到具体内容时再做叙述。

• 启动一个Broadcast,等讲述到具体内容时再做叙述。

当使用一个Intent进行组件通信时,需要先实例化一个Intent对象,这时需要设置Intent的属性。Intent的属性设置包括Action(要执行的动作)、Data(执行动作所操作的数据)、Type(显式指定Intent的数据类型)、Category(执行动作的附加信息)、Component(指定Intent目标组件的类名称)、Extras(其他所有附加信息的集合)。下面具体叙述这些属性。

• Action:在SDK中定义了一系列标准动作,其中的一部分如图2-4所示。

图2-4 SDK的部分标准动作

其中,ACTION_CALL表示调用拨打电话的应用,ACTION_EDIT表示调用编辑器,ACTION_SYNC表示同步数据。

• Data:在Intent中,Data使用指向数据的URI来表示。比如,在联系人应用中,指向联系人列表的URI是content://contacts/people/。

• Type:对于不同的动作,其URI数据的类型是不同的。通常,Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性可以强制采用显式指定的类型。

• Category:Category表示执行动作的附加信息。比如,当我们想要让所执行的动作被接收后,作为顶级应用而位于其他所有应用的最上层,并可以使用附加信息LAUNCHER_CATEGORY来实现。

• Component:用于指定Intent目标组件的类名称。通常,Android会根据Intent中所包含的其他属性信息(比如Action、Data/Type、Category)进行查找,并找到一个与之匹配的目标组件。但是,如果我们设置了Component属性,明确指定了Intent目标组件的类名称,那么上述查找过程将不需要执行。

• Extras:可以为组件提供扩展信息。

另外,在使用Intent时,根据是否明确指定Intent对象的接收者,可以分为两种情况。一种是显式的Intent,即在构造Intent对象时就指定接收者;另一种是隐式的Intent,即在构造Intent对象时并不指定接收者。

对于显式的Intent来说,Android不需要解析Intent,因为目标组件已经很明确。对于隐式的Intent来说,Android需要对其进行解析,通过解析将Intent映射给可以处理该Intent的Activity、Service或Broadcast。

Intent解析机制是通过查找注册在AndroidManifest.xml文件中的所有IntentFilter以及IntentFilter所定义的Intent找到最匹配的Intent。

在解析过程中,Android通过判断Intent的Action、Type、Category这3个属性找出最匹配的Intent,具体的判断方法如下:

(1)如果Intent指明了Action,那么目标组件IntentFilter的Action列表中就必须包含有这个Action,否则不能匹配。

(2)如果Intent没有提供Type,那么系统将从Data中得到数据类型。目标组件的数据类型列表中必须包含Intent的数据类型,否则不能匹配。

(3)如果Intent中的数据不是content: URI,而且Intent也没有明确指定它的Type,就将根据Intent中数据的scheme(比如http:或者mailto:)进行匹配。同理,Intent的scheme必须出现在目标组件的scheme列表中,否则不能匹配。

(4)如果Intent指定了一个或多个Category,那么这些类别必须全部出现在目标组件的类别列表中,否则不能匹配。

2.3.2 使用Intent进行Activity跳转

下面我们通过新建一个包含两个Activity的Android工程来实现应用程序内部之间Activity的跳转。除了系统生成的MainActivity,我们再手动新建一个SecondaryActivity。建立Activity的方法是到需要的包下右击,然后单击new→Activity,选择需要的Activity类型即可。此时Android Studio会自动在AndroidManifest.xml中注册,无须手动注册。

1.显式意图

显式意图要求必须知道被激活组件的包和class。如下代码便实现了从MainActivity跳转到SecondaryActivity,并向SecondaryActivity中传递一个字符串的功能。

对activity_main.xml做一些改造:

这里为了跳转方便,使用了一个button按钮,其中findViewById方法已经在前文讲过,是获得View控件对象的方法。button.setOnClickListener()是事件处理的内容,这里不做详细解释,读者只需要像文中一样使用即可,之后重点讲解如何进行事件处理的内容,现在重点关注gotoSecondaryActivity方法。gotoSecondaryActivity方法中的代码使用的各种方法在上一部分有讲述,而注释部分也说明了它们的作用,此处不再分析。仔细分析会发现,使用Intent进行Activity跳转分为两步,第一步构建Intent的对象,第二步调用startActivity方法开启第二个Activity。除了上面给出的构建Intent的对象方法外,还可以直接在实例化Intent对象时在Intent的构造函数中传入第二个Activity类,或者使用setComponent方法传入。而向第二个Activity传递数据除了上面直接用intent对象调用putExtra方法外,还可以使用如下方法:

上面给出了如何实现跳转的方法,那么如何在SecondaryActivity中接收数据呢?代码如下:

为了方便辨识,改造activity_secondary.xml文件:

接收前一个Activity传递来的参数也需要使用Intent。具体来说就是创建Intent对象,使用intent对象来获得一个bundle实例,传递的参数就存储在bundle实例中。这里为了方便使用Log来打印接收过来的数据。

运行程序,如图2-5所示。

点击按钮就会跳转到第二个Activity,如图2-6所示。

图2-5 程序运行显示效果

图2-6 第二个Activity显示的界面

查看Log会发现有如下记录:

这样就说明应用成功地执行了两个Activity之间的跳转,并且在两个Activity之间进行了数据的传输。

2.隐式意图

隐式意图和显式意图不同,它可以不知道被激活组件的包和class,只通过指定action就进行跳转。同时,被激活的组件必须是在AndroidManifest.xml文件中注册的,注册的方式如下:

其中的重点就是加入:

除此之外,和显式意图相比,只需要将gotoSecondaryActivity方法中的toSecondary.setClass(this,SecondaryActivity.class)改为toSecondary.setAction("com.buaa.SecondaryActivity")即可,这里的参数内容需要和AndroidManifest.xml文件中注册的内容一致,其他部分都不需要改动。

测试的结果也是一样的。读者可能会问,它们如此相像,为什么还需要两种呢?简单来说就是隐式意图可以更好地让代码解耦,使不同模块之间的耦合性更低。另外,如果一个Activity想要启动另一个应用中的Activity就只能使用隐式意图。

3.带回调方法的意图

有时我们需要通过定义在MainActivity中的某一控件来启动SecondaryActivity,并且当SecondaryActivity结束时返给MainActivity一个执行结果。要实现上述功能,只需要完成以下3步即可。

步骤01 在MainActivity中实现向SecondaryActivity发送带请求码的意图,具体实现方法如下(改造MainActivity中的gotoSecondaryActivity方法即可):

步骤02 在SecondaryActivity中接收toSecondary _request,并向意图中填充要返给MainActivity的内容,最后还需要设置一个返回码。加入一个button按钮,并实现点击时结束SecondaryActivity。改造SecondaryActivity:

在activity_secondary.xml文件中加入一个button按钮:

步骤03 结束SecondaryActivity时将返回到MainActivity界面。此时,MainActivity中的onActivityResult()方法将被回调。在本示例中,该方法的具体实现如下:

经过上述的3个步骤,运行应用,第二个Activity中会出现一个按钮,点击这个按钮就回到了第一个Activity中。此时在控制台上打开日志,会发现有如下记录:

这样应用就完成了一次带回调方法的跳转。

4.跳转中对象参数的传递

在Android开发中,有时多个Activity之间需要进行对象的传递,使用Intent也可以完成这一功能。具体来说就是将显式跳转或者隐式跳转中的例子做如下修改。

先创建一个User类:

再改造MainActivity中的gotoSecondaryActivity方法:

最后改造SecondaryActivity中接收参数的内容为:

通过上面的改造,一个在多个Activity之间传递对象参数的应用就完成了。运行应用,点击按钮即可进入第二个Activity,并在日志中出现如下记录:

和我们的预期一致,这说明通过Intent在多个Activity之间传递对象是可行的。

Activity和Intent是Android中非常重要的内容,希望读者在学习完之后多做练习,多加揣摩。