第
章


应用程序

学习目标

本章主要介绍Android 项目中的目录结构、
AndroidManifest.xml文件等项目构成文件,同时,对
Android 应用程序组件Activity、Service、Intent和
IntentFilter、BroadcastReceiver、ContentProvider进行介
绍,并就Android程序生命周期和组件生命周期详细讲解。
通过本章的内容,读者能够学习以下知识要点。

(1)Android项目文件及应用程序。

(2)Android系统的进程优先级的变化方式。

(3)Android系统的基本组件。

(4)Android程序生命周期及Activity的生命周期中

各状态的变化关系。
(5)Activity事件回调函数的作用和调用顺序。
(6)Android应用程序的调试方法和工具。

在Android的应用过程中,对于初学者而言,通常会混
淆Android项目和Android应用程序的概念。它们之间既
有区别又有联系,要创建Android应用,必须先创建
Android项目,然后才能在项目中创建Android应用程序, 
下面就Android项目的构成和Android应用程序的组成进
行介绍。

3.nrid项目的构成
1 
Ado

1.目录结构
3.1 
在建立新项目的过程中,ADT 会自动建立一些目录和
文件,这些目录和文件有其固定的作用,有的允许修改,有
的不能修改。AndroidStudio环境下和Eclipse环境下创
建Android项目的目录结构不同,本书针对Android 


第 3 章 Android应用程序
71 
Studio环境下新建项目Hello 工程进行介绍,项目工程结构包含.gradle、.idea、app、
gradle、.gitignore、build.gradle、gradle.properties、gradlew、gradlew.bat、local.properties、
Hello.iml、setting.gradle,如图3-1所示,下面逐一介绍。
图3-1 Android Studio 环境下Android 工程目录结构
.gradle目录和.idea目录:分别为AndroidStudio集成环境下自动化构建工具gradle 
(基于JVM 的构建工具)产生的文件和开发工具idea(其全称是IntelliJIDEA,开发集成工
具)产生的文件,存放项目的配置信息,包括历史记录,版本控制信息等。
iml是intellijidea自动创建的模块文件,存储模块开发相关的信息、依赖信息等。
app目录:包含build目录、libs目录、src目录、.gitignore文件、build.gradle文件、app. 
iml文件、proguard-rules.pro文件。
. build目录包含了编译时自动生成的文件。
.libs目录存放项目需要的第三方JAR包。
.src目录是源代码目录,所有允许用户修改的Java文件和用户自己添加的Java文
件都保存在这个目录中的main目录下。
. .gitignore是git源码管理文件ignore,作用是将app模块内指定的目录或文件排除
在代码版本控制之外。
. build.gradle是app模块中的gradle的配置文件,另外一个build.gradle文件位于
工程根目录下。
.app.iml是idea创建的android模块文件,保存模块路径、依赖关系及其他配置
信息。
. proguard-rules.pro是代码混淆配置模板。
gradle.properties:运行环境配置文件,在它里面可以定义全局的属性,然后在各个模
块的build.gradle中直接引用。
gradlew:是Linux下的shell脚本,Linux用户可以通过它来执行gradle任务。
gradlew.bat:是Windows下的可执行脚本,用户可以通过它执行gradle 任务,与
gradle文件夹配合使用。
local.properties:是项目工程的本地配置。该文件是在工程编译时自动生成的,用于
描述开发者本机的环境配置,如SDK的本地路径、NDK的本地路径等。
setting.gradle:配置同时编译的模块。初始内容为include':app',表示只编译app

Android应用开发案例教程(第2版) 
72 
模块。
Hello.iml:工程的配置文件。
3.1.2 AndroidManifest.xml文件简介
AndroidManifest.xml是XML格式的Android程序声明文件,是全局描述文件,包含
了Android系统运行Android程序前所必须掌握的重要信息,这些信息包含应用程序名
称、图标、包名称、模块组成、授权和SDK最低版本等。创建的每个Android项目应用程序
必须在根目录下包含一个AndroidManifest.xml工程文件。
AndroidManifest.xml文件的代码: 
1. <? xml version="1.0" encoding="utf-8"? > 
2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
3. package="com.hisoft" 
4. > 
5. <application 
6. android:allowBackup="true" 
7. android:icon="@mipmap/ic_launcher" 
8. android:label="@string/app_name" 
9. android:roundIcon="@mipmap/ic_launcher_round" 
10. android:supportsRtl="true" 
11. android:theme="@style/AppTheme"> 
12. <activity android:name=".HelloWorldActivity"> 
13. <intent-filter> 
14. <action android:name="android.intent.action.MAIN" /> 
15. <category android:name="android.intent.category.LAUNCHER" /> 
16. </intent-filter> 
17. </activity> 
18. </application> 
19. </manifest> 
AndroidManifest.xml文件的根元素是manifest,包含了xmlns:android、package、
android:versionCode和android:versionName共4个属性。
xmlns:android定义了Android的命名空间,值为http://schemas.android.com/apk/ 
res/android。
package定义了应用程序的包名称。
manifest元素仅能包含一个application元素,application元素中能够声明Android程
序中最重要的4个组成部分,包括Activity、Service、BroadcastReceiver和ContentProvider,所定
义的属性将影响所有组成部分。
第6行属性android:allowBackup定义是否允许备份应用的数据,默认是true,表示
用户可通过adbbackup和adbrestore来对应用数据进行备份和恢复。实际中为安全起
见,建议开发者将allowBackup标志值设置为false来关闭应用程序的备份和恢复功能,以
免造成信息泄露。

第 3章
Android应用程序

第7行属性android:icon定义了Android应用程序的图标,其中@mipmap/ic_ 
launcher是一种资源引用方式,表示资源类型是图像,资源名称为ic_launcher,对应的资源
文件为res/mipmap目录下的ic_launcher.n。

pg

第8行属性android:label则定义了Android应用程序的标签名称。@string/app_ 
name同样属于资源引用,表示资源类型是字符串,资源名称为app_name,资源保存在res/ 
vaus目录下的srnsxml文件中。

letig.
第9行属性android:roundIcon与第7行属性类似,不同的是其引用了圆形图片。
第10行属性android:supportsRtl表示application是否支持从右到左(RTL,right

的布局。如果设置为tn设置应为17(And2以后)或更

to-left) rue,targetSdkVersioroid4.
高,各种RTL的API将被激活,应用程序可以显示RTL布局。如果taretSdkVersion设
置为16或更低,RTL的设置将被忽略。
g

第11行属性android:theme定义主题风格,其中的@style/AppTheme是引用的res/

le/y.l中的主题样式。

vausstlesxm
第12行属性android:name定义了实现Activity类的名称,可以是完整的类名称,也
可以是简化后的类名称。
aciiy元素是对Actvty子类的声明,必须在AndodMaietxml文件中声明的

tvtiirinfs.
Activity才能在用户界面中显示。
inetfler中声明了两个子元素aton和ctgritn-itr使HeloAndod程

tn-itciaeoy,netfleri
序在启动时,将.HeloWorldActivity这个Activity作为默认启动模块。

3.1.3 
build目录
在上文的目录结构中已讲述,在app目录下build/generated/not_namespaced_r_clas 
sources/debug/procesDebugResources/r/包名的目录下只存放一个由ADT自动生成, 并(_) 不需要人工修改的R.aa文件。

jvR.aa文件包含对drale、aot和vaus目录内的资源的引用指针,Andrid程

jvawblyuleo
序能够直接通过R类引用目录中的资源。
Android系统中资源引用有两种方式:一种是在代码中引用资源;另一种是在资源中
引用资源。
代码中引用资源需要使用资源的ID,可以通过[R.eore_yp.eore_ame]或

rsuctersucn[adod.rsuc_ypersuc_ame]

nriR.eoret.eoren获取。

resource_type代表资源类型,也就是R类中的内部类名称。

resource_name代表资源名称,对应资源的文件名或在XML文件中定义的资源名称

属性。
ackagtype:n资源中引用资源,引用格式:@[pe:]ame 。

.@表示对资源的引用。
.
jpackage是包名称,如果在相同的包,package则可以省略。
R.ava文件不能手工修改,如果向资源目录中增加或删除资源文件,则需要在工程名
称上右击,选择Rh来更新R.a文件中的代码。

efresjavR类包含的几个内部类,分别与资源类型相对应,资源ID便保存在这些内部类中,例


Android应用开发案例教程(第2版) 
74 
如子类drawable表示图像资源,内部的静态变量icon 表示资源名称,其资源ID 为
0x7f020000。一般情况下,资源名称与资源文件名相同。
3.1.4 res目录
在Androidstudio环境下,res目录位于app/src/main/下,res目录中包含了9个子目
录,介绍如下。
drawable目录:存放背景文件ic_launcher_background.xml文件,使用SVG 格式绘
制出带纹理的底图。
drawable-v24目录:存放前景文件ic_launcher_foreground.xml文件,使用SVG 格式
绘制出一个带有投影效果的Android机器人Logo。
layout目录:用来保存与用户界面相关的布局文件,这些布局文件都是XML文件,默
认存放的是activity_main.xml文件。
mipmap-anydpi-v26目录:该文件夹用来存放自适应图标,用ic_launcher.xml和ic_ 
launcher_round.xml两个文件表示,分别资源引用drawable目录中的。
mipmap-hdpi目录:里面主要放高分辨率的图片,如WVGA(480×800)、FWVGA 
(480×854),默认存放的是ic_launcher.png图片。
mipmap-mdpi目录:里面主要放中等分辨率的图片,如HVGA(320×480)。
mipmap-xhdpi目录:里面主要放超高分辨率的图片,如QVGA(720×1280)。
mipmap-xxhdpi目录:里面主要放超超高分辨率的图片,如QVGA(1080×1920)。
mipmap-xxxhdpi目录:里面主要放超高分辨率的图片,如2K屏幕。
上述mipmap各个文件夹代表像素密度(dpi),mipmap仅用于应用启动图标,可以根
据不同分辨率进行优化。其他的图标资源,要放到drawable文件夹中。系统会根据机器
的分辨率来分别到这几个文件夹里面去找对应的图片。
valuse目录:保存文件颜色、风格、主题和字符串等,默认存放的是strings.xml文件。
上述activity_main.xml文件,是界面布局文件,利用XML语言描述的用户界面界面
布局的相关内容将在后续章节用户界面设计中进行详细介绍。
(1)activity_main.xml文件代码: 
1. <? xml version="1.0" encoding="utf-8"? > 
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
3. android:orientation="vertical" 
4. android:layout_width="fill_parent" 
5. android:layout_height="fill_parent" 
6. > 
7. <TextView 
8. android:layout_width="fill_parent" 
9. android:layout_height="wrap_content" 
10. android:text="@string/hello" 
11. /> 
12. </LinearLayout>

第 3 章 Android应用程序
75 
第7行的代码说明在界面中使用TextView 控件,TextView 控件主要用来显示字符
串文本。
第10行代码说明TextView控件需要显示的字符串,非常明显,@string/hello是对资
源的引用。
(2)strings.xml文件代码: 
<? xml version="1.0" encoding="utf-8"? > 
<resources> 
<string name="hello">Hello World, HelloWorldActivity!</string> 
<string name="app_name">HelloWorld</string> 
</resources> 
通过strings.xml文件的第3行代码分析,在TextView 控件中显示的字符串应是
HelloWorld,HelloAndroidActivity! 
如果修改strings.xml文件的第3行代码的内容,重新编译、运行后,模拟器中显示的
结果也应该随之更改。
3.2 Android应用程序组成
3.2.1 Android应用程序概述 
Android应用程序是在Android应用框架之上,由一些系统自带和用户创建的应用程
图3-2 Android 应用程序组件
序组成。组件是可以调用的基本功能模块,Android应用程
序就是由组件组成的。一个Android的应用程序通常包含
4个核心组件和一个Intent,4 个核心组件分别是: 
Activity、Service、BroadcaseReceiver 和ContentProvider。
Intent是组件之间进行通信的载体,它不仅可以在同一个应
用中起传递信息的作用,还可以在不同的应用间传递信息, 
如图3-2所示。
3.2.2 Activity组件
Activity是Android程序的呈现层,显示可视化的用户界面,并接收与用户交互所产生
的界面事件。一个Android应用程序可以包含一个或多个Activity,其中一个作为main 
Activity用于启动显示,一般在程序启动后会呈现一个Activity,用于提示用户程序已经正
常启动。
Activity通过View管理用户界面UI。View 绘制用户界面UI与处理用户界面事件
(UIevent),View可通过XML描述定义,也可在代码中生成。一般情况下,Android建议
将UI设计和逻辑分离,AndroidUI设计类似swing,通过布局(layout)组织UI组件。
在应用程序中,每一个Activity都是一个单独的类,继承实现了Activity基础父类,这
个类通过它的方法设置并显示由Views组成的用户界面UI,并接受、响应与用户交互产生
的界面事件。Activity通过startActivity或startActivityForResult启动另外的Activity。

Android应用开发案例教程(第2版) 
76 
在应用程序中一个Activity在界面上的表现形式通常有:全屏窗体、非全屏悬浮窗
体、对话框等。
3.2.3 Service组件
Service常用于没有用户界面,但需要长时间在后台运行的应用。与应用程序的其他
模块(例如activity)一同运行于主线程中。一般通过startService或bindService方法创建
Service,通过stopService或stopSelf方法终止Service。通常情况下,都在Activity中启动
和终止Service。
在Android应用中,Service的典型应用是音乐播放器。在一个媒体播放器程序中,大
概要有一个或多个活动(Activity)来供用户选择歌曲并播放它。然而,音乐的回放就不能
使用活动(Activity)了,因为用户希望能够切换到其他界面时音乐继续播放。这种情况下, 
媒体播放器活动(Activity)要用Context.startService()启动一个服务来在后台运行保持音
乐的播放。系统将保持这个音乐回放服务的运行直到它结束。需要注意,你要用Context. 
bindService()方法连接服务(如果它没有运行,要先启动它)。当连接到服务后,你可以通
过服务暴露的一个接口和它通信。对于音乐服务,它支持暂停、倒带、重放等功能。
3.2.4 Intent和IntentFilter组件
1.Intent 
Android中提供了Intent机制来协助应用间的交互与通信,Intent负责对应用中一次
操作的动作、动作涉及数据、附加数据进行描述。Android则根据此Intent的描述,负责找
到对应的组件,将Intent传递给调用的组件,并完成组件的调用。Intent不仅可用于应用
程序之间,也可用于应用程序内部的Activity/Service之间的交互。因此,Intent在这里起着一
个媒体中介的作用,类似于消息、事件通知,它充当Activity、Service、broadcastReceiver之间联
系的桥梁,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。具体详
述见8.1.2节。
通常Intent分为显式和隐式两类。显式的Intent,就是指定了组件名字,由程序指定
具体的目标组件来处理。即在构造Intent对象时就指定接收者,指定了一个明确的组件
(setComponent或setClass)来使用处理Intent。 
Intent intent = new Intent( 
getApplicationContext(), 
Test.class 
); 
startActivity(intent); 
特别注意:被启动的Activity需要在AndroidManifest.xml中进行定义。
隐式的Intent就是没有指定Intent的组件名字,没有指定明确的组件来处理该
Intent。使用这种方式时,需要让Intent与应用中的IntentFilter描述表相匹配。需要
Android根据Intent中的Action、data、Category等来解析匹配。由系统接收调用并决定
如何处理,即Intent的发送者在构造Intent对象时,并不知道也不关心接收者是谁,有利于

第 3 章 Android应用程序
77 
降低发送者和接收者之间的耦合。如:startActivity(newIntent(Intent.ACTION_ 
DIAL));。 
Intent intent = new Intent(); 
intent.setAction("test.intent.IntentTest"); 
startActivity(intent); 
目标组件(Activity、Service、BroadcastReceiver)是通过设置它们的IntentFilter来界
定其处理的Intent。如果一个组件没有定义IntentFilter,那么它只能接受处理显式的
Intent,只有定义了IntentFilter的组件才能同时处理隐式和显式的Intent。
一个Intent对象包含了很多数据的信息,由6部分组成: 
. Action———要执行的动作。
. Data———执行动作要操作的数据。
. Category———被执行动作的附加信息。
. Extras———其他所有附加信息的集合。
. Type———显式指定Intent的数据类型(MIME)。
. Component———指定Intent的目标组件的类名称比如要执行的动作、类别、数据、
附加信息等。
下面就一个Intent中包含的信息进行简要介绍。
1)Action 
一个Intent的Action在很大程度上说明这个Intent要做什么,例如查看(View)、删除
(Delete)、编辑(Edit)等。Action 是一个字符串命名的动作,Android中预定义了很多
Action,可以参考Intent类查看,表3-1是Android文档中的几个动作。
表3-1 Android动作
Constant(全局常量) Targetcomponent 
(目标组件) Action(动作) 
ACTION_CALL Activity 调用拨打电话界面
ACTION_EDIT Activity 提供显式的可使用户编辑访问给定的
数据
ACTION_MAIN Activity 发起一个任务或程序的起始Activity,无
数据输入和返回信息
ACTION_SYNC Activity 用于移动设备上带有数据的服务的数据
同步
ACTION_BATTERY_LOW broadcastReceiver 调用电池电量低的系统警告对话框框
ACTION_HEADSET_PLUG broadcastReceiver 无线耳机插入和拔出状态检测
ACTION_SCREEN_ON broadcastReceiver 屏幕亮屏监听
ACTION_TIMEZONE_CHANGED broadcastReceiver 时间时区设置更改 
此外,用户也可以自定义Action,比如ACTION_CALL_BUTTON:拨打按钮,调用
拨号程序。定义的Action最好能表明其所表示的意义,要做什么,这样Intent中的数据才

Android应用开发案例教程(第2版) 

好填充。Intent对象的getAction()可以获取动作,使用setAction()可以设置动作。
2)Data 
其实就是一个URI,用于执行一个Action时所用到的数据的URI和MIME 。不同的

Action有不同的数据规格,比如ACTION_EDIT动作,数据就可以包含一个用于编辑文档
的URI 。如果是一个ACTION_CALL动作,那么数据就是一个包含了tel:6546541的数
据字段,所以上面提到的自定义Action时要规范命名。数据的URI和类型对于Intent的
匹配是很重要的,Android往往根据数据的URI和MIME找到能处理该Intent的最佳目
标组件。

3)component(组件) 
指定Intent的目标组件的类名称。通常Android会根据Intent中包含的其他属性的
信息,比如Action、Data/Type、Category进行查找,最终找到一个与之匹配的目标组件。

如果设置了Intent目标组件的名字,那么这个Intent就会被传递给特定的组件,而不
再执行上述查找过程,指定了这个属性以后,Intent的其他所有属性都是可选的。也就是
我们说的显式Intent。如果不设置,则是隐式的Intent,Android系统将根据IntentFilter 
中的信息进行匹配。

4)Category

Category指定了用于处理Intent的组件的类型信息,一个Intent可以添加多个
Category,使用addCategory()方法即可,使用removeCategory()删除一个已经添加的类
别。Android的Intent类里定义了很多常用的类别,可以参考使用。

5)Extras 

有些用于处理Intent的目标组件需要一些额外的信息,那么就可以通过Intent的
put.
()方法把额外的信息塞入到Intent对象中,用于目标组件的使用,一个附件信息就是
一个key-value的键值对。Intent有一系列的put和get方法用于处理附加信息的塞入和
取出。

2.IntentFilter 

应用程序的组件为了告诉Android自己能响应、处理哪些隐式Intent请求,可以声明
一个甚至多个IntentFilter。每个IntentFilter描述该组件所能响应Intent请求的能
力———组件希望接收什么类型的请求行为,什么类型的请求数据。比如在请求网页浏览器
这个例子中,网页浏览器程序的IntentFilter就应该声明它所希望接收的IntentAction是
WEB_SEARCH_ACTION,以及与之相关的请求数据是网页地址URI格式。如何为组件
声明自己的InetFitr? rinfs.l文件中用属性<Itn

tnle常见的方法是在AndodMaietxmnetFilter>描述组件的IntentFilter。

Intent解析机制主要是通过查找已注册在AndroidManifestxml中的所有
IntentFilter及其中定义的Intent,最终找到匹配的Intent。在这个解析过程(.) 中,Android是
通过Intent的Action、Type、Category这3个属性来进行判断的,判断方法如下。

.如果Intent指明定了Action,则目标组件的IntentFilter的Action列表中就必须包
含有这个Action,否则不能匹配。
.如果Intent没有提供Type,系统将从Data中得到数据类型。和Action一样,目标

第 3 章 Android应用程序
79 
组件的数据类型列表中必须包含Intent的数据类型,否则不能匹配。
. 如果Intent中的数据不是content类型的URI,而且Intent也没有明确指定它的
type,将根据Intent中数据的scheme(比如http:或者mailto:)进行匹配。同上, 
Intent的scheme必须出现在目标组件的scheme列表中。
. 如果Intent指定了一个或多个category,这些类别必须全部出现在组建的类别列表
中。比如Intent 中包含了两个类别: LAUNCHER _ CATEGORY 和
ALTERNATIVE_CATEGORY,解析得到的目标组件必须至少包含这两个类别。
一个Intent对象只能指定一个action,而一个IntentFilter可以指定多个action。
action的列表不能为空,否则它将组织所有的intent。
一个Intent对象的Action必须和IntentFilter中的某一个Action匹配,才能通过测
试。如果IntentFilter的Action列表为空,则不通过。如果Intent对象不指定Action,并
且IntentFilter的Action列表不为空,则通过测试。
下面针对Intent和IntentFilter中包含的子元素Action(动作)、Data(数据)以及
Category(类别)进行比较并对具体规则进行详细介绍。
1)动作测试
<intent-filter>元素中可以包括子元素<action>,比如: 
<intent-filter> 
<action android:name="com.example.project.SHOW_CURRENT" /> 
<action android:name="com.example.project.SHOW_RECENT" /> 
<action android:name="com.example.project.SHOW_PENDING" /> 
</intent-filter> 
一条<intent-filter>元素至少应该包含一个<action>,否则任何Intent请求都不能
和该<intent-filter> 匹配。如果Intent请求的Action 和<intent-filter> 中某一条< 
action>匹配,那么该Intent就通过了这条<intent-filter>的动作测试。如果Intent请求
或<intent-filter>中没有说明具体的Action类型,那么会出现下面两种情况。
如果<intent-filter>中没有包含任何Action类型,那么无论什么Intent请求都无法
和这条<intent-filter>匹配; 
反之,如果Intent请求中没有设定Action类型,那么只要<intent-filter>中包含有
Action类型,这个Intent请求就将顺利地通过<intent-filter>的行为测试。
2)类别测试
<intent-filter>元素可以包含<category>子元素,比如: 
<intent-filter ...> 
<category android:name="android.Intent.Category.DEFAULT" /> 
<category android:name="android.Intent.Category.BROWSABLE" /> 
</intent-filter> 
只有当Intent请求中所有的Category与组件中某一个IntentFilter的<category>完
全匹配时,才会让该Intent请求通过测试,IntentFilter中多余的<category>声明并不会
导致匹配失败。一个没有指定任何类别测试的IntentFilter仅仅只会匹配没有设置类别的
Intent请求。

Android应用开发案例教程(第2版) 
80 
3)数据测试
数据在<intent-filter>中的描述如下: 
<intent-filter ... > 
<data android:type="video/mpeg" android:scheme="http" ... /> 
<data android:type="audio/mpeg" android:scheme="http" ... /> 
</intent-filter> 
<data>元素指定了希望接收的Intent请求的数据URI和数据类型,URI被分成3 
部分来进行匹配:scheme、authority和path。其中,用setData()设定的Intent请求的URI 
数据类型和scheme必须与IntentFilter中所指定的一致。若IntentFilter中还指定了
authority或path,它们也需要相匹配才会通过测试。
3.2.5 BroadcastReceiver组件
在Android中,Broadcast是一种广泛运用在应用程序之间传输信息的组件。而
BroadcastReceiver是接收并响应广播消息的组件,对发送出来的Broadcast进行过滤接收
并响应,它不包含任何用户界面,可以通过启动Activity或者Notification通知用户接收到
重要信息,在Notification中有多种方法提示用户,如闪动背景灯、振动设备、发出声音或在
状态栏上放置一个持久的图标。
BroadcastReceiver过滤接收的过程如下。
在需要发送信息时,把要发送的信息和用于过滤的信息(如Action、Category)装入一
个Intent 对象,然后通过调用Context.sendBroadcast()、sendOrderBroadcast()或
sendStickyBroadcast()方法,把Intent对象以广播的方式发送出去。
当Intent发送后,所有已经注册的BroadcastReceiver会检查注册时的IntentFilter是
否与发送的Intent相匹配,若匹配则调用BroadcastReceiver的onReceive()方法。因此在
我们定义一个BroadcastReceiver时,通常都需要实现onReceive()方法。
BroadcastReceiver注册有两种方式: 
一种方式是静态地在AndroidManifest.xml中用<receiver>标签声明注册,并在标签
内用<intent-filter>标签设置过滤器; 
另一种方式是动态地在代码中先定义并设置好一个IntentFilter对象,然后在需要注册的
地方调用Context.registerReceiver()方法,如果取消时就调用Context.unregisterReceiver() 
方法。
不管是用XML注册的还是用代码注册的,在程序退出时,一般都需要注销,否则下次
启动程序可能会有多个BroadcastReceiver。另外,若在使用sendBroadcast()的方法时指
定了接收权限,则只有在AndroidManifest.xml中用<uses-permission>标签声明了拥有
此权限的BroascastReceiver才会有可能接收到发送来的Broadcast。
同样,若在注册BroadcastReceiver时指定了可接收的Broadcast的权限,则只有在包
内的AndroidManifest.xml中用<uses-permission>标签声明了拥有此权限的Context对
象所发送的Broadcast才能被这个BroadcastReceiver所接收。
3.2.6 ContentProvider组件
ContentProvider是Android系统提供的一种标准的共享数据的机制,在Android中

第 
3 
章Android应用程序

每一个应用程序的资源都为私有,应用程序可以通过ContentProvider组件访问其他应用
程序的私有数据(私有数据可以是存储在文件系统中的文件,或者是存放在SQLite中的数
据), 如图3-3所示。


图3-3 应用程序、ContentResolver 
与ContentProvider

对ContentProvider的使用,有两种方式: 

.ContentResolver访问。
.Cnetgtotneovr()。
otx.eCnetRsleAndroid系统内部也提供一些内置的ContentProvider,能够为应用程序提供重要的数
据信息。使用ContentProvider对外共享数据的好处是统一了数据的访问方式。

3.nrid生命周期
3 
Ado

3.程序生命周期
3.1 

程序的生命周期是指在Android系统中进程从启动到终止的所有阶段,也就是
Android程序启动到停止的全过程。程序的生命周期是由
Android系统进行调度和控制的。

Android系统中的进程分为:前台进程、可见进程、服务进

程、后台进程、空进程。

Android系统中的进程优先级由高到低,如图3-4所示。

1. 
前台进程
前台进程是Android系统中最重要的进程,是指与用户正在
交互的进程,包含以下4种情况: 

.进程中的Activity正在与用户进行交互。
.进程服务被Activity调用,而且这个Activity正在与用
户
进行交互
。
.进程服务正在执行声明周期中的回调方法,如onCreate()
、
onStart() 或onDestroy()
。
.进程的BroadcastReceiver正在执行onReceive() 方法。
Android系统有多个前台进程同时运行时,可能会出现资源不足的情况,此时会清除
部分前台进程,保证主要的用户界面能够及时响应。

图3-4 Android系统的
进程及优先级

Android应用开发案例教程(第2版) 

2. 
可见进程
可见进程指部分程序界面能够被用户看见,但不在前台与用户交互,不响应界面事件
的进程。如果一个进程包含服务,且这个服务正在被用户可见的Activity调用,此进程同
样被视为可见进程。

Android系统一般存在少量的可见进程,只有在特殊的情况下,Android系统才会为保
证前台进程的资源而清除可见进程。

3. 
服务进程
服务进程是指包含已启动服务的进程,通常特点: 

.没有用户界面。
.在后台长期运行。
Android系统在不能保证前台进程或可视进程所必要的资源时,才会强行清除服务
进程。

4. 
后台进程
后台进程是指不包含任何已经启动的服务,而且没有任何用户可见的Activity的
进程。
Android系统中一般存在数量较多的后台进程,在系统资源紧张时,系统将优先清除
用户较长时间没有见到的后台进程。

5. 
空进程
空进程是指不包含任何活跃组件的进程,空进程在系统资源紧张时会被首先清除。但
为了提高Android系统应用程序的启动速度,Android系统会将空进程保存在系统内存
中,在用户重新启动该程序时,空进程会被重新使用。

除了以上的优先级外,以下两方面也决定它们的优先级: 

.进程的优先级取决于所有组件中的优先级最高的部分。
.进程的优先级会根据与其他进程的依赖关系而变化。
3.组件生命周期
3.2 
所有Android组件都具有自己的生命周期,是指从组件的建立到组件的销毁整个过
程。在生命周期中,组件会在可见、不可见、活动、非活动等状态中不断变化。下面就各个
组件的生命周期逐一进行讲述。

1.Service生命周期
Service组件通常没有用户界面UI,其启动后一直运行于后台;它与应用程序的其他模

块(如activity)一同运行于程序的主线程中。
一个Service的生命周期通常包含:创建、启动、销毁这几个过程。
Service只继承了onCreate()、onStart()、onDestroy()3个方法,当第一次启动Service 

时,先后调用了onCreate()、onStart() 这两个方法;当停止Service时,则执行onDestroy() 
方法。需要注意的是,如果Service已经启动了,当再次启动Service时,不会再执行


第 3 章 Android应用程序
83 
onCreate()方法,而是直接执行onStart()方法。
创建Service 的方式有两种,一种是通过startService 创建,另外一种是通过
bindService创建Service。两种创建方式的区别在于,startService是创建并启动Service, 
而bindService只是创建了一个Service实例并取得了一个与该Service关联的binder对
象,但没有启动它,如图3-5所示。
图3-5 Service 的生命周期
如果没有程序停止它或者它没有自己停止,Service将一直运行。在这种模式下, 
Service开始于调用Context.startService(),停止于Context.stopService()。Service可以
通过调用stopService()或Service.stopSelfResult()停止自己。不管调用多少次
startService(),只需要调用一次stopService()就可以停止Service。一般在Activity中启
动和终止Service。它可以通过接口被外部程序调用。外部程序建立到Service的连接,通
过连接来操作Service。建立连接开始于Context.bindService(),结束于Context. 
unbindService()。多个客户端可以绑定到同一个Service,如果Service 没有启动, 
bindService()可以选择启动它。
上述两种方式不是完全分离的。通过startService()启动的服务。如一个Intent想要
播放音乐,通过startService()方法启动后台播放音乐的Service。然后,用户想要操作播
放器或者获取当前正在播放的乐曲的信息,一个Activity就会通过bindService()建立一个
到这个Service的连接。这种情况下stopService()在全部的连接关闭后才会真正停止
Service。
像Activity一样,Service也有可以通过监视状态实现的生命周期。但是比Activity要
少,通常只有3种方法,并且是public而不是protected的属性的,具体如下: 
1. void onCreate() 
2. void onStart(Intent intent) 
3. void onDestroy() 
通过实现上述3种方法,可以监视Service生命周期的两个嵌套循环: 
整个生命周期从onCreate()开始,到onDestroy()结束,像Activity一样,一个Android 
Service的生命周期在onCreate()中执行初始化操作,在onDestroy()中释放所有用到的资源。
如:后台播放音乐的Service可能在onCreate()创建一个播放音乐的线程,在onDestroy()中销
毁这个线程。
活动生命周期开始于onStart()。这个方法处理传入到startService()方法的Intent。音乐
服务会打开Intent查看要播放哪首歌曲,并开始播放。当服务停止的时候,没有方法检测到
(没有onStop()方法)、onCreate()和onDestroy()用于所有通过Context.startService()or

Android应用开发案例教程(第2版) 
84 
Context.bindService()启动的Service。onStart()只用于通过startService()开始的Service。
如果一个AndroidService生命周期是可以从外部绑定的,它就可以触发以下的方法: 
1. IBinder onBind(Intent intent) 
2. boolean onUnbind(Intent intent) 
3. void onRebind(Intent intent) 
onBind()回调被传递给调用bindService的Intent、onUnbind()被unbindService()中
的intent处理。如果服务允许被绑定,那么onBind()方法返回客户端和Service的沟通通
道。如果一个新的客户端连接到服务,onUnbind()会触发onRebind()调用。
后续案例将会讲解说明Service的回调方法。将通过startService和bindService()启
动的Service分开了,但是要注意不管它们是怎么启动的,都有可能被客户端连接,因此都
有可能触发到onBind()和onUnbind()方法。
2.Service生命周期应用案例
具体实现步骤如下。
(1)在AndroidStudio中选择File→New→NewProject,创建一个新的Android工
程,项目名为ServiceTestDemo,目标API选择28(即Android9.0版本),应用程序名为
ServiceTestDemo,包名为com.hisoft.activity,创建的Activity的名字为ServiceTestDemoActivity, 
最小SDK版本根据选择的目标API会自动添加为15。
(2)修改res目录下layout文件夹中main.xml代码,添加4个Button控件,代码
如下: 
1. <? xml version="1.0" encoding="utf-8"? > 
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
3. android:orientation="vertical" 
4. android:layout_width="fill_parent" 
5. android:layout_height="fill_parent" 
6. > 
7. <TextView 
8. android:id="@+id/text" 
9. android:layout_width="fill_parent" 
10. android:layout_height="wrap_content" 
11. android:text="@string/hello" 
12. /> 
13. <Button 
14. android:id="@+id/startservice" 
15. android:layout_width="fill_parent" 
16. android:layout_height="wrap_content" 
17. android:text="启动Service" 
18. /> 
19. <Button

第 3 章 Android应用程序
85 
20. android:id="@+id/stopservice" 
21. android:layout_width="fill_parent" 
22. android:layout_height="wrap_content" 
23. android:text="停止Service" 
24. /> 
25. <Button 
26. android:id="@+id/bindservice" 
27. android:layout_width="fill_parent" 
28. android:layout_height="wrap_content" 
29. android:text="绑定Service" 
30. /> 
31. <Button 
32. android:id="@+id/unbindservice" 
33. android:layout_width="fill_parent" 
34. android:layout_height="wrap_content" 
35. android:text="解除Service" 
36. /> 
37. </LinearLayout> 
(3)在上述包下,新建一个Service,命名为MyService.java,代码如下: 
1. package com.hisoft.service; 
2. import android.app.Service; 
3. import android.content.Intent; 
4. import android.os.Binder; 
5. import android.os.IBinder; 
6. import android.text.format.Time; 
7. import android.util.Log; 
8. public class MyService extends Service { 
9. //定义一个Tag 标签
10. private static final String TAG ="TestService"; 
11. //这里定义把一个Binder 类用在onBind()方法里,这样Activity 可以获取到
12. private MyBinder mBinder =new MyBinder(); 
13. @Override 
14. public IBinder onBind(Intent intent) { 
15. Log.e(TAG, " -----start IBinder-----~~~"); 
16. return mBinder; 
17. } 
18. @Override 
19. public void onCreate() { 
20. Log.e(TAG, "-----start onCreate-----"); 
21. super.onCreate(); 
22. } 
23. 
24. @Override

Android应用开发案例教程(第2版) 
86 
25. public void onStart(Intent intent, int startId) { 
26. Log.e(TAG, "-----start onStart-----"); 
27. super.onStart(intent, startId); 
28. } 
29. 
30. @Override 
31. public void onDestroy() { 
32. Log.e(TAG, "-----start onDestroy-----"); 
33. super.onDestroy(); 
34. } 
35. 
36. 
37. @Override 
38. public boolean onUnbind(Intent intent) { 
39. Log.e(TAG, "-----start onUnbind-----"); 
40. return super.onUnbind(intent); 
41. } 
42. 
43. //这里是一个获取当前时间的函数,不过没有格式化
44. public String getSystemTime(){ 
45. 
46. Time t =new Time(); 
47. t.setToNow(); 
48. return t.toString(); 
49. } 
50. 
51. public class MyBinder extends Binder{ 
52. MyService getService() 
53. { 
54. return MyService.this; 
55. } 
56. } 
57. } 
(4)修改com.hisoft.activity包下的ServiceTestDemoActivity.java文件,代码如下: 
1. package com.hisoft.activity; 
2. import android.app.Activity; 
3. import android.content.ComponentName; 
4. import android.content.Context; 
5. import android.content.Intent; 
6. import android.content.ServiceConnection; 
7. import android.os.Bundle; 
8. import android.os.IBinder; 
9. import android.view.View;

第 3 章 Android应用程序
87 
10. import android.view.View.OnClickListener; 
11. import android.widget.Button; 
12. import android.widget.TextView; 
13. public class ServiceTestDemoActivity extends Activity implements 
OnClickListener{ 
14. private MyService mMyService; 
15. private TextView mTextView; 
16. private Button startServiceButton; 
17. private Button stopServiceButton; 
18. private Button bindServiceButton; 
19. private Button unbindServiceButton; 
20. private Context mContext; 
21. //这里需要用到ServiceConnection,在Context.bindService()和Context. 
//unBindService()里用到
22. private ServiceConnection mServiceConnection = new ServiceConnection() { 
23. //当单击bindService 按钮时,让TextView 显示MyService 里getSystemTime() 
//方法的返回值
24. public void onServiceConnected(ComponentName name, IBinder service) { 
25. //TODO Auto-generated method stub 
26. //mMyService = ((MyService.MyBinder)service).getService(); 
27. } 
28. 
29. public void onServiceDisconnected(ComponentName name) { 
30. //TODO Auto-generated method stub 
31. 
32. } 
33. }; 
34. public void onCreate(Bundle savedInstanceState) { 
35. super.onCreate(savedInstanceState); 
36. setContentView(R.layout.main); 
37. setupViews(); 
38. } 
39. 
40. public void setupViews(){ 
41. 
42. mContext = ServiceDemo.this; 
43. mTextView = (TextView)findViewById(R.id.text); 
44. 
45. startServiceButton = (Button)findViewById(R.id.startservice); 
46. stopServiceButton = (Button)findViewById(R.id.stopservice); 
47. bindServiceButton = (Button)findViewById(R.id.bindservice); 
48. unbindServiceButton = (Button)findViewById(R.id.unbindservice); 
49. 

Android应用开发案例教程(第2版) 
88 
50. startServiceButton.setOnClickListener(this); 
51. stopServiceButton.setOnClickListener(this); 
52. bindServiceButton.setOnClickListener(this); 
53. unbindServiceButton.setOnClickListener(this); 
54. } 
55. 
56. public void onClick(View v) { 
57. //TODO Auto-generated method stub 
58. if(v == startServiceButton){ 
59. Intent i = new Intent(); 
60. i.setClass(ServiceTestDemoActivity.this, MyService.class); 
61. mContext.startService(i); 
62. }else if(v == stopServiceButton){ 
63. Intent i = new Intent(); 
64. i.setClass(ServiceTestDemoActivity.this, MyService.class); 
65. mContext.stopService(i); 
66. }else if(v == bindServiceButton){ 
67. Intent i = new Intent(); 
68. i.setClass(ServiceTestDemoActivity.this, MyService.class); 
69. mContext.bindService(i, mServiceConnection, BIND_AUTO_CREATE); 
70. }else{ 
71. mContext.unbindService(mServiceConnection); 
72. } 
73. } 
74. 
75. } 
(5)修改AndroidManifest.xml代码,在<application>标签下的根目录下,添加注册
新创建的MyService,如下代码第14行: 
1. <? xml version="1.0" encoding="utf-8"? > 
2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
3. package="com.hisoft.activity " 
4. android:versionCode="1" 
5. android:versionName="1.0"> 
6. <application android:icon="@drawable/icon" android:label= 
"@string/app_name"> 
7. <activity android:name=". ServiceTestDemoActivity" 
8. android:label="@string/app_name"> 
9. <intent-filter> 
10. <action android:name="android.intent.action.MAIN" /> 
11. <category android:name="android.intent.category.LAUNCHER" /> 
12. </intent-filter> 
13. </activity>

第 3 章 Android应用程序
89 
14. <service android:name=".MyService" android:exported="true"></service> 
15. </application> 
16. </manifest> 
(6)部署工程,并执行上述工程,运行结果如图3-6所示。
图3-6 Service 生命周期运行界面
① 单击“启动SERVICE”按钮时,程序先后执行了Service中onCreate()、onStart()这
两个方法,打开日志界面Logcat视窗,显示如图3-7所示内容。
图3-7 启动Service 调用顺序
② 然后单击Home键进入Settings(设置)→Applications(应用)→RunningServices 
(正在运行的服务)查看刚才新启动的服务,如图3-8所示。
图3-8 新启动的Service 服务
③ 单击“停止SERVICE”按钮时,Service则执行了onDestroy()方法,如图3-9所示。
④ 再次单击“启动SERVICE”按钮,然后再单击bindService按钮(通常bindService都