`

Android技术鸟瞰

 
阅读更多
1.1.2 Android 的系统构架
Android 的体系结构,如图1-6 所示。
图1-6 Android 系统结构图
从图1-6 可以看出Android 分为4 层,从高到底分别是应用层、应用框架层、
系统运行库层和Linux 内核层。下面将对这4 层进行简要的分析和介绍。
1.应用层
应用是用Java 语言编写的运行在虚拟机上的程序,如图1-6 中最上层部分
所示。其实,Google 最开始时就在Android 系统中捆绑了一些核心应用,比如
E-mail 客户端、SMS 短消息程序、日历、地图、浏览器、联系人管理程序,等等。
2.应用框架层
这一层是编写Google 发布的核心应用时所使用的API 框架,开发人员同样
可以使用这些框架来开发自己的应用,这样便简化了程序开发的架构设计,但是
必须遵守其框架的开发原则。
从图1-6 中可以看出,Android 提供了如下一些组件。
* 丰富而又可扩展的视图(View):可以用来构建应用程序,它包括列表(List)、
网格(Grid)、文本框(Text Box)、按钮(Button),以及可嵌入的Web 浏览
器。
* 内容提供器(Content Providers):它可以让一个应用访问另一个应用的数
据(如联系人数据库), 或共享它们自己的数据。
* 资源管理器(Resource Manager):提供非代码资源的访问,如本地字符串、
图形和布局文件(Layout file)。
* 通知管理器(Notification Manager):应用可以在状态栏中显示自定义的
提示信息。
* 活动管理器(Activity Manager):用来管理应用程序生命周期并提供常用
的导航退回功能。
* 窗口管理器(Window Manager):管理所有的窗口程序。
* 包管理器(Package Manager):Android 系统内的程序管理。
后面的章节将进一步介绍这些组件的使用。
3.系统运行库(C/C++库以及Android 运行库)层
当使用Android 应用框架时,Android 系统会通过一些C/C++库来支持我们
使用的各个组件,使其能更好地为我们服务。
* Bionic 系统C 库:C 语言标准库,系统最底层的库,C 库通过Linux 系统来
调用。
* 多媒体库(MediaFramework):Android 系统多媒体库,基于PacketVideo
OpenCORE,该库支持多种常见格式的音频、视频的回放和录制,以及图片,比如
MPEG4、MP3、AAC、AMR、JPG、PNG 等。
* SGL:2D 图形引擎库。
* SSL:位于TCP/IP 协议与各种应用层协议之间,为数据通信提供支持。
* OpenGL ES 1.0:3D 效果的支持。
* SQLite:关系数据库。
* Webkit:Web 浏览器引擎。
* FreeType:位图(bitmap)及矢量(vector)。
每个Java 程序都运行在Dalvik 虚拟机之上。与PC 一样,每个Android 应
用程序都有自己的进程,Dalvik 虚拟机只执行.dex 的可执行文件。当Java 程序
通过编译,最后还需要通过SDK 中的dx 工具转化成.dex 格式才能正常在虚拟机
上执行。
Google 于2007 年底正式发布了Android SDK, 作为Android 系统的重要特
性,Dalvik 虚拟机也第一次进入了人们的视野。它对内存的高效使用,以及在
低速CPU 上表现出的高性能,确实令人刮目相看。Android 系统可以简单地完成
进程隔离和线程管理。每一个Android 应用在底层都会对应一个独立的Dalvik
虚拟机实例,其代码在虚拟机的解释下得以执行。
很多人认为Dalvik 虚拟机是一个Java 虚拟机,因为Android 的编程语言恰
恰就是Java 语言。但是这种说法并不准确,因为Dalvik 虚拟机并不是按照Java
虚拟机的规范来实现的,两者并不兼容。它们有两个明显的不同:Java 虚拟机
运行的是Java 字节码,而Dalvik 虚拟机运行的则是其专有的文件格式为dex
(Dalvik Executable)的文件。在Java SE 程序中的Java 类会被编译成一个或
者多个字节码文件(.class)然后打包到jar 文件,而后Java 虚拟机会从相应
的class 文件和jar 文件中获取相应的字节码;Android 应用虽然也是使用Java
语言进行编程,但是在编译成class 文件后,还会通过一个工具(dx)将应用所
有的class 文件转换成一个dex 文件,而后Dalvik 虚拟机会从其中读取指令和
数据。
Dalvik 虚拟机非常适合在移动终端上使用,相对于在桌面系统和服务器系
统运行的虚拟机而言,它不需要很快的CPU 计算速度和大量的内存空间。根据
Google 的测算,64MB 的内存已经能够让系统正常运转了。其中24MB 被用于底层
系统的初始化和启动,另外20MB 被用于启动高层服务。当然,随着系统服务的
增多和应用功能的扩展,其所消耗的内存也势必越来越大。归纳起来,Dalvik
虚拟机有如下几个主要特征:
(1)专有的dex 文件格式。dex 是Dalvik 虚拟机专用的文件格式,而为什
么弃用已有的字节码文件(.class 文件)而采用新的格式呢?原因如下:
* 每个应用中会定义很多类,编译完成后即会有很多相应的class 文件,class
文件中会有大量冗余信息,而dex 文件格式会把所有的class 文件内容整合到一
个文件中。这样,除了减少整体的文件尺寸和I/O 操作外,也提高了类的查找速
度。
* 增加了对新的操作码的支持。
* 文件结构尽量简洁,使用等长的指令,借以提高解析速度。
* 尽量扩大只读结构的大小,借以提高跨进程的数据共享。
(2)dex 的优化。dex 文件的结构是紧凑的,但是如果还想运行时的性能有
进一步提高,就需要对dex 文件进一步优化。优化主要针对以下几个方面:
* 调整所有字段的字节序(LITTLE_ENDIAN)和对齐结构中的每一个域。
* 验证DEX 文件中的所有类。
* 对一些特定的类和方法里的操作码进行优化。
(3)基于寄存器。相对于基于堆栈实现的虚拟机,基于寄存器实现的虚拟
机虽然在硬件、通用性上要差一些,但是它在代码的执行效率上却更胜一筹。
(4)一个应用,一个虚拟机实例,一个进程。每一个Android 应用都运行
在一个Dalvik 虚拟机实例中,而每一个虚拟机实例都是一个独立的进程空间。
虚拟机的线程机制、内存分配和管理、Mutex 等的实现都依赖底层操作系统。所
有Android 应用的线程都对应一个Linux 线程,虚拟机因而可以更多地依赖操作
系统的线程调度和管理机制。不同的应用在不同的进程空间里运行,对不同来源
的应用都使用不同的Linux 用户来运行,可以最大程度地保护应用的安全和独立
运行。
4.Linux 内核层
Android 的核心系统服务基于Linux 2.6 内核,如安全性、内存管理、进程
管理、网络协议栈和驱动模型等都依赖于该内核。Linux 内核同时也作为硬件和
软件栈之间的抽象层。
Android 更多的是需要一些与移动设备相关的驱动程序,主要的驱动如下所示。
* 显示驱动(Display Driver):基于Linux 的帧缓冲(Frame Buffer)驱动。
* 键盘驱动(KeyBoard Driver):作为输入设备的键盘驱动。
* Flash 内存驱动(Flash Memory Driver):基于MTD 的Flash 驱动程序。
* 照相机驱动(Camera Driver):常用的基于Linux 的v4l2(Video for Linux)
驱动。
* 音频驱动(Audio Driver):常用的基于ALSA(Advanced Linux Sound
Architecture)的高级Linux 声音体系驱动。
* 蓝牙驱动(Bluetooth Driver):基于IEEE 802.15.1 标准的无线传输技术。
* WiFi 驱动:基于IEEE 802.11 标准的驱动程序。
* Binder IPC 驱动:Android 的一个特殊的驱动程序,具有单独的设备节点,
提供进程间通信的功能。
* Power Management(电源管理):比如电池电量等。
1.1.3 Android 应用程序框架
上一节我们对Android 的系统构架进行了详细剖析,Android 分为应用层、
应用框架层、系统运行库层和Linux 内核层。我们在开发应用时都是通过框架来
与Android 底层进行交互,接触最多的就是应用框架层了。
什么是应用程序框架呢?框架可以说是一个应用程序的核心,是所有参与开
发的程序员共同使用和遵守的约定,大家在其约定上进行必要的扩展,但程序始
终保持主体结构的一致性。其作用是让程序保持清晰和一目了然,在满足不同需
求的同时又不互相影响。
Android 系统提供给应用开发者的本身就是一个框架,所有的应用开发都必
须遵守这个框架的原则。我们在开发应用时就是在这个框架上进行扩展,下面来
看看Android 这个框架都有些什么功能可供我们使用。
* android.app:提供高层的程序模型和基本的运行环境。
* android.content:包含对各种设备上的数据进行访问和发布。
* android.database:通过内容提供者浏览和操作数据库。
* android.graphics:底层的图形库,包含画布、颜色过滤、点、矩形,可以
将它们直接绘制到屏幕上。
* android.location :定位和相关服务的类。
* android.media:提供一些类管理多种音频、视频的媒体接口。
* android.net :提供帮助网络访问的类,超过通常的java.net.* 接口。
* android.os :提供了系统服务、消息传输和IPC 机制。
* android.opengl:提供OpenGL 的工具。
* android.provider:提供访问Android 内容提供者的类。
* android.telephony:提供与拨打电话相关的API 交互。
* android.view:提供基础的用户界面接口框架。
* android.util :涉及工具性的方法,例如时间日期的操作。
* android.webkit :默认浏览器操作接口。
* android.widget:包含各种UI 元素(大部分是可见的)在应用程序的布局中
使用。



本章讲解如何配置Android 开发环境首先介绍Android 开发所需要的开发包
和工具,以及获得它们的方式;其次介绍如何正确安装和配置这些开发包;最后,
为了测试安装的开发环境,创建了第一个Android 项目——HelloAndroid,然后
在模拟器上运行和调试该程序,并将该应用程序安装到Android 手机上。
2.1 Android 开发准备工作
配置Android 开发环境之前,首先需要了解Android 对操作系统的要求。它
可以使用Windows XP 及其以上版本、Mac OS、Linux 等操作系统,本书以Windows
XP 为例进行讲解。Android 开发所需软件的版本及其下载地址如表2-1 所示。
表2-1 Android 开发所需软件的版本及其下载地址
2.2 开发包及其工具的安装和配置
Android 以Java 作为开发语言,JDK 是进行Java 开发时必需的开发包。
Eclipse 是一款非常优秀的开源IDE,在大量插件的“配合”下,完全可以满足
从企业级Java 应用到手机终端Java 游戏的开发。Google 官方也提供了基于
Eclipse 的Android 开发插件ADT,所以本书选择Eclipse 作为开发IDE。
2.2.1 安装JDK 和配置Java 开发环境
很多人不能够很好地进行Java 开发,原因就在于对Java 运行环境不了解或
是了解得不够透彻。如果连一个普通的Java 程序运行环境都搭建不好,就更不
要说理解J2EE、J2ME 以及本书所讲的Android 等的运行环境了。因此,这里我
们先讲如何安装JDK 以及Java 环境的配置,教大家搭建一个学习Java 的基础平
台,让大家少走一些弯路,多学到一些小窃门。
(1)登录http://java.sun.com,下载最新版JDK。
(2)安装JDK,安装包中包含了JDK 和JRE 两部分,笔者建议将它们安装
在同一个盘符下。双击安装程序,选择安装的目录,点击“下一步”,等待安装
程序自动完成安装即可。
(3)右键单击“我的电脑”,选择“属性”菜单项,选择“高级”选项卡,
选择“环境变量”,找到“Path”变量名(如果没有就新建一个名为“Path”的
变量),点击“编辑”按钮,添加JDK 安装目录中“bin”文件夹路径,如图2-1
所示。然后点击“确定”按钮完成。再找到“ClASSPATH”变量(如果没有,同
样可以新建),输入JDK 安装目录中“lib”以及“demo”的路径,如图2-2 所
示,单击“确定”按钮完成。
图2-1 “Path”变量配置图2-2 “ClASSPATH”变量配置
(4)安装配置完成之后,要测试是否安装成功。点击开始→运行,输入
“CMD”,打开命令行模式。键入命令“java -version”,检测JDK 是否安装成
功,如果运行结果如图2-3 所示,即表示安装成功。
图2-3 “java -version”测试命令
2.2.2 Eclipse 的安装与汉化
Eclipse 的安装非常简单,直接将下载的压缩包解压即可。老版本的Eclipse
的多国语言项目只更新到3.2.1 版本,以后就再也没有更新了。Eclipse 最近发
布了一个名为Babel project 的项目,这个项目就是用来解决国际化的问题,旨
在为每一个插件提供独立的语言包。这样,当做RCP 项目的时候,根据需要对语
言进行打包即可!
Babel 的安装方法和步骤如下所示:
(1)启动Eclipse 开发工具,依次点击“Help ” →选择“Software
Update ...”菜单命令,打开“Software Updates and Add-ons”对话框,选择
“Avaliable Software”项。接着点击“Add Site...”按钮,在“Location”
文本框中输入Babel 更新地址:http://download.eclipse.org/
technology/babel/update-site/ganymede,然后点击OK 按钮,如图2-4 所示。
图2-4 添加语言包更新地址
(2)“Avaliable Software”表中会多出一项
http://download.eclipse.org/technology/babel/update-site/ganymede/,点
击该项左边的箭头,就会出现网络更新软件列表,如图2-5 所示。
图2-5 Avaliable Software 选择框
(3)选择“Simplified Chinese”语言包后,点击“Install... ”按钮,
等待Eclipse 处理。
处理完成后会出现“Install”对话框,这时会提示你选择要安装的语言包。
根据提示,很容易完成后面的操作,这里就不再赘述了。
安装完毕后,重新启动Eclipse 即可完成全部汉化过程。
如果重启Eclipse 后不显示中文,请用命令行“eclipse.exe -nl zh_CN”重新
启动Eclipse。
2.2.3 SDK 和ADT 的安装和配置
安装了JDK 和Eclipse 后,现在就要安装Android SDK 和ADT 插件了。
1.Android SDK 安装
(1)解压缩下载好的SDK 安装包到要安装SDK 的路径,然后运行“SDK
Setup.exe”。
(2)如果遇到了消息为“Failed to fetch URL…”的错误提示,那么需要
将HTTPS 方式改为HTTP 方式,在“Android SDK and AVD Manager”窗口的左侧
选择“Settings”,选中“Force https://…”选项(如图2-6 所示),点击
“Save & Apply”并重新运行SDK Setup.exe。
图2-6 更改HTTP 方式
(3)点击“Available Packages”,选择要安装的API 版本及USB 驱动和
SDK 文档,如图2-7 所示。这里为了测试方便,所以全部选择了。
图2-7 选择API 版本
(4)选择好之后点击“Install Selected”按钮,安装选中的软件包,在
接下来出现的界面中依次点击“Accept All”单选按钮和“Install Accepted”
按钮,开始下载所选择的安装包。
下载完成之后,根据提示即可完成后续的安装操作。
到这里,我们就完成了Android SDK 的安装,下面来配置Android SDK。
2.Android SDK 配置
需要将Android SDK 安装目录中的tools 文件夹路径添加到环境变量中以便
使用,操作步骤如下:
(1)右键点击“我的电脑”,依次选择“属性”→“高级”→“环境变量”
选项,如图2-8 所示。
(2)选择“系统变量”中变量名为“Path”的项,点击“编辑”按钮,将
Android SDK 安装文件夹下的tools 文件夹的路径加入到“Path”变量中,注意
用“、”隔开,如图2-9 所示。
图2-8 环境变量图2-9 编辑系统环境变量
(3)依次点击“确定”,完成环境变量配置。
3.安装和配置ADT
下面我们来安装和配置ADT 插件,步骤如下:
(1)启动Eclipse,点击“Help”菜单,依次选择“Software Update...”
项和“Avaiable Software”选项卡,点击“Add Site...”按钮,输入地址
https://dl-ssl.google.com/android/eclipse/,结果如图2-10 所示。
(2)点击“OK”,这时可能会出现如图2-11 所示的错误。
图2-10 添加ADT 的更新地址图2-11 更新地址错误
解决这个问题的方法是:将
“https://dl-ssl.google.com/android/eclipse/”中的“https”更改为
“http”,在接下来的对话框中选中“Name”下的所有选项,根据提示即可完成
后续的安装过程。
(3)打开菜单“Windows”,依次选择“Preferences”→“Android”,点
击“Browse...”按钮,选择Android SDK 的安装路径,如图2-12 所示。
图2-12 Eclipse 首选项
(4)点击“OK”按钮,开打菜单“File”,依次选择“NEW” →
“Project...”菜单命令,出现如图2-13 所示的“Android Projest”选项,则
表示安装配置成功。
图2-13 新建工程界面
到这里,我们的准备工作已经就绪,可以在Android 平台上开发我们的应用
了,很心动吧!神奇的Android 之旅即将开始。
2.3 创建第一个Android 项目——HelloAndroid
为了便于第一次开发Android 应用的朋友能对整个开发过程有系统性的了
解,并能亲自动手创建自己的应用,我们特在本书的开篇准备了一个简单的实例
项目——HelloAndroid。
2.3.1 创建HelloAndroid 项目
ADT 提供了简单的生成Andriod 应用框架的功能,我们现在使用ADT 通过
Eclipse 创建一个Android 工程,其步骤如下。
(1)打开Eclipse 开发工具,新建一个项目,在弹出的“New Project”对
话框的列表中展开“Android”项,然后选择“Android Project”子项,如图
2-14 所示。
图2-14 新建一个Android 工程
(2)点击“Next”按钮,在“Project name”文本框中输入
“HelloAndroid”,然后在“Build Target”选项框中选择“Android SDK
1.5”,在Application name 文本框中输入这个应用程序的名字(HelloAndroid),
在Package name 文本框中输入应用程序包的名字
(com.yarin.Android.HelloAndroid),在Create Activity 文本框中输入
Activity 的名字(HelloAndroid),如图2-15 所示。
图2-15 新建HelloAndroid 工程
(3)单击“Finish”按钮,此时Eclipse 会自动完成Android 项目的创建,
这时Eclipse 开发平台左边的导航器中显示了刚才创建的项目
“HelloAndroid”。如果没有出现导航器,则可以通过单击“Window”→“Show
View” →“Package Explorer”菜单命令来显示导航器,如图2-16 所示。
到这里,HelloAndroid 项目已经创建好,而且这个项目是由我们前面安装
的ADT 插件自动生成,所以不用编写代码即可运行。下面我们将讲述如何在模拟
器中运行刚刚创建的HelloAndroid 项目。
2.3.2 运行HelloAndroid 及模拟器的使用
上面我们已经利用ADT 插件通过Eclipse 创建好了第一个Android 项目,而
且没有编写任何代码,我们很想看看运行之后的结果!不要着急,在模拟器中运
行该应用之前,有必要了解一下模拟器的使用和配置。
从Android 1.5 开始引入了AVD(Android Virtual Device)这个概念。AVD 是
一个经过配置的模拟器。在创建AVD 时可以配置的选项有:模拟器影像大小、触
摸屏、轨迹球、摄像头、屏幕分辨率、键盘、GSM 、GPS、Audio 录放、SD 卡支
持、缓存区大小等。配置Android 模拟器的具体步骤如下所示。
(1)首先打开“Android SDK and AVD Manager”,如图2-17 所示。
图2-16 显示项目管理器图2-17 Android SDK and AVD Manager 菜单
(2)点击左边的“Virtual Devices”选项,再点击右边的“New...”按钮,
新建一个AVD。
(3)在“Name”标签处填写AVD 的名字,在“Target”标签处选择API 等
级,在“Size”标签处填写要创建的SD 卡的大小,在“Skin”标签中设置模拟
器的风格,如图2-18 所示。
(4)到这里,我们便可以运行第一个Android 项目了吗?还是不行,还需
要配置模拟器运行的AVD。操作步骤为:点击“Run”,选择“Run
Configurations”菜单命令,打开“Run Configurations”对话框,如图2-19
所示。
(5)双击“Run Configurations”对话框左边的导航器中的“Android
Application”菜单命令,创建一个Android 项目运行配置。在右边的“Name”
文本框中输入Android 项目运行配置的名字(HelloAndroid),在“Android”
选项卡中的“Project”文本框中输入要运行的Android 项目,同样可以点击右
边的“Browse...”按钮来选择Android 项目,如图2-20 所示。
图2-18 创建AVD
图2-19 运行配置界面
图2-20 配置要运行的HelloAndroid 项目
(6)点击“Target”选项卡,选择“Automatic”单选框,然后在AVD 列表
框中选择我们刚才创建的AVD,如图2-21 所示。
图2-21 制定运行HelloAndroid 项目的AVD
(7)点击“Run”按钮,这样便可以运行HelloAndroid 项目了,不过Android
模拟器启动非常慢,慢慢等吧。但是Android 的模拟器做得非常漂亮,终于可以
看到第一个Android 项目的运行效果了,如图2-22 所示。
图2-22HelloAndroid 项目在模拟器中的运行效果图2-23 Android 模拟器显示中文界面
从Android SDK 1.5 版本开始,Android 模拟器开始支持中文了,也内置了
中文输入法(谷歌拼音输入法),下面我们就将模拟器改为中文环境。操作步骤
为:启动Android 模拟器,进入Android 模拟器菜单,选择“Settings”菜单项,
开打“Settings”菜单,选择“Locale&text”菜单项,打开“Locale&text”菜
单,依次选择“Select locale”项和“Chinese(China)”项,这样就设置为中
文了,然后返回桌面,如图2-23 所示。
上文我们使用ADT 插件在Eclipse 开发工具中创建了AVD 及设置模拟器等操
作,同样可以在命令行模式下完成上面的操作。
扩展学习
大家已经看到了Android 的模拟界面了,这款模拟器功能非常齐全,电话本、
通话等功能都可正常使用(当然不是真的从模拟器中打电话)。甚至其内置的浏
览器和Google Maps 都可以联网。用户可以使用键盘输入,鼠标点击模拟器按键
输入,甚至还可以使用鼠标点击、拖动屏幕进行操纵。我们在开发项目时,这个
模拟器完全可以满足我们测试的需求。下面我们列举一些常用的模拟器操作。
* 列出模拟器类型:android list targets。
* 创建模拟器:android create avd --target 2 --name cupcake,cupcake
为新建模拟器的名字。
* 列出自己创建的模拟器:android list avd。
* 切换模拟器样式:在创建命令后面加上“--skin QVGA”即可。切换样式:
Windows 操作系统按F7 键即可。
* 删除模拟器:android delete avd --name cupcake,cupcake 为删除的模拟
器的名字。
* 指定用什么模拟器启动:emulator -debug avd_config -avd cupcake,cupcake
为模拟器的名字。
* 将apk 文件安装到Android 模拟器。操作步骤为:首先启动Android 模拟器,
然后打开命令行对话框,进入命令行模式。在命令行模式下进入Android SDK
安装目录下面的tools 文件夹,输入“adb install c:\ poker80.apk”(c:\
poker80.apk 是要安装的文件的路径),这样便可以将apk 文件安装到模拟器上,
如图2-24 所示。
图2-24 安装apk 文件到模拟器
* 卸载模拟器中的apk 文件。操作步骤为:首先启动Android 模拟器,进入命
令行模式。在命令行模式下进入Android SDK 安装目录下面的tools 文件夹,然
后在命令行处依次输入“adb shell”、“cd data”、“cd app”、“ls”
(主要是针对不知道包下面的文件的情况,可以用ls 命令列表显示出来)、“rm
com.fungsing.poker80.apk”命令(“com.fungsing.poker80.apk.apk”是你要
卸载的apk 包),如图2-25 所示。
图2-25 从Android 模拟器卸载apk 文件
2.3.3 调试HelloAndroid
在Eclipse 开发工具中调试程序的方法很多,使用Eclipse 调试Android
程序时需要注意一些细节上的问题。许多刚接触Android 的开发者,在调试
Android 程序时总是不能迅速地找到程序的错误所在,Eclipse+ADT 的开发环境
中没有直接跟踪对象内容的方法,但是我们可以使用Google 提供的ADT 插件
DDMS(Dalvik Debug Monitor Service)在Eclipse 上轻松地调试Android 程序。
DDMS 为我们提供了很多功能,例如:测试设备截屏,针对特定的进程查看正在
运行的线程以及堆信息,Logcat,广播状态信息,模拟电话呼叫,接收SMS,虚
拟地理坐标等等,下面我们通过DDMS 来调试我们的HelloAndroid 项目。
(1)将Eclipse 开发工具的工作界面切换到DDMS 标签。首先确定Eclipse
开发工具右上角是否有“DDMS”标签,如果有,则直接点击该标签即可切换到
DDMS 工作界面,如图2-26 所示。如果没有,则点击“Open Perspective”按钮,
选择“Other...”命令按钮,打开“Open Perspective”对话框,如图2-27 所
示。在“Open Perspective”对话框中选择“DDMS”选项,然后点击“OK”按钮,
如图2-28 所示。
图2-26 DDMS 工作界面切换图2-27 打开视图布局显示操作
图2-28 视图布局选择框
(2)在“DDMS”界面中选择“Devices”标签,查看其菜单的功能,可以看
到Debug Process(调试进程)、Update Threads(更新线程)、Update Heap
(更新堆)、Cause GC(引起垃圾回收)、Stop Process(停止进程)、Screen
Capture(屏幕截图)、Reset adb(重启Android Debug Bridge)菜单选项,
如图2-29 所示。
从图2-29 中可以观察到Android 程序运行时的各种状态,比如进程信息、
线程分析、堆内存的占用,结束一个进程等。当然,这些操作都是在DDMS 框架
下进行的,日常开发的程序是无法执行调用的。如果adb 调试桥运行不稳定,可
以选择“Reset adb”来重新启动“adb.exe”进程。下面我们介绍如何使用DDMS
的“Logcat”来调试Android 程序,步骤如下:
(1)“Logcat”通过“android.util.Log”类的静态方法来查找错误和打
印系统日志消息。它是一个进行日志输出的API,我们在Android 程序中可以随
时为某一个对象插入一个Log,然后在DDMS 中观察Logcat 的输出是否正常。
android.util.Log 常用的方法有以下5 个:
* Log.v(String tag, String msg);
* Log.d(String tag, String msg);
* Log.i(String tag, String msg);
* Log.w(String tag, String msg);
* Log.e(String tag, String msg)。
图2-29 DDMS 操作菜单
这5 种方法的首字母分别对应VERBOSE、DEBUG、INFO、WARN、ERROR。当利
用DDMS 进行调试时,它们的区别并不大,只是显示的颜色不同,可以控制要显
示的某一类错误,一般如果使用“断点”方式来调试程序,则使用Log.e 比较合
适。但是根据规范建议Log.v、Log.d 信息应当只存在于开发过程中,最终版本
只可以包含Log.i、Log.w、Log.e 这三种日志信息。下面我们对“HelloAndroid”
程序进行调试,首先修改“HelloAndroid.java”如代码清代2-1 所示。我们在
代码中加入了需要输出的日志信息。
代码清单2-1 第2 章
\HelloAndroid\src\com\yarin\Android\HelloAndroid\HelloAndroid.java
Java 代码
1. /* 定义TAG 标签,这样可以很好地区分打印出来的Log */
2. private static final String TAG = "HelloAndroid";
3. public void onCreate(Bundle savedInstanceState)
4. {
5. super.onCreate(savedInstanceState);
6. /* 打印出不同的Log 信息*/
7. Log.v(TAG,"VERBOSE");
8. Log.d(TAG,"DEBUG");
9. Log.i(TAG,"INFO");
10. Log.w(TAG,"WARN");
11. Log.e(TAG,"ERROR");
12. setContentView(R.layout.main);
13.}
(2)点击“Run”→“Debug”菜单命令,进入调试模式,如图2-30 所示。
(3)切换到“DDMS”界面,点击“Logcat”标签,即可查看我们刚刚在程
序中打印的Log 信息。用不同颜色表示了不同等级的信息,这样就可方便地对程
序进行跟踪,使得调试Android 程序更加方便。
图2-30 调试菜单命令
在调试Android 程序时,同样可以通过设置断点的方式来调试程序。在启动
应用程序进行调试时,Eclipse 会自动切换到Debug 透视图。毫无疑问,最常
见的调试步骤是设置断点,这样可以检查条件语句或循环内的变量和值。要在
Java 透视图的Package Explorer 视图中设置断点,双击选择的源代码文件,
在一个编辑器中打开它。遍历代码,将鼠标放在可疑代码一行的标记栏(在编辑
器区域的左侧)上,双击即可设置断点,如图2-31 所示。
注意最好不要将多条语句放在一行上,因为会无法单步执行,也不能为同一行
上的多条语句设置行断点。
一旦找到错误发生的位置,你可能想知道在程序崩溃之前它在做什么。一种
方法是单步执行程序的每行语句,直到运行到可疑的那一行。有时候最好只运行
一段代码,在可疑处停止运行,检查数据。另一种方法是声明条件断点,断点在
表达式值发生变化时触发。如图2-32 所示,我们设置条件“savedInstanceState
== null”,当满足这个条件时,程序就会挂起。除此之外,在输入条件表达式
时,也可以使用代码帮助。为了在Debug 透视图的编辑器中计算表达式的值,
选择设置了断点的那行代码,在上下文菜单中,通过Ctrl+Shift+I 或右键单击
你感兴趣的变量并选择Inspect 选项。在当前堆栈框架的上下文中会计算表达
式的值,在Display 窗口的Expressions 视图中会显示结果。
图2-31 设置“断点”
图2-32 设置条件断点
要在Debug 视图中挂起执行线程,选择一个运行线程,单击Debug 视图工
具栏中的Suspend。该线程的当前调用堆栈就会显示出来,当前执行的代码行就
会在Debug 透视图中的编辑器中高亮显示。挂起一个线程时,将鼠标放在Java
编辑器中的变量上,该变量的值就会在一个小的悬停窗口中显示出来。此时,该
线程的顶部堆栈框架也会自动选中,其中的可视变量也会在Variables 视图中
显示出来,可以通过单击Variables 视图中合适的变量名来检查变量。
以上列举了一些在Eclipse 编辑器中常用的调试方式,当然调试的方式很多,读
者同样可以根据自己的需要选择不同的方式进行调试。希望读者能够根据不同的
错误采取不同的方式进行调试,使错误能快速地出现在眼前。
2.4 小结
本章主要对Android 应用开发的前期工作进行了整理,即Android 开发工具
的准备、环境的搭建及配置,最后为了测试我们的环境安装是否正确,写出了一
个最经典的HelloAndroid 程序。同时,了解了Android 平台如何调试程序,以
辅助我们后期能够快速开发出Android 应用。本章是Android 应用开发的基础,
大家好好把握,下面我们将正式对Android 进行系统学习。
第二部分基础篇
第3 章Android 程序设计基础
通过上一章的学习,我们对Eclipse+ADT 开发流程有了初步的认识和了解,
对初学者来说,这一章的内容比较繁琐,但是又必须掌握,这也是进行Android
开发必须经过的第一步,有了这个基础,我们下面将进行正式开始Android 应用
程序设计。
3.1 Android 程序框架
上一章我们建立了HelloAndroid 项目,代码是由ADT 插件自动生成的,我
们没有对其进行编码,所以没有对其框架进行分析。其实每一个平台都有自己的
结构框架,比如我们在最初学习Java 或者C\C++时,第一个程序总是main 函数,
以及文件类型和存储方式等。这一节将对Android 平台的目录结构、文件类型及
其负责的功能和Android 平台的main 函数进行剖析。
3.1.1 Android 项目目录结构
有了前面两章的基础,现在我们再来打开上一章建立的HelloAndroid 项目,
分析其项目目录结构,对Android 项目进一步地深入了解。首先启动Eclipse,
展开“Package Explorer”导航器中的“HelloAndroid”项目,如图3-1 所示。
图3-1 HelloAndroid 项目
与一般的Java 项目一样,src 文件夹是项目的所有包及源文件(.java),
res 文件夹中则包含了项目中的所有资源,比如:程序图标(drawable)、布局
文件(layout)、常量(values)等。下面来介绍其他Java 项目中没有的的gen
文件夹中的R.java 文件和每个Android 项目都必须有的AndroidManfest.xml
文件。
* R.java 是在建立项目时自动生成的,这个文件是只读模式,不能更改,R.java
文件是定义该项目所有资源的索引文件。先来看看HelloAndroid 项目的R.java
文件,如代码清单3-1 所示。
代码清单3-1 第2 章
\HelloAndroid\gen\com\yarin\Android\HelloAndroid\R.java
Java 代码
1. package com.yarin.Android.HelloAndroid;
2. public final class R {
3. public static final class attr {
4. }
5. public static final class drawable {
6. public static final int icon=0x7f020000;
7. }
8. public static final class layout {
9. public static final int main=0x7f030000;
10. }
11. public static final class string {
12. public static final int app_name=0x7f040001;
13. public static final int hello=0x7f040000;
14. }
15.}
可以看到这里定义了很多常量,这些常量的名字都与res 文件夹中的文件名
相同,这再次证明了R.java 文件中所存储的是该项目所有资源的索引。有了这
个文件,可以很快地找到要使用的资源,由于这个文件不能手动编辑,所以当在
项目中加入了新的资源时,只需要刷新一下该项目,R.java 文件便自动生成了
所有资源的索引。
* AndroidManfest.xml 文件则包含了该项目中所使用的Activity、Service、
Receiver,我们先来打开HelloAndroid 项目中的AndroidManfest.xml 文件,如
代码清单3-2 所示。
代码清单3-2 第2 章\HelloAndroid\AndroidManfest.xml
Java 代码
1. <?xml version="1.0" encoding="utf-8"?>
2. <manifest xmlns:android="http://schemas.android.com/apk/res/and
roid"
3. package="com.yarin.Android.HelloAndroid"
4. android:versionCode="1"
5. android:versionName="1.0">
6. <application android:icon="@drawable/icon" android:label="@
string/app_name">
7. <activity android:name=".HelloAndroid"
8. android:label="@string/app_name">
9. <intent-filter>
10. <action android:name="android.intent.action.MAI
N" />
11. <category android:name="android.intent.category.
LAUNCHER" />
12. </intent-filter>
13. </activity>
14. </application>
15. <uses-sdk android:minSdkVersion="5" />
16.</manifest>
代码清单3-2 中,itent-filters 描述了Activity 启动的位置和时间。每
当一个Activity(或者操作系统)要执行一个操作时,它将创建出一个Intent
的对象,这个Intent 对象能承载的信息可描述你想做什么,你想处理什么数据,
数据的类型,以及一些其他信息。而Android 则会和每个Application 所暴露的
intent-filter 的数据进行比较,找到最合适Activity 来处理调用者所指定的
数据和操作。下面我们来仔细分析AndroidManfest.xml 文件,如表3-1 所示。
表3-1 AndroidManfest.xml 分析
下面我们看看资源文件中一些常量的定义,如String.xml,如代码清单3-3
所示。
代码清单3-3 第2 章\HelloAndroid\String.xml
Java 代码
1. <?xml version="1.0" encoding="utf-8"?>
2. <resources>
3. <string name="hello">Hello World, HelloAndroid!</string>
4. <string name="app_name">HelloAndroid</string>
5. </resources>
这个文件很简单,就定义了两个字符串资源,因此,我们可以在代码清单
3-1 中看到如下内容,即定义了“app_name”和“hello”两个常量,分别指向
代码清单3-3 中的两个字符串资源。
Java 代码
1. public static final class string {
2. public static final int app_name=0x7f040001;
3. public static final int hello=0x7f040000;
4. }
那么如何在程序中使用我们所定义的这些资源呢?首先,通过Context 的
getResources 实例化一个Resources 对象,然后通过Resources 的getString
方法取得指定索引的字符串,代码如下:
Java 代码
1. Resources r = this.getContext().getResources();
2. String appname= ((String) r.getString(R.string.app_name));
3. String hello= ((String) r.getString(R.string.hello));
项目中所有使用的常量都可以通过这种XML 文件的方式定义,比如,下面是
我们通过XML 文件定义的一些有关颜色的资源。
Java 代码
1. <?xml version="1.0" encoding="utf-8"?>
2. <resources>
3. <color name="status_idle">#cccccc</color>
4. <color name="status_done">#637a47</color>
5. <color name="status_sync">#cc9900</color>
6. <color name="status_error">#ac4444</color>
7. </resources>
现在来分析HelloAndroid 项目的布局文件(layout),首先打开
res->layout->main.xml 文件,如代码清单3-4 所示。
代码清单3-4 第2 章\HelloAndroid\res\layout\main.xml
Java 代码
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-4 中,有以下几个布局和参数。
* <LinearLayout>:线性版面配置,在这个标签中,所有元件都是按由上到下
的排列排成的。
* android:orientation:表示这个介质的版面配置方式,其中“vertical”
代表从上到下垂直布局,而“horizontal”代表从左到右水平布局。
* android:layout_width:定义当前视图在屏幕上所占的宽度,fill_parent
即填充整个屏幕。
* android:layout_height:定义当前视图在屏幕上所占的高度,fill_parent
即填充整个屏幕。
* wrap_content:随着文字栏位的不同而改变这个视图的宽度或高度。
layout_weight 用于给一个线性布局中的多个视图的重要度赋值。所有视图
都有layout_weight 值,默认为零,即需要显示多大的视图就占据多大的屏幕
空间。如果值大于零,则将父视图中的可用空间分割,分割大小具体取决于每一
个视图的layout_weight 值和该值在当前屏幕布局的整体layout_weight 值,
以及在其他视图屏幕布局的layout_weight 值中所占的比例。
在这里,布局中设置了一个TextView,用来配置文本标签Widget,其中设
置的属性android:layout_width 为整个屏幕的宽度,android:layout_height
可以根据文字来改变高度,而android:text 则设置了这个TextView 要显示的文
字内容,这里引用了@string 中的hello 字符串,即String.xml 文件中的hello
所代表的字符串资源。hello 字符串的内容“Hello World, HelloAndroid!”就
是我们在HelloAndroid 项目运行时看到的字符串。
最后,我们来分析HelloAndroid 项目的主程序文件HelloAndroid.java,
如代码清单3-5 所示。
代码清单3-5 第2 章
\HelloAndroid\src\com\yarin\Android\HelloAndroid\HelloAndroid.java
Java 代码
1. ...
2. public void onCreate(Bundle savedInstanceState)
3. {
4. super.onCreate(savedInstanceState);
5. /* 设置Activity 要显示的布局为(R.layout.main) */
6. setContentView(R.layout.main);
7. }
8. ...
主程序HelloAndroid 类继承自Activity 类,重写了void onCreate(Bundle
savedInstanceState)方法。在onCreate 方法中通过
setContentView(R.layout.main)设置Activity 要显示的布局文件
(\layout\main.xml)。
到这里,是不是明白了为什么我们在创建项目时没有进行编码就可以直接运
行程序呢?当然,这也是Android 开发的特点,这样可以很轻松地将代码和UI
分开,在国际化和程序维护方面有着巨大的作用。如果你的Android 程序需要适
应国际化,比如说多国语言等问题,那么就可以定义不同语言的UI 布局,在程
序装载时调用不同的布局。而且,如果我们需要修改UI 的一些问题,就不必查
看代码了,直接更改这些布局文件即可,是不是很方便?当然,这需要开发者在
开发时使用这种MVC 框架,尽量减少使用“硬编码”。笔者个人建议使用这种框
架。
3.1.2 Android 应用解析
上面我们了解了Android 应用程序的目录结构和其中每个文件的功能,要进
行应用开发,还需要对Android 应用构造进行深入分析。Android 应用程序由4
个模块构造而成:Activity,Intent,Content Provider,Service。
当然,也不是每个Android 应用程序都必须由这4 部分组成,可以根据开发
者需求进行组合,比如上面建立的HelloAndroid 项目就只使用了Activity 这一
个模块。但是,任何一个应用程序都必须在AndroidManfest.xml 文件中声明使
用到的这些模块。
1.Activity
Activity 是最基本的模块,我们在HelloAndroid 项目中已经使用过。我们
称之为“活动”,在应用程序中,一个Activity 通常就是一个单独的屏幕。每
一个活动都被实现为一个独立的类,并且从活动基类中继承而来,活动类将会显
示由视图控件组成的用户接口,并对事件作出响应。例如HelloAndroid 项目中
的HelloAndroid.java 即继承了Activity 类。大多数的应用都是由多个
Activity 显示组成,例如,对一个文本信息应用而言,第一个屏幕用来显示发
送消息的联系人列表,第二个屏幕用来写文本消息和选择收件人,第三个屏幕查
看消息历史或者消息设置操作等。
这里的每一个屏幕就是一个活动,很容易实现从一个屏幕到一个新的屏幕,
并且完成新的活动。当一个新的屏幕打开后,前一个屏幕将会暂停,并保存在历
史栈中。用户可以返回到历史栈中的前一个屏幕,当屏幕不再使用时,还可以从
历史栈中删除。
简单理解,Activity 代表一个用户所能看到的屏幕,主要用于处理应用程
序的整体性工作,例如,监听系统事件(按键事件、触摸屏事件等),为用户显
示指定的View,启动其他Activity 等。所有应用的Activity 都继承于
android.app.Activity 类,该类是Android 提供的基层类,其他的Activity 继
承该父类后,通过父类的方法来实现各种功能,这种设计在其他领域也较为常见。
2.Intent
Android 用Intent 这个特殊类实现在Activity 与Activity 之间的切换。
Intent 类用于描述应用的功能。在Intent 的描述结构中,有两个最重要的部分:
动作和动作对应的数据。典型的动作类型有MAIN、VIEW、PICK、EDIT 等,而动
作对应的数据则以URI 的形式表示。例如,要查看一个人的联系方式,需要创建
一个动作类型为VIEW 的Intent,以及一个表示这个人的URI。
通过解析各种Intent,从一个屏幕导航到另一个屏幕是很简单的。当向前
导航时,Activity 将会调用startActivity(IntentmyIntent)方法。然后,系统
会在所有已安装的应用程序中定义的IntentFilter 中查找,找到最匹配
myIntent 的Intent 对应的Activity。新的Activity 接收到myIntent 的通知后,
开始运行。当startActivity 方法被调用时,将触发解析myIntent 的动作,该
机制提供了两个关键好处:
* Activity 能够重复利用从其他组件中以Intent 形式产生的请求。
* Activity 可以在任何时候被具有相同IntentFilter 的新的Activity 取代。
下面我们举例说明两个Activity 之间的切换。运行效果:当应用程序启动
时显示布局main.xml,如图3-2 所示,当点击“切换”按钮时,屏幕显示布局
main2.xml,如图3-3 所示,再点击“切换”按钮,又回到如图3-2 所示界面。
就这样通过Intent 完成了两个Activity 之间的切换。
图3-2 Activity01 图3-3 Activity02
下面我们来分析一下代码的具体实现,我们知道该项目是由两个Activity
构成,在这两个Activity 中分别显示了一个文本标签和一个按钮,关于界面的
布局会在本书第4 章进行详细讲解,要实现两个Activity 的跳转,我们可以将
要跳转的Activity 类名绑定到Intent 对象中,然后通过startActivity 方法激
活Intent 对象中所指定的Activity。关键代码如代码清单3-6 所示。
代码清单3-6 第3 章
\Examples_03_01\src\com\yarin\android\Examples_03_01\Activity01.java
Java 代码
1. /* 监听button 的事件信息*/
2. button.setOnClickListener(new Button.OnClickListener() {
3. public void onClick(View v)
4. {
5. /* 新建一个Intent 对象*/
6. Intent intent = new Intent();
7. /* 指定intent 要启动的类*/
8. intent.setClass(Activity01.this, Activity02.class);
9. /* 启动一个新的Activity */
10. startActivity(intent);
11. /* 关闭当前的Activity */
12. Activity01.this.finish();
13. }
14.});
然后,我们要从Activity02 跳转到Activity01 时,就只是需要在
Activity02.java 中使用同样的方法返回Activity01 中。大家可以参考本书所
附代码:第3 章\Examples_03_01\src\com\yarin\android\
Examples_03_01\ Activity02.java。值得注意的是,该项目中我们使用了两个
Activity,每一个Activity 都需要在AndroidManifest.xml 文件中进行声明,
声明方法如代码清单3-7 所示。
代码清单3-7 第3 章\Examples_03_01\AndroidManifest.xml
Java 代码
1. <activity android:name=".Activity01"
2. android:label="@string/app_name">
3. <intent-filter>
4. <action android:name="android.intent.action.MAIN" />
5. <category android:name="android.intent.category.LAUNCHE
R" />
6. </intent-filter>
7. </activity>
8. <activity android:name="Activity02"></activity>
9.
如果希望Android 应用能够对外部事件(如当电话呼入时,或者数据网络可
用时,或者到了晚上时)做出响应,可以使用IntentReceiver。虽然
IntentReceiver 在感兴趣的事件发生时会使用NotificationManager 通知用户,
但它并不能生成UI。IntentReceiver 在AndroidManifest.xml 中注册,但也可
以在代码中使用Context.registerReceiver()进行注册。当IntentReceiver 被
触发时,应用不必对请求调用IntentReceiver,系统会在需要时启动应用。各
种应用还可以通过使用Context.broadcastIntent()将它们自己的
IntentReceiver 广播给其他应用。
3.Content Provider
Android 应用能够将它们的数据保存到文件和SQLite 数据库中,甚至是任
何有效的设备中。当想将应用数据与其他的应用共享时,Content Provider 就
可以发挥作用了。因为Content Provider 类实现了一组标准的方法,能够让其
他的应用保存或读取此内容提供器处理的各种数据类型。
数据是应用的核心。在Android 中,默认使用鼎鼎大名的SQLite 作为系统
数据库。但是在Android 中,使用方法有点不一样。在Android 中,每一个应用
都运行在各自的进程中,当一个应用需要访问其他应用的数据时,也就是数据需
要在不同的虚拟机之间传递,这样的情况操作起来可能有些困难(正常情况下,
不能读取其他应用的db 文件),Content Provider 正是用来解决在不同的应用
包之间共享数据的工具。
在Android 中,Content Provider 是一个特殊的存储数据的类型,它提供
了一套标准的接口用来获取和操作数据。并且,Android 自身也提供了现成的
Content Provider:Contacts、Browser、CallLog、Settings、MediaStore。应
用可以通过唯一的ContentResolver 界面来使用具体的某个Content Provider,
然后就可以用ContentResolver 提供的方法来使用你需要的Content Provider
了。其中,ContentResolver 提供的方法包括query()、insert()、update()等。
要使用这些方法,还会涉及URI。你可以将它理解成string 形式的Content
Provider 的完全路径。
下面我们通过一个例子来学习Content Provider 的使用,该例子主要实现
通过Content Provider 获得电话本中的数据,然后显示到一个TextView 中,在
运行程序之前我们先看看电话本中存储的电话号码,如图3-4 所示,然后再运行
程序看看我们获得的数据,如图3-5 所示,并看看我们通过Content Provider
获得的数据是否正确。
图3-4 电话本数据图3-5 通过ContentProvider 获得电话本数据
下面我们来分析一下如何实现通过ContentProvider 取得电话本的数据,首
先通过getContentResolver 方法来取得一个ContentResolver 对象,然后通过
其query 方法查询出符合标准的电话本记录,最后将这些数据都显示在一个
TextView 中即可,如代码清单3-8 所示。
代码清单3-8 第3 章
\Examples_03_02\src\com\yarin\android\Examples_03_02\Activity01.java
Java 代码
1. public class Activity01 extends Activity
2. {
3. public void onCreate(Bundle savedInstanceState)
4. {
5. TextView tv = new TextView(this);
6. String string = "";
7. super.onCreate(savedInstanceState);
8. //得到ContentResolver 对象
9. ContentResolver cr = getContentResolver();
10. //取得电话本中开始一项的光标
11. Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null,
12. null, null);
13. //向下移动光标
14. while(cursor.moveToNext())
15. {
16. //取得联系人名字
17. int nameFieldColumnIndex = cursor.getColumnIndex(PhoneLookup.
18. DISPLAY_NAME);
19. String contact = cursor.getString(nameFieldColumnIndex);
20. //取得电话号码
21. int numberFieldColumnIndex = cursor.getColumnIndex(PhoneLooku
p.
22. NUMBER);
23. String number = cursor.getString(numberFieldColumnIndex);
24.
25. string += (contact+":"+number+"\n");
26. }
27. cursor.close();
28. //设置TextView 显示的内容
29. tv.setText(string);
30. //显示到屏幕
31. setContentView(tv);
32. }
33.}
前面强调过,要使用这些模块,需要在AndroidManifest.xml 声明,本例中
我们使用了读取联系人的API,因此,声明方式如下所示:
Java 代码
1. <uses-permission
2. android:name="android.permission.READ_CONTACTS">
3. </uses-permission>
4.Service
Service 即“服务”的意思,既然是服务,那么Service 将是一个生命周期
长且没有用户界面的程序。比如一个正在从播放列表中播放歌曲的媒体播放器,
在这个媒体播放器应用中,应该会有多个Activity,让使用者可以选择歌曲并
播放歌曲。然而,音乐重放这个功能并没有对应的Activity,因为使用者会认
为在导航到其他屏幕时音乐应该还在播放。在这个例子中,媒体播放器这个
Activity 会使用Context.startService()来启动一个Service,从而可以在后
台保持音乐的播放。同时,系统也将保持这个Service 一直执行,直到这个
Service 运行结束。另外,还可以通过使用Context.bindService()方法连接到
一个Service 上(如果这个Service 当前还没有处于启动状态,则将启动它)。
当连接到一个Service 之后,还可用Service 提供的接口与它进行通信。以媒体
播放器为例,我们还可以执行暂停、重播等操作。
下面通过一个例子来学习Service 的使用,该例子通过Service 来播放一首
MP3,如图3-6 所示。当用户点击“开始”按钮,音乐开始播放;点击“停止”
按钮,停止音乐播放。当然,这里需要在资源文件中添加一首MP3 歌曲,如图
3-7 所示。
要实现音乐的播放,需要在界面中放置两个按钮,用来控制音乐的播放和停
止。而我们的音乐播放是通过一个服务来实现的,所以我们可以通过
startService 和stopService 方法来开启和停止这个播放音乐的服务,如代码
清单3-9 所示。
图3-6 使用Service 播放音乐图3-7 test.mp3
代码清单3-9 第3 章
\Examples_03_03\src\com\yarin\android\Examples_03_03\ Activity01.java
Java 代码
1. //开始按钮
2. private OnClickListener start = new OnClickListener()
3. {
4. public void onClick(View v)
5. {
6. //开启Service
7. startService(new Intent("com.yarin.Android.MUSIC"));
8. }
9. };
10.//停止按钮
11.private OnClickListener stop = new OnClickListener()
12.{
13. public void onClick(View v)
14. {
15. //停止Service
16. stopService(new Intent("com.yarin.Android.MUSIC"));
17. }
18.};
下面是该例子的核心内容。如何通过Service 来播放音乐,其实也很简单,
首先创建一个MusicService 继承自Service,然后通过start 和stop 方法来控
制音乐的播放,如代码清单3-10 所示。具体实现请参见本书所附代码:第3 章
\Examples_03_03。
代码清单3-10 第3 章
\Examples_03_03\src\com\yarin\android\Examples_03_03
\MusicService.java
Java 代码
1. public class MusicService extends Service
2. {
3. //MediaPlayer 对象
4. private MediaPlayer player;
5. public IBinder onBind(Intent arg0)
6. {
7. return null;
8. }
9. public void onStart(Intent intent, int startId)
10. {
11. super.onStart(intent, startId);
12. //这里可以理解为装载音乐文件
13. player = MediaPlayer.create(this, R.raw.test);
14. //开始播放
15. player.start();
16. }
17. public void onDestroy()
18. {
19. super.onDestroy();
20. //停止音乐—停止Service
21. player.stop();
22. }
23.}
我们使用Service 时同样需要在AndroidManifest.xml 中声明,声明方式如
代码清单3-11 所示。
代码清单3-11 第3 章\Examples_03_03 \AndroidManifest.xml
Java 代码
1. <service android:name=".MusicService">
2. <intent-filter>
3. <action android:name="com.yarin.Android.MUSIC" />
4. <category android:name="android.intent.category.default
" />
5. </intent-filter>
6. </service>
3.2 Android 的生命周期
在前面的几个例子中,我们发现所有继承自Activity 的类都重写了
onCreate 方法,程序运行就会自动进入这个方法。其实Activity 类中还有很多
类似于onCreate 的方法,比如onStart、onResume、onPause、onDestroy 等,
而这些方法都是系统自动调用,从名字上大概就可以看出这是一些关于生命周期
的方法,那么这些方法被调用的先后顺序是怎样的呢?Android 应用的生命周期
又是如何呢?下面通过一个例子来进一步分析。
当应用程序启动时,进入如图3-8 所示的Activity01 界面,此时,点击
“Activity02”按钮,进入Activity02 界面,如图3-9 所示。再点击
“Activity01”按钮,返回Activity01 界面,最后点击“Exit”按钮退出整个
应用程序。
图3-8 Activity01 界面图3-9 Activity02 界面
我们在这些类似于onCreate 的方法中都加入了log 函数,输出不同的信息,
以便我们能更好地跟踪程序运行的过程,具体实现参见本书所附代码:第3 章
\Examples_03_04。
首先,我们需要在程序启动所默认的第一个界面中,加入一些Log 函数,用
于显示和输出Log 信息,以帮助我们分析程序的执行流程,如代码清单3-12 所
示。
代码清单3-12 第3 章
\Examples_03_04\src\com\yarin\android\Examples_03_04 \Activity01.java
Java 代码
1. public class Activity01 extends Activity
2. {
3. private static final String TAG = "Activity01";
4. public void onCreate(Bundle savedInstanceState)
5. {
6. super.onCreate(savedInstanceState);
7. setContentView(R.layout.main);
8. Log.v(TAG, "onCreate");
9.
10. Button button1 = (Button) findViewById(R.id.button1);
11. /* 监听button 的事件信息*/
12. button1.setOnClickListener(new Button.OnClickListener() {
13. public void onClick(View v)
14. {
15. /* 新建一个Intent 对象*/
16. Intent intent = new Intent();
17. /* 指定intent 要启动的类*/
18. intent.setClass(Activity01.this, Activity02.class);
19. /* 启动一个新的Activity */
20. startActivity(intent);
21. /* 关闭当前的Activity */
22. Activity01.this.finish();
23. }
24. });
25. /******************************/
26. Button button3 = (Button) findViewById(R.id.button3);
27. /* 监听button 的事件信息*/
28. button3.setOnClickListener(new Button.OnClickListener() {
29. public void onClick(View v)
30. {
31. /* 关闭当前的Activity */
32. Activity01.this.finish();
33. }
34. });
35. }
36. public void onStart()
37. {
38. super.onStart();
39. Log.v(TAG, "onStart");
40. }
41.
42. public void onResume()
43. {
44. super.onResume();
45. Log.v(TAG, "onResume");
46. }
47.
48. public void onPause()
49. {
50. super.onPause();
51. Log.v(TAG, "onPause");
52. }
53.
54. public void onStop()
55. {
56. super.onStop();
57. Log.v(TAG, "onStop");
58. }
59. public void onDestroy()
60. {
61. super.onDestroy();
62. Log.v(TAG, "onDestroy");
63. }
64. public void onRestart()
65. {
66. super.onRestart();
67. Log.v(TAG, "onReStart");
68. }
69.
70.}
在第二个界面中,同第一个界面一样,加入一些可以区分的不同的Log 信息。
同样需要在AndroidManifest.xml 文件中声明所使用的两个Activity 模块,
如代码清单3-13 所示。具体实现请参见本书所附代码:第3 章\Examples_03_04。
代码清单3-13 第3 章\Examples_03_04\AndroidManifest.xml
Java 代码
1. <activity android:name=".Activity01"
2. android:label="@string/app_name">
3. <intent-filter>
4. <action android:name="android.intent.action.MAIN" />
5. <category android:name="android.intent.category.LAUNCHE
R" />
6. </intent-filter>
7. </activity>
8. <activity android:name="Activity02"></activity>
当在Debug 该项目时,切换到DDMS 标签即可以看到所打印出来的Log 信息,
这样就可以很清楚地分析程序的运行过程。
当程序第一次启动时,打印的Log 信息如图3-10 所示。我们看到程序的运
行顺序为:Activity01 onCreate→Activity01 onStart →Activity01 onResume。
这里我们可以看到,当一个Activity 启动时,不是“创建”之后“开始”就完
了,而是要经过“创建”,然后“开始”,最后“重绘”。
图3-10 第一次启动进入Activity01 界面
当我们进入Activity02 界面时,打印出的Log 信息如图3-11 所示。我们看
到程序的运行顺序为:Activity01 onPause→Activity02 onCreate→Activity02
onStart→Activity02 onResume→Activity01 onStop→Activity01 onDestroy。
这里我们看到,当程序从Activity01 界面进入Activity02 界面时,并不是马上
将Activity01 销毁,而是待Activity02 启动之后将Activity01 停止并销毁。
当我们返回Activity01 界面时,打印出的Log 信息如图3-12 所示。我们看
到程序的运行顺序为:Activity02 onPause→Activity01 onCreate→Activity01
onStart→Activity01 onResume→Activity02 onStop→Activity02
onDestroy。这里我们看到,当程序从Activity02 界面返回Activity01 界面时,
并不是马上将Activity02 销毁,而是待Activity01 启动之后将Activity02 停
止并销毁。
图3-11 进入Activity02 界面
图3-12 返回Activity01 界面
最后,当我们点击“Exit”按钮退出应用程序时,打印出的Log 信息如图
3-13 所示。这里我们看到程序的运行顺序为:Activity01 onPause→Activity01
onStop→Activity01 onDestroy。这里我们看到当一个应用程序在退出时,并不
是马上“停止”且“销毁”,而是经过“暂停”,到“停止”,然后再“销毁”。
图3-13 退出应用程序
通过上面的例子,我们得出Android 应用程序的生命周期如图3-14 所示。
图3-14 Android 应用的生命周期
3.3 Android 程序UI 设计
在前面章节的例子中,我们已经接触了一些UI 控件,比如TextView、Button
等,其实这里所说的UI 就是在我们所说的布局文件,UI 是一个应用程序的脸面,
一个应用程序要想受用户喜爱, UI 可不能差。自从Android SDK 1.0_r2 版本
开始,ADT 提供了UI 预览的功能。现在我们只需要打开一个Android 项目的
“/res/ layout/main.xml”并右键单击,依次选择“Open With”→“Android
layout Editor”菜单命令,或者直接双击main.xml 文件,即可以切换到UI 设
计界面,如图3-15 所示。
图3-15 Android Layout Editor 命令
左边的Layouts 标签的内容则是一些线性布局,可以使用它轻松地完成对布
局的排版,比如横向或者纵向布局。Views 标签则是一些UI 控件,可以将这些
控件直接拖动到右边的窗口进行编辑,如图3-16 所示。
图3-16 Android Layout Editor
当然,还可以点击右下角的main.xml 标签来切换到XML 编辑器,对代码进
行编排,如图3-17 所示。将这些功能配合起来使用,基本可以满足开发者需求。
除了这个还不算太成熟的UI 编辑器之外,笔者曾经使用过一个第三方的工具
DroidDraw,DroidDraw 是一个公开了源代码的UI 设计器,可以根据自己的开发
需要进行修改。www. DroidDraw.org 提供了在线使用DroidDraw 的功能,当然
也可以下载到本地来进行编辑,下载地址:
http://code.google.com/p/droiddraw/。
图3-17 XML 编辑器
DroidDraw 的功能比较强大,可以直接拖动控件到窗口,然后设置其属性、
参数等,这样便可以随心所欲地设计自己需要的UI,然后点击“Generate”按
钮即可生成出对应的布局代码,同时也可以点击“Load”按钮来载入已经编辑好
的布局文件,如图3-18 所示。
图3-18 DroidDraw 操作界面
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics