2.4 Android系统四大组件
之前我们已经学习了Android应用框架的四大核心要点,对Android的应用框架有了一个总体性的了解,接下来我们要学习Android应用程序中的四个重要组成部分,也就是我们一般所说的“应用组件”。在前面讲解四大核心要点的篇幅中,我们曾经提到了控件(View控件)的概念,现在我们再来学习一下Android应用框架中的组件的概念。那么何谓组件呢?顾名思义,组件当然要比控件复杂,简而言之,组件是用于工业化组装的部件。要达到组件的标准,必须符合三个要求,以下我们结合Android应用框架讨论如下。
1.有统一标准
这点应该是形成组件的前提条件,试问,组件如果没有标准,如何组装?在这点上,Android应用框架中定义了很多标准接口,满足了组件间的各种接口需求;换一种说法,整合Android系统都是按照接口规范设计出来的。
2.可独立部署
组件应该是独立的,每个组件都有自成一套的功能体系,否则就没有形成组件的必要。比如每个Activity都是可以独立构造的,使用Activity组件,我们可以完成一个包含许多复杂功能的界面;而使用Service,我们可以操作一个独立的后台进程等。
3.可组装整合
可组装是组件最重要的特性,一个完整的Android应用必然是若干个系统组件构成的,这就要求组件必须是能组装在一起的,至于如何组装,我们会在后面的章节中结合实例进行介绍。
通常来讲,Android应用框架中包含了四大组件:活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)和内容提供者(Content Provider)。这四大组件除了具有前面所提到的三个特点之外,还有着相同的显著特点,那就是它们都可以在Android的基础配置文件,即AndroidManifest.xml中进行配置。下面我们就来学习Android系统四大组件的基本概念和使用方法。
2.4.1 活动(Activity)
在2.3.1节中,我们已经介绍了Android活动(Activity)的生命周期以及基本行为,大家应该对Activity的概念有了一定的了解。此外,Activity同时还是Android系统四大组件中的一员,因此,本节将着重介绍Activity作为组件的一般声明方法。
说到Activity的声明方法,我们必须先了解Android全局配置文件AndroidManifest.xml的基础知识。每个Android应用项目都会有自己的全局配置文件,该文件包含了应用的系统常量、系统权限以及所含组件等配置信息。配置使用范例如代码清单2-4所示。
代码清单 2-4
<manifest ...> <application ...> <activity android:name="com.app.android.HelloActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:screenOrientation="landscape"> <intent-filter> ... </intent-filter> <intent-filter> ... </intent-filter> </activity> ... </application> <uses-permission .../> <uses-permission .../> ... </manifest>
从上述配置使用范例中,我们可以看到在AndroidManifest.xml配置文件范例的根元素<manifest/>下面有两种标签,即<application/>和<uses-permission/>元素。前者是应用配置的根元素,而后者则用于配置应用的权限。这里顺便说一下,每个Android应用都必须事先声明应用需要的权限,比如是否需要网络、是否需要使用摄像头或者是否需要打开卫星定位(GPS)等。而用户在安装该应用之前,系统会先提示用户是否允许该应用使用这些权限,如果用户觉得应用不安全便可以选择不安装,这在一定程度上也提高了Android系统的安全性。
另外,我们还可以看到,在以上配制文件中的<application/>元素里面含有一个或者若干个<activity/>元素,这个就是我们需要重点了解的Activity标签了。首先,我们来看一下该标签内部的一些常用的配置选项。
·android:name:表示该Activity对应的类的名称,在代码清单2-4中,我们就定义了一个Activity,它的具体类包名就是“com.app.android.HelloActivity”。
·android:theme:表示Activity所使用的主题,在Android系统中是允许我们自定义主题的(这部分的内容我们在后面章节的实例中会介绍到),在代码清单2-4中,使用的是默认主题“@android:style/Theme.NoTitleBar.Fullscreen”,也就是全屏模式。
·android:launchMode:Activity的行为模式,之前在2.3.4节中介绍过该标签的4种选项,即与任务行为有关的Standard、singleTop、singleTask以及singleInstance。
·android:screenOrientation:表示屏幕的方向,在代码清单2-4中,landscape表示的是该Activity是横屏显示的,如果改成portrait的话,则就变成竖屏显示。
当然,Activity标签可配置的选项远不止以上这些,更详细的使用说明可以参考7.1.2节的内容,使用范例可参考代码清单7-11。此外,从上面的配制文件中我们还可以看到不止一个<intent-filter>元素。关于这点,实际上,前面我们已经介绍过消息过滤器的用法,如果大家有疑问的话,可以参考2.3.2节中与消息(Intent)相关的内容。
另外,Activity在应用开发中被用做控制界面的逻辑,也就是MVC中的Controller控制器,关于Android应用中MVC的概念可参考5.2.3节中的内容。开发者可以根据需要,在Activity的生命周期方法中添加不同的逻辑来控制对应应用界面的显示、动作和响应等,而Activity类的具体用法和代码示例我们可以在本书第7章的“微博实例”代码中学习到。
2.4.2 服务(Service)
Android系统中的Service服务组件和Windows系统中的后台服务有点类似,这个概念应该很容易理解,比如,我们在退出某些聊天软件之后还是可以接收到好友发来的消息,就是使用Android服务组件来实现的。此外,如果需要在应用后台运行某些程序,Service服务组件也绝对是最佳的选择。另外,值得注意的是,Service和之前的Activity一样,也有自己的生命周期,但是,Service的生命周期相对简单一些,如图2-6所示。
从图2-6中我们可以看出Android服务(Service)主要有以下两种运行模式。
·独立运行模式:我们一般通过“startService()”方法来启动一个独立的服务,在这种模式下,该服务不会返回任何信息给启动它的进程,进程的动作结束后会自动结束。比如,浏览器下载就属于独立服务。
·绑定运行模式:与独立服务不同,绑定服务是与启动它的应用绑定在一起的,当该应用结束的时候,绑定服务也会停止。另外,这种服务可以和应用中的其他模块进行信息交互,甚至进行进程通信(IPC)。
图2-6 Service生命周期
与Activity类似,onCreate和onDestroy分别是Android服务创建和销毁过程中的回调方法。与独立运行模式相比,绑定运行模式中多出来onBind和onUnbind两个函数,分别是服务绑定和解绑过程的回调方法。在Android应用开发的时候,我们通常会使用startService方法来开启Service服务。另外,在应用开发的时候千万别忘了我们必须事先在全局配置文件中进行如下声明,如代码清单2-5所示。
代码清单 2-5
<application ...> <service android:name=".HelloService"/> <activity ...> ... </activity> </application>
理解Android服务(Service)时要特别注意,千万不要盲目认为服务是一个独立的进程或者线程。实际上,它和应用程序的进程之间存在着复杂的联系,所以如果我们需要在Service中做一些耗时操作的话,必须新起一个线程并使用消息处理器Handler来处理消息。另外,Android服务的进程间通信(IPC)功能还涉及AIDL(Android Interface Definition Language,Android接口定义语言),有兴趣的话尽管去了解一下。关于Service的具体使用实例,大家可以先去看看Android SDK中API Demos里面的RemoteService实现,本书后面的实例中我们也会穿插介绍。
小贴士:Handler是消息处理器,用于接受子线程的消息进行处理并配合主线程更新UI界面,具体内容可参考5.2.2节中界面基础类BaseUi的相关内容。
在Android系统中,Service服务类的使用方法比较简单,执行Service对象的start方法就可以开启一个服务。实际上,第7章的“微博实例”中也有与Service服务相关的代码实例,请参考7.5.4节。
2.4.3 广播接收器(Broadcast Receiver)
广播接收器(Broadcast Receiver)是Android系统的重要组件之一,可以用来接收其他应用发出来的广播,这样不仅增强了Android系统的交互性,而且能在一定程度上提高用户的操作体验。比如,你在把玩应用或者游戏的同时也可以随时接收一条短信或者一个电话,或者你在打开网页的同时还可以接收短信验证码等。
广播接收器的使用也很简单,和其他组件的步骤一样:先声明,再调用。代码清单2-6就是一个声明广播接收器的例子。
代码清单 2-6
<application ...> <receiver android:name=".HelloReceiver"> <intent-filter> <action android:name="com.app.basicreceiver.helloreceiver"/> </intent-filter> </receiver> <activity ...> ... </activity> </application>
这里我们定义了一个名为HelloReceiver的广播接收器。这个类里面只有一个onReceive方法,里面我们可以定义需要的操作。使用的时候,我们可以在Activity中直接使用sendBroadcast方法来发送广播消息,这样HelloReceiver就会接收到我们发送的信息并进行相应的处理。这里需要注意的是,广播接收器也是在应用主线程里面的,所以我们不能在这里做一些耗时的操作,如果需要的话,可以新开线程来解决。发送广播消息的范例如代码清单2-7所示。
代码清单 2-7
... Intent intent = new Intent("com.app.basicreceiver.hello"); sendBroadcast(intent); ...
而接收消息的使用范例,也就是广播接收器类HelloReceiver的逻辑实现,我们可以参考代码清单2-8。
代码清单 2-8
public class HelloReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Toast.makeText(context, "Receive Action : " + action, 1000).show(); } }
另外,我们需要了解,Android系统中的广播消息是有等级的,可分为普通广播(Normal Broadcasts)和有序广播(Ordered Broadcasts)两种。前者是完全异步的,可以被所有的接收者接收到,而且接收者无法终止广播的传播;而有序广播则是按照接收者的优先级别被依次接收到。优先级别取决于intent-filter元素的android:priority属性,数越大,优先级越高。至于使用,我们通常会在onResume事件中通过registerReceiver进行注册,在onPause等事件中注销,这种方式使其能够在运行期间保持对相关事件的关注。常见的广播事件有:短信广播、电量通知广播等。
2.4.4 内容提供者(Content Provider)
在Android应用中,我们可以使用显式消息(Explicit Intent)来直接访问其他应用的Activity,但是这仅限于Activity的范畴;如果需要使用其他应用的数据,还需要用到另外一种组件,这就是所谓的内容提供者(Content Provider)。
顾名思义,内容提供者就是Android应用框架提供的应用之间的数据提供和交换方案,它为所有的应用开了一扇窗,应用可以使用它对外提供数据。每个Content Provider类都使用URI(Universal Resource Identifier,通用资源标识符)作为独立的标识,格式如:content://xxx。其格式类似于REST,但是比REST更灵活,因为在调用接口的时候还可以添加Projection、Selection、OrderBy等参数,结果以Cursor的模式返回。Content Provider的声明写法非常简单,示例可参考代码清单2-9。
代码清单 2-9
<application ...> <provider android:name="com.app.android.HelloProvider" android:authorities="com.app.android.HelloProvider"/> <activity ...> ... </activity> </application>
关于Content Provider的类实现,我们只需要继承ContentProvider接口并实现其中的抽象方法即可,这几个方法有点类似于数据操作对象DAO的抽象方法,其中包括insert、delete、query和update这些常见的“增删查改”的接口方法。对于具体的数据存储来说,一般会使用Android的内置数据库SQLite,当然也可以采用文件或者其他形式的混合数据来实现。关于Android系统中的数据存储我们会在2.6节中介绍。
我们在使用上述四大组件的时候还需要注意的是:实际上,Service和Content Provider都可用于IPC(Inter-Process Communication,进程间通信),也就是在多个应用之间进行数据交换。Service可以是异步的,而Content Provider则是同步的。在某些情况下,在设计的时候我们要考虑到性能问题。当然,Android也提供了一个AsyncQueryHandler帮助异步访问Content Provider。关于以上四大组件的具体使用,我们会在后面的章节中穿插介绍。
另外,与Content Provider配合使用的还有Content Resolver,即内容处理器。前面也提到了Content Provider是以数据库接口的方式将数据提供出去,那么Content Resolver也将采用类似的数据库操作来从Content Provider中获取数据,而获取数据就需要使用query接口。和Content Provider类似,Content Resolver也需要使用URI的方式来获取对应的内容,其使用范例可参考7.3.2节中提到的HttpUtil类的相关代码(代码清单7-34)。