2023年12月21日发(作者:谷梁灵凡)
第一部分 准备篇
第1章 Android开发简介
在Google及其开放手机联盟推出基于Linux平台的开源手机操作系统Android之后,Google又不惜重金举办了Android开发者大赛,吸引了众多开发者的目光。Android不仅功能强大,而且具有开放和免费等先天优势,全球范围内的电信行业、手机制造商因此毫不犹豫地加入到Android开放手机联盟中来。2008年9月22日,美国运营商T-Mobile USA在纽约正式发布了第一款基于Android的手机--T-Mobile G1。这让更多的移动设备厂商看到了Android的光明前景,并纷纷加入其中,Android甚至已经涉足上网本市场。中国移动也在Android的基础之上推出了自己的操作系统OMS,而基于OMS操作系统的联想O1手机也即将上市,2009年年底将会有更多的Android手机出现。
随着Android手机的普及,Android应用的需求势必会越来越大,这将是一个潜力巨大的市场,会吸引无数软件开发厂商和开发者投身其中。作为程序员的我们,当然也不应该落后于人,赶快加入到Android应用的开发阵营中来吧!
1.1 Android基本概念
Android一词本意是指“机器人”,当然现在大家都知道它是Google推出的开源手机操作系统。Android基于Linux平台,由操作系统、中间件、用户界面和应用软件组成,号称是首个为移动终端打造的真正开放和完整的移动软件。它是由一个由 30 多家科技公司和手机公司组成的“开放手机联盟”共同研发的,这将大大降低新型手机设备的研发成本。完全整合的全移动功能性产品成为“开放手机联盟”的最终目标。
1.1.1 Android简介
Android 作为Google移动互联网战略的重要组成部分,将进一步推进“随时随地为每个人提供信息”这一企业目标的实现。Google的目标是让移动通信不依赖于设备,甚至是平台。出于这个目的,Android 将完善而不是替代Google长期以来推行的移动发展战略:通过与全球各地的手机制造商和移动运营商成为合作伙伴,开发既实用又有吸引力的移动服务,并推广这些产品。
Android平台的研发队伍阵容强大,包括Google、HTC(宏达电)、T-Mobile、高通、摩托罗拉、三星、LG以及中国移动在内的30多家企业都将基于该平台开发手机的新型业务,应用之间的通用性和互联性将在最大程度上得到保持。“开放手机联盟”表示,Android平台可以促使移动设备的创新,让用户体验到最优质的移动服务。同时,开发商也将得到一个新的开放级别,更方便地进行协同合作,从而保障新型移动设备的研发速度。因此Android 是第一个完整、开放、免费的手机平台。下面我们来欣赏一下第一款基于Android操作系统的手机G1,外观相当漂亮,如图1-1所示。
Android系统具有如下5个特点:
* 开放性。Google与开放手机联盟合作开发了 Android,Google通过与运营商、设备制造商、开发商和其他有关各方结成深层次的合作伙伴关系,希望通过建立标准化、开放式的移动电话软件平台,在移动产业内形成一个开放式的生态系统。
* 应用程序无界限。Android 上的应用程序可以通过标准 API 访问核心移动设备功能。通过互联网,应用程序可以声明它们的功能可供其他应用程序使用。
* 应用程序是在平等的条件下创建的。移动设备上的应用程序可以被替换或扩展,即使是拨号程序或主屏幕这样的核心组件。
* 应用程序可以轻松地嵌入网络。应用程序可以轻松地嵌入HTML、JavaScript 和样式表,还可以通过
WebView 显示网络内容。
* 应用程序可以并行运行。Android 是一种完整的多任务环境,应用程序可以在其中并行运行。在后台运行时,应用程序可以生成通知以引起注意。
为什么Android手机如此受用户青睐,下面我们来看看Android究竟有些什么功能在吸引着我们。
(1)智能虚拟键盘。虚拟键盘的出现意味着基于Android 1.5或以上版本(Android 2.0)的移动设备可以同时支持物理键盘和虚拟键盘。不同的输入方式可满足用户在特定场景的需求。Android虚拟键盘可以在任何应用中提供,包括Gmail、浏览器、SMS,当然也包括大量的第三方应用,如自动校正、推荐、用户词典等。不同于其他手机平台,Android 1.5及其以上的版本还支持第三方虚拟键盘应用的安装,如图1-2所示。
(2)使用Widget实现桌面个性化。可以用Widget“武装”自己的桌面。大多数小的Web应用都是从网络上获得实时数据并展示给用户的。Android预装了5个桌面Widget,包括数字时钟、日历、音乐播放器、相框和搜索。不同于iPhone,Android通过内置的应用程序库安装第三方Widget,如图1-3所示。
图1-1 Android G1 图1-2
虚拟键盘
图1-3 用Widget实现个性化桌面
(3)用在线文件夹快速浏览在线数据。类似于OS X Leopard的QuickLook特征,Android的在线文件夹可显示常见的数据条目,比如联系人、喜欢的应用、E-mail信息、播放列表、书签、RSS源等,并不需要运行系统程序处理特定的数据条目。在线文件夹数据实时更新,就像通过云或是本地创建新的数据。什么是最好的,开发者可以拓展通用数据条目和注册新数据类型的内置支持。例如,Twitter客户端程序可以注册tweet作为新数据类型,因此可以让你从你的朋友那里创建tweet的在线文件。Android可以为我们的个人桌面提供一组在线文件夹,从而帮助我们快速、方便地浏览联系人、股市、书签等信息。
(4)视频录制和分享。Android还有录制和分享视频的功能,对回放和MPEG-4、3GP等视频格式也有了更好的支持。可以通过E-mail、MMS或直接上传到YouTube等方式来分享视频,使用隐私控制来决定是分享给朋友还是每个人。上传视频的同时,可以继续使用手机,甚至可以继续录制和上传新的视频。如图1-4所示,通过YouTube分享录制的视频。
(5)图片上传。在线分享图片需要的点击更少。完成照相后,当浏览图片或选择Google在线图片服务Picasa时,只需轻点“分享”就会拥有1GB的免费图片存储空间。
(6)更快、更兼容的浏览器。Android的基于Webkit内核的浏览器带来了重要的调速装置(SpeedPumb),这得益于新的Webkit渲染引擎和优化的Java脚本编译器(SquireIFish)。当使用包含大量Java脚本的复杂Web应用时,可以体验到更佳的性能。除提高速度外,Android的浏览器还支持Web页面内的复制和粘贴操作,用户可以选中文本并复制,然后粘贴到搜索框中进行搜索。
(7)Voice Search语音搜索。带有语音识别技术的Google手机已于2008年11月面世,它支持语音搜索功能。该功能增强了默认的搜索能力,已超过纯文本搜索。当你大声说出要搜索的内容后,Android将上传数字信号并记录到Google服务器中。在服务器中,语音识别技术能将语音转化为特定的文本搜索,使之通过Google搜索引擎,通过地理位置的筛选,将结果反馈到手机设备。图1-5显示了Google文本和语音搜索桌面。
(8)立体声蓝牙和免提电话。除了增强的免提电话体验,Android还支持立体声蓝牙(A2DP和AVCRP),并有自动配对功能。
(9)强大的GPS技术。Android内部提供了大量GPS组件,我们可以很轻松地获得设备当前的位置等信息,让导航等功能更加完美。
(10)Android系统硬件检测。Android可自动检测和修复SD卡的文件系统,允许第三方应用显示Android系统的硬件特征。为了让用户下载到与自己的设备更匹配的应用,我们可以检测用户设备的硬件信息,让满足应用要求的设备安装该程序,当更多的Android设备建立在不同的硬件上时,这个功能会显得很实用。
图1-4 通过YouTube分享录制的视频 图1-5 Google文本和语音搜索桌面
1.1.2 Android的系统构架
通过上一节的介绍,我们对Android的特点以及它为什么会如此受欢迎有了初步的了解。下面将讨论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这个框架都有些什么功能可供我们使用。
* :提供高层的程序模型和基本的运行环境。
* t:包含对各种设备上的数据进行访问和发布。
* se:通过内容提供者浏览和操作数据库。
* cs:底层的图形库,包含画布、颜色过滤、点、矩形,可以将它们直接绘制到屏幕上。
* on :定位和相关服务的类。
* :提供一些类管理多种音频、视频的媒体接口。
* :提供帮助网络访问的类,超过通常的.* 接口。
* :提供了系统服务、消息传输和IPC机制。
* :提供OpenGL的工具。
* er:提供访问Android内容提供者的类。
* ony:提供与拨打电话相关的API交互。
* :提供基础的用户界面接口框架。
* :涉及工具性的方法,例如时间日期的操作。
* :默认浏览器操作接口。
* :包含各种UI元素(大部分是可见的)在应用程序的布局中使用。
1.2 OMS介绍
OMS是Open Mobile System的简称,即面向移动互联网的开放型移动智能终端软件平台,它包括基于Linux 2.6内核的移动终端下层操作系统、上层应用软件、中间件、Java虚拟机、硬件参考设计以及基于WebKit的各类应用。它具有强大的兼容性、扩展性和安全性,以及简单易用、友好的人机界面等,而且具有完全自主的知识产权。在此之上,OMS拥有开放统一的API开发接口、完备的集成开发环境和活跃的在线生态环境,极大地方便了移动应用的开发。
OMS的可移植性将使该软件平台在其他领域具有广泛的应用,如航空航天、军事、制造业等。
1.2.1 OPhone介绍
OPhone是基于Linux的面向移动互联网的终端基础软件及系统解决方案。由于OPhone与Android兼容,都是基于Java开发的,因此可以同时用OMS API和Android API来开发OMS应用。任何用Android API开发的应用都可以在OMS终端上正确地运行。然而,不能在Android终端上运行由扩展的OMS API开发的程序,因为这些OMS API是OMS平台独有的,而且在运行时是必需的。
OPhone是指采用了OMS智能操作系统的手机。为了突破TD终端瓶颈,以及促进手机终端与中国移动的网络和应用服务进行无缝对接,中国移动在Android操作系统基础上自主开发了OMS系统,该系统直接内置了中国移动的服务菜单、音乐随身听、手机导航、号簿管家、139邮箱、飞信、快讯和移动梦网等特色业务,如图1-7所示。
1.2.2 Widget介绍
OMS除了支持基于Java的应用,还支持Widget应用开发。Widget应用是OMS的精华,而Android从1.5版本开始同样支持Widget应用开发,但是所采用的标准则和OMS不同,我们会在后面的章节详细讲解。
Widget应用采用了 JIL (Joint Innovation Lab)Widget标准。JIL Widget是一个采用HTML、JavaScript和CSS等网络技术的应用程序。Widget应用是在Widget引擎上运行的独立的应用程序。Widget已经成为手机上非常流行的技术,可以为用户带来良好的移动互联网体验,随时随地获取有用的资讯,如天气预报、股票信息、头条新闻等。从用户的角度来看,Widget应用和OPhone应用没有什么区别。实际上,Widget应用不同于OPhone应用。OPhone应用是采用Java技术的应用程序,而Widget应用则是采用HTML、JavaScript和CSS等网络技术的应用程序。相比较而言,Widget应用的开发更加方便快捷。此外,JIL Widget还提供了许多JavaScript API来扩展Widget应用的能力,如访问手机电话本、手机文件系统等。Widget应用运行效果如图1-8所示。
图1-7 OPhone系统界面
图1-8 HelloWidget效果预览
1.3 小结
本章主要介绍了与Android相关的一些基本概念,同时分析了Android系统的特点及其功能,简单介绍了目前主流的7个Linux平台手机以及中国移动的OMS操作系统。在介绍Android基本概念时重点介绍了Android系统架构和应用框架,其中应用层即是我们使用Java语言编写的一些运行在虚拟机上的程序,应用框架层是我们开发应用时接触最为紧密的一层,在开发应用程序时必须遵守其规则,才能保证所开发的应用程序能在Android上安全地运行。大家应着重理解这两层,这样才能开发出效率更高的应用程序。
据称今年底将会有18款之多的以Android平台为系统的Linux手机上市, Android将在移动开发中具有更广阔的前景。要想成为一个优秀的Android手机开发者,还需要从基础做起,希望大家好好掌握本章的内容。
第2章 Android开发环境搭建
本章讲解如何配置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)登录,下载最新版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”项。接着点击“”按钮,在“Location”文本框中输入Babel更新地址:/
technology/babel/update-site/ganymede,然后点击 OK 按钮,如图2-4所示。
图2-4 添加语言包更新地址
(2)“Avaliable Software”表中会多出一项/technology/babel/update-site/ganymede/,点击该项左边的箭头,就会出现网络更新软件列表,如图2-5所示。
图2-5 Avaliable Software选择框
(3)选择“Simplified Chinese”语言包后,点击 “ ”按钮,等待Eclipse处理。
处理完成后会出现“Install”对话框,这时会提示你选择要安装的语言包。根据提示,很容易完成后面的操作,这里就不再赘述了。
安装完毕后,重新启动Eclipse即可完成全部汉化过程。
如果重启Eclipse后不显示中文,请用命令行 “ -nl zh_CN”重新启动Eclipse。
2.2.3 SDK和ADT的安装和配置
安装了JDK和Eclipse后,现在就要安装Android SDK和ADT插件了。
1.Android SDK安装
(1)解压缩下载好的SDK安装包到要安装SDK的路径,然后运行“SDK ”。
(2)如果遇到了消息为“Failed to fetch URL…”的错误提示,那么需要将HTTPS方式改为HTTP方式,在“Android SDK and AVD Manager”窗口的左侧选择 “Settings”,选中 “Force …”选项(如图2-6
所示),点击“Save & Apply”并重新运行 SDK 。
图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”菜单,依次选择“”项和“Avaiable Software”选项卡,点击“”按钮,输入地址/android/eclipse/,结果如图2-10所示。
(2)点击“OK”,这时可能会出现如图2-11所示的错误。
图2-10 添加ADT的更新地址 图2-11 更新地址错误
解决这个问题的方法是:将“/android/eclipse/”中的“https”更改为“http”,在接下来的对话框中选中“Name”下的所有选项,根据提示即可完成后续的安装过程。
(3)打开菜单“Windows”,依次选择“Preferences”→“Android”,点击“”按钮,选择 Android SDK的安装路径,如图2-12所示。
图2-12 Eclipse首选项
(4)点击“OK”按钮 ,开打菜单“File”,依次选择“NEW” → “”菜单命令,出现如图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文本框中输入应用程序包的名字(ndroid),在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”选项,再点击右边的“”按钮,新建一个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项目,同样可以点击右边的“”按钮来选择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:
”(c: 是要安装的文件的路径),这样便可以将apk文件安装到模拟器上,如图2-24所示。
图2-24 安装apk文件到模拟器
* 卸载模拟器中的apk文件。操作步骤为:首先启动Android模拟器,进入命令行模式。在命令行模式下进入Android SDK安装目录下面的tools文件夹,然后在命令行处依次输入“adb shell”、“cd data”、“cd
app”、“ls”(主要是针对不知道包下面的文件的情况,可以用ls命令列表显示出来)、“rm
”命令(“”是你要卸载的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”按钮,选择“”命令按钮,打开“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”来重新启动“”进程。下面我们介绍如何使用DDMS的“Logcat”来调试Android程序,步骤如下:
(1)“Logcat”通过“”类的静态方法来查找错误和打印系统日志消息。它是一个进行日志输出的API,我们在Android 程序中可以随时为某一个对象插入一个Log,然后在DDMS中观察Logcat的输出是否正常。常用的方法有以下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”程序进行调试,首先修改“”如代码清代2-1所示。我们在代码中加入了需要输出的日志信息。
代码清单2-1 第2章
Java代码
/* 定义TAG标签,这样可以很好地区分打印出来的Log */
private static final String TAG = "HelloAndroid";
public void onCreate(Bundle savedInstanceState)
{
te(savedInstanceState);
/* 打印出不同的Log信息 */
Log.v(TAG,"VERBOSE");
Log.d(TAG,"DEBUG");
Log.i(TAG,"INFO");
Log.w(TAG,"WARN");
Log.e(TAG,"ERROR");
setContentView();
}
(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或者CC++时,
第一个程序总是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文件夹中的文件和每个Android项目都必须有的文件。
* 是在建立项目时自动生成的,这个文件是只读模式,不能更改,文件是定义该项目所有资源的索引文件。先来看看HelloAndroid项目的文件,如代码清单3-1所示。
代码清单3-1 第2章
Java代码
package ndroid;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
}
}
可以看到这里定义了很多常量,这些常量的名字都与res文件夹中的文件名相同,这再次证明了文件中所存储的是该项目所有资源的索引。有了这个文件,可以很快地找到要使用的资源,由于这个文件不能手动编辑,所以当在项目中加入了新的资源时,只需要刷新一下该项目,文件便自动生成了所有资源的索引。
* 文件则包含了该项目中所使用的Activity、Service、Receiver,我们先来打开HelloAndroid项目中的文件,如代码清单3-2所示。
代码清单3-2 第2章
Java代码
package="ndroid" android:versionCode="1" android:versionName="1.0"> android:label="@string/app_name"> android:label="@string/app_name"> android:name="ER" />
代码清单3-2中,itent-filters描述了Activity启动的位置和时间。每当一个Activity(或者操作系统)要执行一个操作时,它将创建出一个Intent的对象,这个Intent对象能承载的信息可描述你想做什么,你想处理什么数据,数据的类型,以及一些其他信息。而Android则会和每个Application所暴露的intent-filter的数据进行比较,找到最合适Activity来处理调用者所指定的数据和操作。下面我们来仔细分析文件,如表3-1所示。
表3-1 分析
下面我们看看资源文件中一些常量的定义,如,如代码清单3-3所示。
代码清单3-3 第2章
Java代码
这个文件很简单,就定义了两个字符串资源,因此,我们可以在代码清单3-1中看到如下内容,即定义了“app_name”和“hello”两个常量,分别指向代码清单3-3中的两个字符串资源。
Java代码
public static final class string {
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
}
那么如何在程序中使用我们所定义的这些资源呢?首先,通过Context的getResources实例化一个Resources对象,然后通过Resources的getString方法取得指定索引的字符串,代码如下:
Java代码
Resources r = text().getResources();
String appname= ((String) ing(_name));
String hello= ((String) ing());
项目中所有使用的常量都可以通过这种XML文件的方式定义,比如,下面是我们通过XML文件定义的一些有关颜色的资源。
Java代码
现在来分析HelloAndroid项目的布局文件(layout),首先打开res->layout->文件,如代码清单3-4所示。
代码清单3-4 第2章
Java代码
xmlns:android="/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" />
代码清单3-4中,有以下几个布局和参数。
*
* 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字符串,即文件中的hello所代表的字符串资源。hello字符串的内容“Hello World, HelloAndroid!”就是我们在HelloAndroid项目运行时看到的字符串。
最后,我们来分析HelloAndroid项目的主程序文件,如代码清单3-5所示。
代码清单3-5 第2章
Java代码
...
public void onCreate(Bundle savedInstanceState)
{
te(savedInstanceState);
/* 设置Activity要显示的布局为() */
setContentView();
}
...
主程序HelloAndroid类继承自Activity类,重写了void onCreate(Bundle savedInstanceState)方法。在onCreate方法中通过setContentView()设置Activity要显示的布局文件()。
到这里,是不是明白了为什么我们在创建项目时没有进行编码就可以直接运行程序呢?当然,这也是Android开发的特点,这样可以很轻松地将代码和UI分开,在国际化和程序维护方面有着巨大的作用。如果你的Android程序需要适应国际化,比如说多国语言等问题,那么就可以定义不同语言的UI布局,在程序装载时调用不同的布局。而且,如果我们需要修改UI的一些问题,就不必查看代码了,直接更改这些布局文件即可,是不是很方便?当然,这需要开发者在开发时使用这种MVC框架,尽量减少使用“硬编码”。笔者个人建议使用这种框架。
3.1.2 Android应用解析
上面我们了解了Android应用程序的目录结构和其中每个文件的功能,要进行应用开发,还需要对Android应用构造进行深入分析。Android应用程序由4个模块构造而成:Activity,Intent,Content Provider,Service。
当然,也不是每个Android应用程序都必须由这4部分组成,可以根据开发者需求进行组合,比如上面建立的HelloAndroid项目就只使用了Activity这一个模块。但是,任何一个应用程序都必须在文件中声明使用到的这些模块。
1.Activity
Activity是最基本的模块,我们在HelloAndroid项目中已经使用过。我们称之为“活动”,在应用程序中,一个Activity通常就是一个单独的屏幕。每一个活动都被实现为一个独立的类,并且从活动基类中继承而来,活动类将会显示由视图控件组成的用户接口,并对事件作出响应。例如HelloAndroid项目中的即继承了Activity类。大多数的应用都是由多个Activity显示组成,例如,对一个文本信息应用而言,第一个屏幕用来显示发送消息的联系人列表,第二个屏幕用来写文本消息和选择收件人,第三个屏幕查看消息历史或者消息设置操作等。
这里的每一个屏幕就是一个活动,很容易实现从一个屏幕到一个新的屏幕,并且完成新的活动。当一个新
的屏幕打开后,前一个屏幕将会暂停,并保存在历史栈中。用户可以返回到历史栈中的前一个屏幕,当屏幕不再使用时,还可以从历史栈中删除。
简单理解,Activity代表一个用户所能看到的屏幕,主要用于处理应用程序的整体性工作,例如,监听系统事件(按键事件、触摸屏事件等),为用户显示指定的View,启动其他Activity等。所有应用的Activity都继承于ty类,该类是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之间的切换。运行效果:当应用程序启动时显示布局,如图3-2所示,当点击“切换”按钮时,屏幕显示布局,如图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_01srccomyarinandroidExamples_03_
Java代码
/* 监听button的事件信息 */
lickListener(new kListener() {
public void onClick(View v)
{
/* 新建一个Intent对象 */
Intent intent = new Intent();
/* 指定intent要启动的类 */
ss(, );
/* 启动一个新的Activity */
startActivity(intent);
/* 关闭当前的Activity */
();
}
});
然后,我们要从Activity02跳转到Activity01时,就只是需要在中使用同样的方法返回Activity01中。大家可以参考本书所附代码:第3章Examples_03_01srccomyarinandroid
Examples_03_01 。值得注意的是,该项目中我们使用了两个Activity,每一个Activity都需要在文件中进行声明,声明方法如代码清单3-7所示。
代码清单3-7 第3章Examples_03_
Java代码
android:label="@string/app_name">
如果希望Android应用能够对外部事件(如当电话呼入时,或者数据网络可用时,或者到了晚上时)做出响应,可以使用IntentReceiver。虽然IntentReceiver在感兴趣的事件发生时会使用NotificationManager通知用户,但它并不能生成UI。IntentReceiver在中注册,但也可以在代码中使用erReceiver()进行注册。当IntentReceiver被触发时,应用不必对请求调用IntentReceiver,系统会在需要时启动应用。各种应用还可以通过使用astIntent()将它们自己的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_02srccomyarinandroidExamples_03_
Java代码
public class Activity01 extends Activity
{
public void onCreate(Bundle savedInstanceState)
{
TextView tv = new TextView(this);
String string = "";
te(savedInstanceState);
//得到ContentResolver对象
ContentResolver cr = getContentResolver();
//取得电话本中开始一项的光标
Cursor cursor = (T_URI, null,
null,
null, null);
//向下移动光标
while(Next())
{
//取得联系人名字
int nameFieldColumnIndex = umnIndex(PhoneLookup.
DISPLAY_NAME);
String contact = ing(nameFieldColumnIndex);
//取得电话号码
int numberFieldColumnIndex = umnIndex(PhoneLookup.
NUMBER);
String number = ing(numberFieldColumnIndex);
string += (contact+":"+number+"n");
}
();
//设置TextView显示的内容
t(string);
//显示到屏幕
setContentView(tv);
}
}
前面强调过,要使用这些模块,需要在声明,本例中我们使用了读取联系人的API,因此,声明方式如下所示:
Java代码
android:name="_CONTACTS">
4.Service
Service即“服务”的意思,既然是服务,那么Service将是一个生命周期长且没有用户界面的程序。比如一个正在从播放列表中播放歌曲的媒体播放器,在这个媒体播放器应用中,应该会有多个Activity,让使用者可以选择歌曲并播放歌曲。然而,音乐重放这个功能并没有对应的Activity,因为使用者会认为在导航到其他屏幕时音乐应该还在播放。在这个例子中,媒体播放器这个Activity会使用ervice()来启动一个Service,从而可以在后台保持音乐的播放。同时,系统也将保持这个Service一直执行,直到这个Service运行结束。另外,还可以通过使用rvice()方法连接到一个Service上(如果这个Service当前还没有处于启动状态,则将启动它)。当连接到一个Service之后,还可用Service提供的接口与它进行通信。以媒体播放器为例,我们还可以执行暂停、重播等操作。
下面通过一个例子来学习Service的使用,该例子通过Service来播放一首MP3,如图3-6所示。当用户点击“开始”按钮,音乐开始播放;点击“停止”按钮,停止音乐播放。当然,这里需要在资源文件中添加一首MP3歌曲,如图3-7所示。
要实现音乐的播放,需要在界面中放置两个按钮,用来控制音乐的播放和停止。而我们的音乐播放是通过一个服务来实现的,所以我们可以通过startService和stopService方法来开启和停止这个播放音乐的服务,如代码清单3-9所示。
图3-6 使用Service播放音乐 图3-7 3
代码清单3-9 第3章Examples_03_03srccomyarinandroidExamples_03_03
Java代码
//开始按钮
private OnClickListener start = new OnClickListener()
{
public void onClick(View v)
{
//开启Service
startService(new Intent(""));
}
};
//停止按钮
private OnClickListener stop = new OnClickListener()
{
public void onClick(View v)
{
//停止Service
stopService(new Intent(""));
}
};
下面是该例子的核心内容。如何通过Service来播放音乐,其实也很简单,首先创建一个MusicService继承自Service,然后通过start和stop方法来控制音乐的播放,如代码清单3-10所示。具体实现请参见本书所附代码:第3章Examples_03_03。
代码清单3-10 第3章Examples_03_03srccomyarinandroidExamples_03_03
Java代码
public class MusicService extends Service
{
//MediaPlayer对象
private MediaPlayer player;
public IBinder onBind(Intent arg0)
{
return null;
}
public void onStart(Intent intent, int startId)
{
t(intent, startId);
//这里可以理解为装载音乐文件
player = (this, );
//开始播放
();
}
public void onDestroy()
{
roy();
//停止音乐—停止Service
();
}
}
我们使用Service时同样需要在中声明,声明方式如代码清单3-11所示。
代码清单3-11 第3章Examples_03_03
Java代码
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_04srccomyarinandroidExamples_03_04
Java代码
public class Activity01 extends Activity
{
private static final String TAG = "Activity01";
public void onCreate(Bundle savedInstanceState)
{
te(savedInstanceState);
setContentView();
Log.v(TAG, "onCreate");
Button button1 = (Button) findViewById(1);
/* 监听button的事件信息 */
lickListener(new kListener() {
public void onClick(View v)
{
/* 新建一个Intent对象 */
Intent intent = new Intent();
/* 指定intent要启动的类 */
ss(, );
/* 启动一个新的Activity */
startActivity(intent);
/* 关闭当前的Activity */
();
}
});
/******************************/
Button button3 = (Button) findViewById(3);
/* 监听button的事件信息 */
lickListener(new kListener() {
public void onClick(View v)
{
/* 关闭当前的Activity */
();
}
});
}
public void onStart()
{
t();
Log.v(TAG, "onStart");
}
public void onResume()
{
me();
Log.v(TAG, "onResume");
}
public void onPause()
{
e();
Log.v(TAG, "onPause");
}
public void onStop()
{
();
Log.v(TAG, "onStop");
}
public void onDestroy()
{
roy();
Log.v(TAG, "onDestroy");
}
public void onRestart()
{
art();
Log.v(TAG, "onReStart");
}
}
在第二个界面中,同第一个界面一样,加入一些可以区分的不同的Log信息。
同样需要在文件中声明所使用的两个Activity模块,如代码清单3-13所示。具体实现请参见本书所附代码:第3章Examples_03_04。
代码清单3-13 第3章Examples_03_
Java代码
android:label="@string/app_name">
当在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/”并右键单击,依次选择“Open With”→“Android layout Editor”菜单命令,或者直接双击文件,即可以切换到UI设计界面,如图3-15所示。
图3-15 Android Layout Editor命令
左边的Layouts标签的内容则是一些线性布局,可以使用它轻松地完成对布局的排版,比如横向或者纵向布局。Views标签则是一些UI控件,可以将这些控件直接拖动到右边的窗口进行编辑,如图3-16所示。
图3-16 Android Layout Editor
当然,还可以点击右下角的标签来切换到XML编辑器,对代码进行编排,如图3-17所示。将这些功能配合起来使用,基本可以满足开发者需求。
除了这个还不算太成熟的UI编辑器之外,笔者曾经使用过一个第三方的工具DroidDraw,DroidDraw是一个公开了源代码的UI设计器,可以根据自己的开发需要进行修改。www. 提供了在线使用DroidDraw的功能,当然也可以下载到本地来进行编辑,下载地址:/p/droiddraw/。
2023年12月21日发(作者:谷梁灵凡)
第一部分 准备篇
第1章 Android开发简介
在Google及其开放手机联盟推出基于Linux平台的开源手机操作系统Android之后,Google又不惜重金举办了Android开发者大赛,吸引了众多开发者的目光。Android不仅功能强大,而且具有开放和免费等先天优势,全球范围内的电信行业、手机制造商因此毫不犹豫地加入到Android开放手机联盟中来。2008年9月22日,美国运营商T-Mobile USA在纽约正式发布了第一款基于Android的手机--T-Mobile G1。这让更多的移动设备厂商看到了Android的光明前景,并纷纷加入其中,Android甚至已经涉足上网本市场。中国移动也在Android的基础之上推出了自己的操作系统OMS,而基于OMS操作系统的联想O1手机也即将上市,2009年年底将会有更多的Android手机出现。
随着Android手机的普及,Android应用的需求势必会越来越大,这将是一个潜力巨大的市场,会吸引无数软件开发厂商和开发者投身其中。作为程序员的我们,当然也不应该落后于人,赶快加入到Android应用的开发阵营中来吧!
1.1 Android基本概念
Android一词本意是指“机器人”,当然现在大家都知道它是Google推出的开源手机操作系统。Android基于Linux平台,由操作系统、中间件、用户界面和应用软件组成,号称是首个为移动终端打造的真正开放和完整的移动软件。它是由一个由 30 多家科技公司和手机公司组成的“开放手机联盟”共同研发的,这将大大降低新型手机设备的研发成本。完全整合的全移动功能性产品成为“开放手机联盟”的最终目标。
1.1.1 Android简介
Android 作为Google移动互联网战略的重要组成部分,将进一步推进“随时随地为每个人提供信息”这一企业目标的实现。Google的目标是让移动通信不依赖于设备,甚至是平台。出于这个目的,Android 将完善而不是替代Google长期以来推行的移动发展战略:通过与全球各地的手机制造商和移动运营商成为合作伙伴,开发既实用又有吸引力的移动服务,并推广这些产品。
Android平台的研发队伍阵容强大,包括Google、HTC(宏达电)、T-Mobile、高通、摩托罗拉、三星、LG以及中国移动在内的30多家企业都将基于该平台开发手机的新型业务,应用之间的通用性和互联性将在最大程度上得到保持。“开放手机联盟”表示,Android平台可以促使移动设备的创新,让用户体验到最优质的移动服务。同时,开发商也将得到一个新的开放级别,更方便地进行协同合作,从而保障新型移动设备的研发速度。因此Android 是第一个完整、开放、免费的手机平台。下面我们来欣赏一下第一款基于Android操作系统的手机G1,外观相当漂亮,如图1-1所示。
Android系统具有如下5个特点:
* 开放性。Google与开放手机联盟合作开发了 Android,Google通过与运营商、设备制造商、开发商和其他有关各方结成深层次的合作伙伴关系,希望通过建立标准化、开放式的移动电话软件平台,在移动产业内形成一个开放式的生态系统。
* 应用程序无界限。Android 上的应用程序可以通过标准 API 访问核心移动设备功能。通过互联网,应用程序可以声明它们的功能可供其他应用程序使用。
* 应用程序是在平等的条件下创建的。移动设备上的应用程序可以被替换或扩展,即使是拨号程序或主屏幕这样的核心组件。
* 应用程序可以轻松地嵌入网络。应用程序可以轻松地嵌入HTML、JavaScript 和样式表,还可以通过
WebView 显示网络内容。
* 应用程序可以并行运行。Android 是一种完整的多任务环境,应用程序可以在其中并行运行。在后台运行时,应用程序可以生成通知以引起注意。
为什么Android手机如此受用户青睐,下面我们来看看Android究竟有些什么功能在吸引着我们。
(1)智能虚拟键盘。虚拟键盘的出现意味着基于Android 1.5或以上版本(Android 2.0)的移动设备可以同时支持物理键盘和虚拟键盘。不同的输入方式可满足用户在特定场景的需求。Android虚拟键盘可以在任何应用中提供,包括Gmail、浏览器、SMS,当然也包括大量的第三方应用,如自动校正、推荐、用户词典等。不同于其他手机平台,Android 1.5及其以上的版本还支持第三方虚拟键盘应用的安装,如图1-2所示。
(2)使用Widget实现桌面个性化。可以用Widget“武装”自己的桌面。大多数小的Web应用都是从网络上获得实时数据并展示给用户的。Android预装了5个桌面Widget,包括数字时钟、日历、音乐播放器、相框和搜索。不同于iPhone,Android通过内置的应用程序库安装第三方Widget,如图1-3所示。
图1-1 Android G1 图1-2
虚拟键盘
图1-3 用Widget实现个性化桌面
(3)用在线文件夹快速浏览在线数据。类似于OS X Leopard的QuickLook特征,Android的在线文件夹可显示常见的数据条目,比如联系人、喜欢的应用、E-mail信息、播放列表、书签、RSS源等,并不需要运行系统程序处理特定的数据条目。在线文件夹数据实时更新,就像通过云或是本地创建新的数据。什么是最好的,开发者可以拓展通用数据条目和注册新数据类型的内置支持。例如,Twitter客户端程序可以注册tweet作为新数据类型,因此可以让你从你的朋友那里创建tweet的在线文件。Android可以为我们的个人桌面提供一组在线文件夹,从而帮助我们快速、方便地浏览联系人、股市、书签等信息。
(4)视频录制和分享。Android还有录制和分享视频的功能,对回放和MPEG-4、3GP等视频格式也有了更好的支持。可以通过E-mail、MMS或直接上传到YouTube等方式来分享视频,使用隐私控制来决定是分享给朋友还是每个人。上传视频的同时,可以继续使用手机,甚至可以继续录制和上传新的视频。如图1-4所示,通过YouTube分享录制的视频。
(5)图片上传。在线分享图片需要的点击更少。完成照相后,当浏览图片或选择Google在线图片服务Picasa时,只需轻点“分享”就会拥有1GB的免费图片存储空间。
(6)更快、更兼容的浏览器。Android的基于Webkit内核的浏览器带来了重要的调速装置(SpeedPumb),这得益于新的Webkit渲染引擎和优化的Java脚本编译器(SquireIFish)。当使用包含大量Java脚本的复杂Web应用时,可以体验到更佳的性能。除提高速度外,Android的浏览器还支持Web页面内的复制和粘贴操作,用户可以选中文本并复制,然后粘贴到搜索框中进行搜索。
(7)Voice Search语音搜索。带有语音识别技术的Google手机已于2008年11月面世,它支持语音搜索功能。该功能增强了默认的搜索能力,已超过纯文本搜索。当你大声说出要搜索的内容后,Android将上传数字信号并记录到Google服务器中。在服务器中,语音识别技术能将语音转化为特定的文本搜索,使之通过Google搜索引擎,通过地理位置的筛选,将结果反馈到手机设备。图1-5显示了Google文本和语音搜索桌面。
(8)立体声蓝牙和免提电话。除了增强的免提电话体验,Android还支持立体声蓝牙(A2DP和AVCRP),并有自动配对功能。
(9)强大的GPS技术。Android内部提供了大量GPS组件,我们可以很轻松地获得设备当前的位置等信息,让导航等功能更加完美。
(10)Android系统硬件检测。Android可自动检测和修复SD卡的文件系统,允许第三方应用显示Android系统的硬件特征。为了让用户下载到与自己的设备更匹配的应用,我们可以检测用户设备的硬件信息,让满足应用要求的设备安装该程序,当更多的Android设备建立在不同的硬件上时,这个功能会显得很实用。
图1-4 通过YouTube分享录制的视频 图1-5 Google文本和语音搜索桌面
1.1.2 Android的系统构架
通过上一节的介绍,我们对Android的特点以及它为什么会如此受欢迎有了初步的了解。下面将讨论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这个框架都有些什么功能可供我们使用。
* :提供高层的程序模型和基本的运行环境。
* t:包含对各种设备上的数据进行访问和发布。
* se:通过内容提供者浏览和操作数据库。
* cs:底层的图形库,包含画布、颜色过滤、点、矩形,可以将它们直接绘制到屏幕上。
* on :定位和相关服务的类。
* :提供一些类管理多种音频、视频的媒体接口。
* :提供帮助网络访问的类,超过通常的.* 接口。
* :提供了系统服务、消息传输和IPC机制。
* :提供OpenGL的工具。
* er:提供访问Android内容提供者的类。
* ony:提供与拨打电话相关的API交互。
* :提供基础的用户界面接口框架。
* :涉及工具性的方法,例如时间日期的操作。
* :默认浏览器操作接口。
* :包含各种UI元素(大部分是可见的)在应用程序的布局中使用。
1.2 OMS介绍
OMS是Open Mobile System的简称,即面向移动互联网的开放型移动智能终端软件平台,它包括基于Linux 2.6内核的移动终端下层操作系统、上层应用软件、中间件、Java虚拟机、硬件参考设计以及基于WebKit的各类应用。它具有强大的兼容性、扩展性和安全性,以及简单易用、友好的人机界面等,而且具有完全自主的知识产权。在此之上,OMS拥有开放统一的API开发接口、完备的集成开发环境和活跃的在线生态环境,极大地方便了移动应用的开发。
OMS的可移植性将使该软件平台在其他领域具有广泛的应用,如航空航天、军事、制造业等。
1.2.1 OPhone介绍
OPhone是基于Linux的面向移动互联网的终端基础软件及系统解决方案。由于OPhone与Android兼容,都是基于Java开发的,因此可以同时用OMS API和Android API来开发OMS应用。任何用Android API开发的应用都可以在OMS终端上正确地运行。然而,不能在Android终端上运行由扩展的OMS API开发的程序,因为这些OMS API是OMS平台独有的,而且在运行时是必需的。
OPhone是指采用了OMS智能操作系统的手机。为了突破TD终端瓶颈,以及促进手机终端与中国移动的网络和应用服务进行无缝对接,中国移动在Android操作系统基础上自主开发了OMS系统,该系统直接内置了中国移动的服务菜单、音乐随身听、手机导航、号簿管家、139邮箱、飞信、快讯和移动梦网等特色业务,如图1-7所示。
1.2.2 Widget介绍
OMS除了支持基于Java的应用,还支持Widget应用开发。Widget应用是OMS的精华,而Android从1.5版本开始同样支持Widget应用开发,但是所采用的标准则和OMS不同,我们会在后面的章节详细讲解。
Widget应用采用了 JIL (Joint Innovation Lab)Widget标准。JIL Widget是一个采用HTML、JavaScript和CSS等网络技术的应用程序。Widget应用是在Widget引擎上运行的独立的应用程序。Widget已经成为手机上非常流行的技术,可以为用户带来良好的移动互联网体验,随时随地获取有用的资讯,如天气预报、股票信息、头条新闻等。从用户的角度来看,Widget应用和OPhone应用没有什么区别。实际上,Widget应用不同于OPhone应用。OPhone应用是采用Java技术的应用程序,而Widget应用则是采用HTML、JavaScript和CSS等网络技术的应用程序。相比较而言,Widget应用的开发更加方便快捷。此外,JIL Widget还提供了许多JavaScript API来扩展Widget应用的能力,如访问手机电话本、手机文件系统等。Widget应用运行效果如图1-8所示。
图1-7 OPhone系统界面
图1-8 HelloWidget效果预览
1.3 小结
本章主要介绍了与Android相关的一些基本概念,同时分析了Android系统的特点及其功能,简单介绍了目前主流的7个Linux平台手机以及中国移动的OMS操作系统。在介绍Android基本概念时重点介绍了Android系统架构和应用框架,其中应用层即是我们使用Java语言编写的一些运行在虚拟机上的程序,应用框架层是我们开发应用时接触最为紧密的一层,在开发应用程序时必须遵守其规则,才能保证所开发的应用程序能在Android上安全地运行。大家应着重理解这两层,这样才能开发出效率更高的应用程序。
据称今年底将会有18款之多的以Android平台为系统的Linux手机上市, Android将在移动开发中具有更广阔的前景。要想成为一个优秀的Android手机开发者,还需要从基础做起,希望大家好好掌握本章的内容。
第2章 Android开发环境搭建
本章讲解如何配置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)登录,下载最新版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”项。接着点击“”按钮,在“Location”文本框中输入Babel更新地址:/
technology/babel/update-site/ganymede,然后点击 OK 按钮,如图2-4所示。
图2-4 添加语言包更新地址
(2)“Avaliable Software”表中会多出一项/technology/babel/update-site/ganymede/,点击该项左边的箭头,就会出现网络更新软件列表,如图2-5所示。
图2-5 Avaliable Software选择框
(3)选择“Simplified Chinese”语言包后,点击 “ ”按钮,等待Eclipse处理。
处理完成后会出现“Install”对话框,这时会提示你选择要安装的语言包。根据提示,很容易完成后面的操作,这里就不再赘述了。
安装完毕后,重新启动Eclipse即可完成全部汉化过程。
如果重启Eclipse后不显示中文,请用命令行 “ -nl zh_CN”重新启动Eclipse。
2.2.3 SDK和ADT的安装和配置
安装了JDK和Eclipse后,现在就要安装Android SDK和ADT插件了。
1.Android SDK安装
(1)解压缩下载好的SDK安装包到要安装SDK的路径,然后运行“SDK ”。
(2)如果遇到了消息为“Failed to fetch URL…”的错误提示,那么需要将HTTPS方式改为HTTP方式,在“Android SDK and AVD Manager”窗口的左侧选择 “Settings”,选中 “Force …”选项(如图2-6
所示),点击“Save & Apply”并重新运行 SDK 。
图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”菜单,依次选择“”项和“Avaiable Software”选项卡,点击“”按钮,输入地址/android/eclipse/,结果如图2-10所示。
(2)点击“OK”,这时可能会出现如图2-11所示的错误。
图2-10 添加ADT的更新地址 图2-11 更新地址错误
解决这个问题的方法是:将“/android/eclipse/”中的“https”更改为“http”,在接下来的对话框中选中“Name”下的所有选项,根据提示即可完成后续的安装过程。
(3)打开菜单“Windows”,依次选择“Preferences”→“Android”,点击“”按钮,选择 Android SDK的安装路径,如图2-12所示。
图2-12 Eclipse首选项
(4)点击“OK”按钮 ,开打菜单“File”,依次选择“NEW” → “”菜单命令,出现如图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文本框中输入应用程序包的名字(ndroid),在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”选项,再点击右边的“”按钮,新建一个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项目,同样可以点击右边的“”按钮来选择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:
”(c: 是要安装的文件的路径),这样便可以将apk文件安装到模拟器上,如图2-24所示。
图2-24 安装apk文件到模拟器
* 卸载模拟器中的apk文件。操作步骤为:首先启动Android模拟器,进入命令行模式。在命令行模式下进入Android SDK安装目录下面的tools文件夹,然后在命令行处依次输入“adb shell”、“cd data”、“cd
app”、“ls”(主要是针对不知道包下面的文件的情况,可以用ls命令列表显示出来)、“rm
”命令(“”是你要卸载的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”按钮,选择“”命令按钮,打开“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”来重新启动“”进程。下面我们介绍如何使用DDMS的“Logcat”来调试Android程序,步骤如下:
(1)“Logcat”通过“”类的静态方法来查找错误和打印系统日志消息。它是一个进行日志输出的API,我们在Android 程序中可以随时为某一个对象插入一个Log,然后在DDMS中观察Logcat的输出是否正常。常用的方法有以下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”程序进行调试,首先修改“”如代码清代2-1所示。我们在代码中加入了需要输出的日志信息。
代码清单2-1 第2章
Java代码
/* 定义TAG标签,这样可以很好地区分打印出来的Log */
private static final String TAG = "HelloAndroid";
public void onCreate(Bundle savedInstanceState)
{
te(savedInstanceState);
/* 打印出不同的Log信息 */
Log.v(TAG,"VERBOSE");
Log.d(TAG,"DEBUG");
Log.i(TAG,"INFO");
Log.w(TAG,"WARN");
Log.e(TAG,"ERROR");
setContentView();
}
(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或者CC++时,
第一个程序总是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文件夹中的文件和每个Android项目都必须有的文件。
* 是在建立项目时自动生成的,这个文件是只读模式,不能更改,文件是定义该项目所有资源的索引文件。先来看看HelloAndroid项目的文件,如代码清单3-1所示。
代码清单3-1 第2章
Java代码
package ndroid;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
}
}
可以看到这里定义了很多常量,这些常量的名字都与res文件夹中的文件名相同,这再次证明了文件中所存储的是该项目所有资源的索引。有了这个文件,可以很快地找到要使用的资源,由于这个文件不能手动编辑,所以当在项目中加入了新的资源时,只需要刷新一下该项目,文件便自动生成了所有资源的索引。
* 文件则包含了该项目中所使用的Activity、Service、Receiver,我们先来打开HelloAndroid项目中的文件,如代码清单3-2所示。
代码清单3-2 第2章
Java代码
package="ndroid" android:versionCode="1" android:versionName="1.0"> android:label="@string/app_name"> android:label="@string/app_name"> android:name="ER" />
代码清单3-2中,itent-filters描述了Activity启动的位置和时间。每当一个Activity(或者操作系统)要执行一个操作时,它将创建出一个Intent的对象,这个Intent对象能承载的信息可描述你想做什么,你想处理什么数据,数据的类型,以及一些其他信息。而Android则会和每个Application所暴露的intent-filter的数据进行比较,找到最合适Activity来处理调用者所指定的数据和操作。下面我们来仔细分析文件,如表3-1所示。
表3-1 分析
下面我们看看资源文件中一些常量的定义,如,如代码清单3-3所示。
代码清单3-3 第2章
Java代码
这个文件很简单,就定义了两个字符串资源,因此,我们可以在代码清单3-1中看到如下内容,即定义了“app_name”和“hello”两个常量,分别指向代码清单3-3中的两个字符串资源。
Java代码
public static final class string {
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
}
那么如何在程序中使用我们所定义的这些资源呢?首先,通过Context的getResources实例化一个Resources对象,然后通过Resources的getString方法取得指定索引的字符串,代码如下:
Java代码
Resources r = text().getResources();
String appname= ((String) ing(_name));
String hello= ((String) ing());
项目中所有使用的常量都可以通过这种XML文件的方式定义,比如,下面是我们通过XML文件定义的一些有关颜色的资源。
Java代码
现在来分析HelloAndroid项目的布局文件(layout),首先打开res->layout->文件,如代码清单3-4所示。
代码清单3-4 第2章
Java代码
xmlns:android="/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" />
代码清单3-4中,有以下几个布局和参数。
*
* 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字符串,即文件中的hello所代表的字符串资源。hello字符串的内容“Hello World, HelloAndroid!”就是我们在HelloAndroid项目运行时看到的字符串。
最后,我们来分析HelloAndroid项目的主程序文件,如代码清单3-5所示。
代码清单3-5 第2章
Java代码
...
public void onCreate(Bundle savedInstanceState)
{
te(savedInstanceState);
/* 设置Activity要显示的布局为() */
setContentView();
}
...
主程序HelloAndroid类继承自Activity类,重写了void onCreate(Bundle savedInstanceState)方法。在onCreate方法中通过setContentView()设置Activity要显示的布局文件()。
到这里,是不是明白了为什么我们在创建项目时没有进行编码就可以直接运行程序呢?当然,这也是Android开发的特点,这样可以很轻松地将代码和UI分开,在国际化和程序维护方面有着巨大的作用。如果你的Android程序需要适应国际化,比如说多国语言等问题,那么就可以定义不同语言的UI布局,在程序装载时调用不同的布局。而且,如果我们需要修改UI的一些问题,就不必查看代码了,直接更改这些布局文件即可,是不是很方便?当然,这需要开发者在开发时使用这种MVC框架,尽量减少使用“硬编码”。笔者个人建议使用这种框架。
3.1.2 Android应用解析
上面我们了解了Android应用程序的目录结构和其中每个文件的功能,要进行应用开发,还需要对Android应用构造进行深入分析。Android应用程序由4个模块构造而成:Activity,Intent,Content Provider,Service。
当然,也不是每个Android应用程序都必须由这4部分组成,可以根据开发者需求进行组合,比如上面建立的HelloAndroid项目就只使用了Activity这一个模块。但是,任何一个应用程序都必须在文件中声明使用到的这些模块。
1.Activity
Activity是最基本的模块,我们在HelloAndroid项目中已经使用过。我们称之为“活动”,在应用程序中,一个Activity通常就是一个单独的屏幕。每一个活动都被实现为一个独立的类,并且从活动基类中继承而来,活动类将会显示由视图控件组成的用户接口,并对事件作出响应。例如HelloAndroid项目中的即继承了Activity类。大多数的应用都是由多个Activity显示组成,例如,对一个文本信息应用而言,第一个屏幕用来显示发送消息的联系人列表,第二个屏幕用来写文本消息和选择收件人,第三个屏幕查看消息历史或者消息设置操作等。
这里的每一个屏幕就是一个活动,很容易实现从一个屏幕到一个新的屏幕,并且完成新的活动。当一个新
的屏幕打开后,前一个屏幕将会暂停,并保存在历史栈中。用户可以返回到历史栈中的前一个屏幕,当屏幕不再使用时,还可以从历史栈中删除。
简单理解,Activity代表一个用户所能看到的屏幕,主要用于处理应用程序的整体性工作,例如,监听系统事件(按键事件、触摸屏事件等),为用户显示指定的View,启动其他Activity等。所有应用的Activity都继承于ty类,该类是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之间的切换。运行效果:当应用程序启动时显示布局,如图3-2所示,当点击“切换”按钮时,屏幕显示布局,如图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_01srccomyarinandroidExamples_03_
Java代码
/* 监听button的事件信息 */
lickListener(new kListener() {
public void onClick(View v)
{
/* 新建一个Intent对象 */
Intent intent = new Intent();
/* 指定intent要启动的类 */
ss(, );
/* 启动一个新的Activity */
startActivity(intent);
/* 关闭当前的Activity */
();
}
});
然后,我们要从Activity02跳转到Activity01时,就只是需要在中使用同样的方法返回Activity01中。大家可以参考本书所附代码:第3章Examples_03_01srccomyarinandroid
Examples_03_01 。值得注意的是,该项目中我们使用了两个Activity,每一个Activity都需要在文件中进行声明,声明方法如代码清单3-7所示。
代码清单3-7 第3章Examples_03_
Java代码
android:label="@string/app_name">
如果希望Android应用能够对外部事件(如当电话呼入时,或者数据网络可用时,或者到了晚上时)做出响应,可以使用IntentReceiver。虽然IntentReceiver在感兴趣的事件发生时会使用NotificationManager通知用户,但它并不能生成UI。IntentReceiver在中注册,但也可以在代码中使用erReceiver()进行注册。当IntentReceiver被触发时,应用不必对请求调用IntentReceiver,系统会在需要时启动应用。各种应用还可以通过使用astIntent()将它们自己的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_02srccomyarinandroidExamples_03_
Java代码
public class Activity01 extends Activity
{
public void onCreate(Bundle savedInstanceState)
{
TextView tv = new TextView(this);
String string = "";
te(savedInstanceState);
//得到ContentResolver对象
ContentResolver cr = getContentResolver();
//取得电话本中开始一项的光标
Cursor cursor = (T_URI, null,
null,
null, null);
//向下移动光标
while(Next())
{
//取得联系人名字
int nameFieldColumnIndex = umnIndex(PhoneLookup.
DISPLAY_NAME);
String contact = ing(nameFieldColumnIndex);
//取得电话号码
int numberFieldColumnIndex = umnIndex(PhoneLookup.
NUMBER);
String number = ing(numberFieldColumnIndex);
string += (contact+":"+number+"n");
}
();
//设置TextView显示的内容
t(string);
//显示到屏幕
setContentView(tv);
}
}
前面强调过,要使用这些模块,需要在声明,本例中我们使用了读取联系人的API,因此,声明方式如下所示:
Java代码
android:name="_CONTACTS">
4.Service
Service即“服务”的意思,既然是服务,那么Service将是一个生命周期长且没有用户界面的程序。比如一个正在从播放列表中播放歌曲的媒体播放器,在这个媒体播放器应用中,应该会有多个Activity,让使用者可以选择歌曲并播放歌曲。然而,音乐重放这个功能并没有对应的Activity,因为使用者会认为在导航到其他屏幕时音乐应该还在播放。在这个例子中,媒体播放器这个Activity会使用ervice()来启动一个Service,从而可以在后台保持音乐的播放。同时,系统也将保持这个Service一直执行,直到这个Service运行结束。另外,还可以通过使用rvice()方法连接到一个Service上(如果这个Service当前还没有处于启动状态,则将启动它)。当连接到一个Service之后,还可用Service提供的接口与它进行通信。以媒体播放器为例,我们还可以执行暂停、重播等操作。
下面通过一个例子来学习Service的使用,该例子通过Service来播放一首MP3,如图3-6所示。当用户点击“开始”按钮,音乐开始播放;点击“停止”按钮,停止音乐播放。当然,这里需要在资源文件中添加一首MP3歌曲,如图3-7所示。
要实现音乐的播放,需要在界面中放置两个按钮,用来控制音乐的播放和停止。而我们的音乐播放是通过一个服务来实现的,所以我们可以通过startService和stopService方法来开启和停止这个播放音乐的服务,如代码清单3-9所示。
图3-6 使用Service播放音乐 图3-7 3
代码清单3-9 第3章Examples_03_03srccomyarinandroidExamples_03_03
Java代码
//开始按钮
private OnClickListener start = new OnClickListener()
{
public void onClick(View v)
{
//开启Service
startService(new Intent(""));
}
};
//停止按钮
private OnClickListener stop = new OnClickListener()
{
public void onClick(View v)
{
//停止Service
stopService(new Intent(""));
}
};
下面是该例子的核心内容。如何通过Service来播放音乐,其实也很简单,首先创建一个MusicService继承自Service,然后通过start和stop方法来控制音乐的播放,如代码清单3-10所示。具体实现请参见本书所附代码:第3章Examples_03_03。
代码清单3-10 第3章Examples_03_03srccomyarinandroidExamples_03_03
Java代码
public class MusicService extends Service
{
//MediaPlayer对象
private MediaPlayer player;
public IBinder onBind(Intent arg0)
{
return null;
}
public void onStart(Intent intent, int startId)
{
t(intent, startId);
//这里可以理解为装载音乐文件
player = (this, );
//开始播放
();
}
public void onDestroy()
{
roy();
//停止音乐—停止Service
();
}
}
我们使用Service时同样需要在中声明,声明方式如代码清单3-11所示。
代码清单3-11 第3章Examples_03_03
Java代码
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_04srccomyarinandroidExamples_03_04
Java代码
public class Activity01 extends Activity
{
private static final String TAG = "Activity01";
public void onCreate(Bundle savedInstanceState)
{
te(savedInstanceState);
setContentView();
Log.v(TAG, "onCreate");
Button button1 = (Button) findViewById(1);
/* 监听button的事件信息 */
lickListener(new kListener() {
public void onClick(View v)
{
/* 新建一个Intent对象 */
Intent intent = new Intent();
/* 指定intent要启动的类 */
ss(, );
/* 启动一个新的Activity */
startActivity(intent);
/* 关闭当前的Activity */
();
}
});
/******************************/
Button button3 = (Button) findViewById(3);
/* 监听button的事件信息 */
lickListener(new kListener() {
public void onClick(View v)
{
/* 关闭当前的Activity */
();
}
});
}
public void onStart()
{
t();
Log.v(TAG, "onStart");
}
public void onResume()
{
me();
Log.v(TAG, "onResume");
}
public void onPause()
{
e();
Log.v(TAG, "onPause");
}
public void onStop()
{
();
Log.v(TAG, "onStop");
}
public void onDestroy()
{
roy();
Log.v(TAG, "onDestroy");
}
public void onRestart()
{
art();
Log.v(TAG, "onReStart");
}
}
在第二个界面中,同第一个界面一样,加入一些可以区分的不同的Log信息。
同样需要在文件中声明所使用的两个Activity模块,如代码清单3-13所示。具体实现请参见本书所附代码:第3章Examples_03_04。
代码清单3-13 第3章Examples_03_
Java代码
android:label="@string/app_name">
当在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/”并右键单击,依次选择“Open With”→“Android layout Editor”菜单命令,或者直接双击文件,即可以切换到UI设计界面,如图3-15所示。
图3-15 Android Layout Editor命令
左边的Layouts标签的内容则是一些线性布局,可以使用它轻松地完成对布局的排版,比如横向或者纵向布局。Views标签则是一些UI控件,可以将这些控件直接拖动到右边的窗口进行编辑,如图3-16所示。
图3-16 Android Layout Editor
当然,还可以点击右下角的标签来切换到XML编辑器,对代码进行编排,如图3-17所示。将这些功能配合起来使用,基本可以满足开发者需求。
除了这个还不算太成熟的UI编辑器之外,笔者曾经使用过一个第三方的工具DroidDraw,DroidDraw是一个公开了源代码的UI设计器,可以根据自己的开发需要进行修改。www. 提供了在线使用DroidDraw的功能,当然也可以下载到本地来进行编辑,下载地址:/p/droiddraw/。