安卓应用与 Eclipse 教程(一)
零、简介
Android 是手机市场的主要参与者之一,其市场份额正在不断增长。Android 是第一个完整、开放和免费的移动平台,它为移动应用开发者提供了无尽的机会。与所有其他平台一样,拥有一个健壮灵活的开发环境是该平台成功的关键。
Eclipse 是 Java 程序员最常用的集成开发环境(IDE)。现在 Eclipse 是 Android 应用开发人员的首选 IDE。
Android Apps with Eclipse 提供了 Eclipse 的详细概述,包括帮助 Android 开发人员快速掌握 Eclipse 并简化其日常软件开发的步骤和插图。
这本书是给谁的
这本书是为那些希望使用 Eclipse IDE 快速掌握 Android 开发速度的初学者和中级开发人员编写的。
你将学到什么
本书涵盖以下主题:
- Android 平台如何工作以及 Android 应用开发的基础知识
- 如何使用最流行的 Java IDE Eclipse 开发 Android 应用
- 如何为 Android 开发安装和配置 Eclipse
- 如何利用 Eclipse 和 Android 本地开发工具包(NDK)来满足 C/C++ 的需求
- 如何使用 Android 的脚本层(SL4A)利用 Eclipse 编写脚本
- 如何使用 Eclipse 调试和排除 Android 应用的故障
下载代码
这本书的源代码从[www.apress.com](http://www.apress.com)开始对读者开放。
联系作者
读者可以通过作者在[www.zdo.com/android-app…](http://www.zdo.com/android-apps-with-eclipse)的 Android 应用与 Eclipse 网站联系他。
一、安卓优先
在这一章中,我们将从多个角度简要介绍 Android 平台。我们将从 Android 的历史开始,以更好地理解其形成背后的动机。然后,我们将探索 Android 平台架构的各种技术的完美结合,这些技术使平台能够提供卓越的移动体验。我们将强调多层 Android 安全框架,该框架使用软件和硬件来保证平台的安全。我们将简要回顾通过 Android 框架为用户级应用提供的服务应用编程接口(API ),以便与平台进行交互。最后,我们将讨论 Android 应用的部署和分发。
安卓历史
Android Inc .于 2003 年 10 月在加州硅谷成立,其理念是提供一个更加了解用户位置和偏好的移动平台。
Google 于 2005 年 8 月收购了 Android Inc .作为 Google Inc .的全资子公司。Google 的主要意图是为用户和应用开发人员提供一个由 Google technologies 支持的完全开放的平台。
2007 年 11 月,开放手机联盟作为一个为移动设备开发开放标准的联盟成立。开放手机联盟通过宣布 Android 平台开始了它的旅程。不到一年,新成员开始加入这个财团。
在开放手机联盟的保护下,Android 成为由谷歌领导的开源项目。Android 开源项目的目标是提供一个开放平台来改善用户的移动体验。
Android 是第一个完整、开放、免费的移动平台。
- *完成:*Android 平台是一个健壮、安全、易于升级的移动平台,具有全面的框架和定义良好的接口。它允许应用开发人员开发应用,并将其完全融入平台。它还提供兼容性和认证计划,因此设备制造商可以设计高度兼容的设备。
- *开放:*整个 Android 平台都是根据开源 Apache 许可条款开发和提供的。Android 不区分预装应用和第三方应用。开发人员在开发应用时可以完全访问设备功能和服务。
- *免费:*Android 平台在平台上开发应用不收取任何许可费、版税、会员费或认证费。Android 平台源代码和软件开发工具包免费提供给应用开发者。软件开发平台在许多桌面操作系统上广泛可用,允许应用开发人员使用他们选择的操作系统开发应用。
今天,Android 是手机市场的主要参与者之一。根据最近的市场分析,平均每天有 70 万台 Android 设备被激活,已经有超过 2 亿台设备被激活。Android 目前拥有 48%的手机市场份额,并且还在快速增长。
安卓版本
Android 平台的第一个测试版于 2007 年 11 月 5 日发布。从那以后,它经历了一系列的更新和漏洞修复。尽管从应用开发人员的角度来看,错误修复通常是透明的,但更新通常意味着对框架 API 的更改和添加。出于这个原因,除了 Android 平台版本号之外,第二个版本号,称为 API 级别,用于标识所支持的框架 API。
自 2009 年 4 月以来,每个 Android 版本都以基于甜点的代号发布,如克莱尔、弗罗育和姜饼。这为 Android 平台引入了第三种版本控制方案,对于首次使用 Android 应用的开发者来说,这变得更加神秘。说到 Android 应用开发,你会经常听到有人说“我的应用需要 clair 及以上”、“这个方法至少需要 API 级”、“我的手机得到了 Android 2.1 更新”之类的话。了解他们所指的是哪个版本和哪个 API 级别,以及哪些新 API 是哪个 Android 平台版本的一部分,很容易成为一项繁琐的记忆练习。您可以使用表 1-1 作为参考,在这三个版本方案之间进行映射。
**注:**由于 Android 平台仍在继续发展,表 1-1 可能没有涵盖最新的平台版本。有关最新列表,请参考 Android 开发者页面的 API 级别部分,位于[developer.android.com/guide/appendix/api-levels.html](http://developer.android.com/guide/appendix/api-levels.html)。
如表 1-1 所示,在开发应用时,您应该考虑 15 个 API 级别。API 级别也决定了用户的规模,所以在开发新的 Android 应用时,明智地选择这个数字是非常重要的。
安卓手机市场高度分散。仅仅看发布日期,你可能会认为大多数 Android 用户群至少运行 Android 3.0,因为它已经存在一年了;然而,事实并非如此。由于碎片化,发布日期远远不能给出正在使用的 Android 版本的清晰视图。图 1-1 是来自 Android 平台版本仪表盘([developer.android.com/resources/dashboard/platform-versions.html](http://developer.android.com/resources/dashboard/platform-versions.html))的最新版本分布图。
图 1-1。 基于市场数据的安卓版本分布
在图 1-1 中可以看到,目前大部分 Android 用户群都在运行 Android 2.3.3,姜饼。这意味着您的应用至少需要支持 API level 10,以便面向大多数 Android 用户。这也意味着你不能在你的应用中使用新版本 Android 平台中引入的最新 API 特性。在本书中,我们将使用 Android 2.3.3 开发我们的示例。
版本繁多是安卓开发者的通病。大多数应用开发人员为不同的 API 级别开发包。这解决了问题,但是这意味着需要维护不同的代码分支。
2011 年 3 月,谷歌推出了支持包作为版本问题的解决方案。支持包是一组静态库,允许应用开发人员开发支持多个 Android 平台版本的 Android 应用。支持包的主要目标是简化从单一代码库支持多个 Android 版本的过程。您可以在[developer.android.com/sdk/compatibility-library.html](http://developer.android.com/sdk/compatibility-library.html)找到有关支持包的更多信息。
Android 平台架构
Android 更像是一个完整的移动设备软件栈,而不是一个操作系统。它是针对移动需求精心优化的工具和技术的组合。
Android 依赖于久经考验的 Linux 内核来提供其操作系统功能。对于用户空间应用,Android 通过使用 Dalvik 虚拟机依赖于 Java 虚拟机技术。Android Zygote 应用进程通过服务预加载和资源共享,加快了应用的启动时间,并允许高效利用移动平台上稀缺的内存资源。所有这些成功的技术都对 Android 平台的成功起到了重要作用,如图图 1-2 所示。除了这些工具和技术之外,Android 运行时还提供了一个独特的计算环境,该环境专为向最终用户提供流畅的移动体验而定制,同时简化了开发人员的移动应用开发。
图 1-2。 安卓平台架构
硬件抽象层
Android 依赖 Linux 内核作为其硬件抽象层(HAL ),并提供其操作系统功能。在 Android 开发过程中,为了适应移动需求,对 Linux 内核代码进行了多次改进。以下是最显著的特征:
- 闹钟定时器
- 偏执的网络安全
- 粘合剂
- 保持进程在休眠时从屏幕消失
- Android 共享内存(Ashmem)
- 进程共享内存
- 低记忆杀手(维京黑仔)
- 记录器
虽然应用开发人员不需要直接与这些底层组件交互,但是了解它们在整个 Android 平台中的角色是非常重要的。
闹钟定时器
Android 是为在移动平台上运行而设计的,设备的唯一电力是通过电池提供的。Android 进入各种睡眠模式,以便有效地使用有限的电池资源。当设备处于睡眠模式时,应用需要一种方法来唤醒系统,以便执行某些周期性任务。在 Android 上,这是通过闹钟定时器内核模块实现的。它允许用户空间应用调度自己在未来的某个时间运行,而不管设备的状态如何。
Android 运行时中的android.app.AlarmManager类允许用户级应用通过 API 调用与闹钟定时器进行交互。报警管理器允许应用使用报警定时器安排一个意图(意图将在下一章讨论)。当警报响起时,系统广播预定意图以启动应用。只要应用忙于执行其广播接收器的onReceive方法中的代码,报警管理器就会保持一个 CPU 唤醒锁(本章稍后会介绍)。这保证了设备不会再次进入睡眠模式,直到应用完成执行其任务。闹钟定时器在设备睡眠时保留预定的闹钟;但是,如果设备关闭并重新启动,该列表将被清除。
偏执的网络安全
网络安全是任何移动平台最重要的要求之一。为了提供广泛的安全级别,Android 在尽可能低的层将这一要求作为内核修改来处理。通过这种实现,Android 限制了调用进程的组的访问。应用应该预先请求必要的权限,以便成为这些网络组的一部分。否则,这些应用的网络访问将被阻止在内核中。
粘合剂
Android 平台架构大量使用进程间通信(IPC)。应用通过使用 IPC 与系统、电话服务以及相互之间进行通信。
注意:进程间通信(IPC) 是一种允许应用相互之间以及与操作系统本身交换数据的机制。
尽管 Android 依赖于 Linux 内核来实现与操作系统相关的功能,但它并不使用通过 Linux 内核提供的 System V IPC 机制。相反,它依赖于 Android 专用的 IPC 系统,即 Binder。
活页夹技术起源于 Be 公司的工程师,是 Be 操作系统(BeOS)的一部分。Binder 的开发继续在 PalmSource 进行,作为 Cobalt 系统的关键基础,后来作为 Linux 内核模块以 OpenBinder 项目的名义被开源。Android 的 Binder 实现完全重写了 OpenBinder 项目,以符合 Apache 许可。Binder 使用内核模块在进程间通信,如图图 1-3 所示。
图 1-3。 Binder 内核模块允许两个应用进行通信
Binder 的用户空间代码在每个进程中维护一个线程池,这些线程用于将传入的 Binder 请求作为本地事件进行处理。Binder 还负责跟踪跨进程的对象引用。此外,Binder 通过在每个 Binder 请求中传输调用进程的用户和组 ID,提供了额外的安全级别。
Binder 是 Android 平台中的一个关键构造。它是整个 Android 平台的中央消息传递渠道。Android 应用通过 Binder 接口与系统、服务以及相互之间进行通信。
虽然 Binder 是作为一个底层服务实现的,但是应用开发人员并不希望直接与它进行交互。Android 运行时提供了android.os.IBinder接口作为 API,通过 Binder 与其他进程进行通信。Android 提供了 Android 界面定义语言(AIDL),该语言针对 Binder 进行了调整。
AIDL 允许您定义客户机和服务器用来相互通信的编程接口。与许多其他操作系统一样,在 Android 上,不允许进程直接访问另一个进程的内存。AIDL 提供了将对象分解成 Binder 可以理解并跨项目边界使用的原语的功能。
穿线是与 Binder 互动的最重要部分之一:
- 从本地进程发出的调用在调用线程中执行。Binder 调用是同步的,将阻塞当前线程,直到请求被处理。如果请求预计需要很长时间才能完成,则不应该从应用的主线程发出请求。这将使应用挂起,并可能导致应用被 Android 平台终止。Binder 还通过单向属性支持非阻塞请求。
- 来自远程进程的调用从本地进程提供的线程池中分派。服务代码应该是线程安全的,因为请求可以由这些线程中的任何一个执行。
Android SDK 提供了必要的代码生成器,将 AIDL 定义的编程接口转换成实际的 Java 类。应用开发人员只需要为生成的接口和将向客户端提供接口的 Android 服务提供实现。
保持进程在休眠时从屏幕消失
Android 旨在资源稀缺的移动平台上运行。正因为如此,Android 设备非常频繁地进入睡眠模式。虽然这允许系统有效地使用可用资源,但是当内核或应用正在进行重要处理时,设备进入睡眠模式并不可取。唤醒锁是作为内核补丁引入的,目的是允许应用在执行任务时防止系统进入睡眠模式。
Android 平台支持两种类型的唤醒锁:
- 空闲唤醒锁可防止系统进入低功耗空闲状态。
- 挂起唤醒锁可防止系统进入全系统挂起状态。
应用开发者通过android.os.PowerManager.WakeLock接口与唤醒锁进行交互。要使用这个接口,应用应该提前请求android.permission.WAKE_LOCK。
唤醒锁应谨慎使用。阻止设备进入睡眠模式将增加功耗,最终导致电池电量耗尽。应用应在重要操作期间保持唤醒锁,并在操作完成后立即释放唤醒锁。
Android 共享内存
Android 共享内存(Ashmem))是 Android 平台上一个类似 POSIX 的共享内存子系统,作为内核模块实现。Ashmem 针对移动需求进行了高度优化,它为低内存设备提供了更好的支持。Ashmem 支持可以在多个进程之间共享的引用计数对象。
进程共享内存
除了 Ashmem,Android 还提供了第二种类型的共享内存子系统,称为进程共享内存(Pmem)。Pmem 用于在进程间共享大量物理上连续的内存。Android 媒体引擎主要使用 Pmem 在媒体引擎和应用进程之间传递大型媒体帧。
低内存黑仔
低内存杀手,也被称为维京黑仔,是 Linux 内核中其他特定于 Android 的增强功能之一。此功能允许系统在内存耗尽之前回收内存。
为了启动应用,设备必须首先将应用代码从永久存储器读取到随机存取存储器(RAM)中。由于这是一个耗时且昂贵的过程,Android 试图尽可能长时间地保持应用进程。但最终,当内存不足时,它需要将它们从 RAM 中删除。
为防止耗尽内存而删除应用的顺序取决于应用的重要性,这由用户与该应用交互的当前状态来衡量:
- 用户当前正在与之交互的具有前台活动的应用被认为是最重要的。
- 具有可见活动的应用(当前没有与用户交互,但仍然可见)也被认为是重要的。
- 具有对用户不再可见的后台活动的应用被认为是不重要的,因为它的当前状态可以被保存,并且稍后当用户导航回该活动时可以被恢复。
当从内存中删除进程时,Android 从最不重要的应用开始。空进程是指没有活动、服务或广播接收器的进程。这些类型的应用被认为是最不重要的,Android 首先开始删除它们。
使用/etc/init.rc系统配置文件配置每个应用状态的阈值。表 1-2 列出了这些阈值。
低内存杀手服务通过ActivityManagerService获得这些信息。
记录器
日志记录是故障排除最重要的部分,但是很难实现,尤其是在移动平台上,应用的开发和执行发生在两台不同的机器上。Android 有一个广泛的日志系统,允许系统范围内集中记录来自 Android 系统本身和应用的信息。
Android 日志系统被实现为一个内核模块,称为 logger。还提供了一组 API 调用和用户级应用来与记录器模块进行交互。
任何给定时间平台上记录的信息量使得查看和分析这些日志消息非常困难。为了简化这个过程,Android 日志记录系统将日志消息分成四个独立的日志缓冲区:
- *主:*主应用日志消息
- *事件:*系统事件
- *无线电:*与无线电相关的日志消息
- *系统:*用于调试的低级系统调试消息
这四个缓冲区作为伪设备保存在/dev/log系统目录下。因为移动平台上的输入和输出(I/O)操作非常昂贵,所以日志消息不保存在永久存储器中;相反,它们被保存在内存中。为了控制日志消息的内存利用率,logger 模块将它们放在固定大小的缓冲区中。主日志、广播日志和系统日志作为自由格式的文本消息保存在 64KB 的日志缓冲区中。事件日志消息携带二进制格式的附加信息,它们保存在 256KB 的日志缓冲区中。
还提供了一组用户级应用来查看和过滤这些日志,例如 logcat 和 Dalvik 调试监控服务器(DDMS)工具,我们将在第五章中研究这些工具。
Android 运行时提供了一组 API 调用,允许应用轻松地将其日志消息发送给日志记录器。应用日志消息通过以下类发送:
android.util.Log:该类用于发送应用日志消息。它提供了一组方法来指定消息的优先级,还提供了一个标记来指示哪个应用正在生成这个日志消息。android.util.EventLog:这个类用于发送二进制格式的事件日志消息。- 这个类被 Android 运行时组件用来发送系统日志消息。它不是 Android API 的一部分,也不能从应用中访问。
合子
在大多数类似 UNIX 的操作系统中,被称为 Init 的应用被认为是所有进程的父进程。Init 在内核成功启动后启动。它的主要作用是根据系统配置启动一组其他进程。
Zygote 也称为“app 进程”,是系统启动时由 Init 启动的核心进程之一。Zygote 在 Android 平台中的角色与 Init 非常相似。它的首要任务是启动一个新的 Dalvik 虚拟机实例,并初始化核心 Android 服务,如下所示:
供电业务
活动服务
打包服务
内容服务
报警服务
窗口服务
内容提供商
电话服务
电池服务
看门狗
启动这些服务后,Zygote 开始进行它的第二项任务,这也是它名字的由来。
**注:**根据其字典定义,受精卵是最初形成的细胞。在单细胞生物中,受精卵分裂产生后代。
如前所述,在 Android 上,每个应用都在其专用的虚拟机实例中运行。此外,Android 应用依赖于一组类和数据对象,这些对象需要首先加载到内存中,以便应用执行其任务。当启动一个新的应用时,这会带来很大的开销。尽管有这种开销,Android 需要尽可能缩短启动时间,以便提供高度响应的用户体验。通过使用分叉,Zygote 以一种快速有效的方式解决了这个问题。
在计算中,分叉是克隆现有进程的操作。新进程拥有父进程所有内存段的精确副本,尽管两个进程都独立执行,如图 1-4 所示。写时复制是分叉中使用的一种优化策略,通过允许两个进程共享同一个内存段,直到其中一个进程试图修改它,从而延迟内存的复制。
图 1-4。 Zygote 等应用共享安卓框架只读组件
由于 Android 运行时类和数据对象对于应用来说是不可变的,这使得它们成为分叉期间写时复制优化的理想候选对象。
Zygote 预加载 Android 运行时对象,并等待启动新应用的请求。当一个新的请求到达时,它不是启动一个新的虚拟机实例,而是简单地分叉。这使得新的应用可以快速启动,同时保持较低的内存占用。
达尔维克虚拟机
Java 是一种通用的、面向对象的编程语言,专门为平台无关的应用开发而设计,目标是“编写一次,在任何地方运行”Java 通过将应用代码编译成一种称为字节码的中间独立于平台的解释语言来实现这一点。在运行时,这个字节码通过另一个叫做 Java 虚拟机的 Java 实体来执行。
虚拟机是运行在主机上并解释字节码的本地应用。为了优化复杂应用的运行时,大多数虚拟机实现还支持实时(JIT)特性,这允许从字节码到本机代码的即时翻译。这允许长时间运行的应用执行得更快,因为只有在应用执行的开始才需要解释字节码。
大多数移动平台面临的最大挑战之一是缺乏应用。为了从一开始就解决这个问题,Android 依赖于久经考验的 Java 编程语言,这种语言已经拥有非常庞大的开发人员社区,以及将促进应用开发的应用、工具和组件。
Android 还依赖于高度定制的虚拟机实现,该虚拟机实现针对移动需求进行了调整。Dalvik 虚拟机是 Android 为移动平台定制的 Java 虚拟机。
Dalvik 虚拟机与其他 Java 虚拟机实现有很大不同。桌面平台上的大多数虚拟机实现都是基于基于堆栈的虚拟机模型开发的。由于移动需求,Dalvik 虚拟机基于基于寄存器的虚拟机模型。基于寄存器的虚拟机需要更长的指令来解释;然而,与基于堆栈的虚拟机相比,实际执行的指令数量非常少。这使得基于寄存器的虚拟机成为移动环境的更好选择,在移动环境中,计算能力是一种稀缺资源。
由于 Dalvik 虚拟机需要不同类型的字节码来解释,所以它不支持标准的 Java 类文件,它依赖于自己的格式,这种格式被称为 Dalvik 可执行文件(DEX)。Android 软件开发平台附带了一套工具,用于将编译后的 Java 类文件后处理成 DEX 格式。
DEX 格式也是在移动平台上存储编译后的 Java 应用代码的一种更紧凑的方式。标准 Java 应用由多个单独存储的类文件组成。DEX 将所有的类文件合并成一个大的 DEX 文件,如图图 1-5 所示。这最小化了应用代码的占用空间。
图 1-5。 从 JAR 文件中的标准 Java 类文件到单个 DEX 文件的转换
DEX 格式的常量池允许将字符串、类型、字段和方法常量以及代码中的其他内容存储在一个地方,使用这些列表的索引而不是全名。这将类文件的大小减少了近 50%。
Android 平台在自己的专用虚拟机实例中运行每个应用,作为沙箱。这就对平台提出了很高的要求,因为多个虚拟机被期望在有限的 CPU 资源环境中同时运行。Dalvik 虚拟机专门针对这种环境进行了调整。
文件系统
文件系统是操作系统中非常重要的一部分。特别是在移动平台上,文件系统在满足操作系统的期望方面起着重要的作用。
移动设备依赖基于闪存的存储芯片。Android 依赖另一个 Flash 文件系统(YAFFS2)作为其主要文件系统。YAFFS2 是 Charles Manning 为 Linux 操作系统设计和编写的一个开源文件系统实现。YAFFS2 是一个高性能文件系统,专门设计用于基于 NAND 的闪存芯片。它是一个日志结构的文件系统,将数据完整性作为一个高优先级。
除了文件系统,操作系统文件和组件的组织结构在 Android 中也起着重要的作用。为了保护用户的机密信息,移动平台应该易于升级并且高度安全。Android 通过使用多个分区来组织自己,从而解决了这一需求。通过将操作系统的不同部分保存在不同的分区中,Android 提供了高级别的安全性,也使平台易于升级。
使用的分区取决于设备制造商。以下是一些最常见的问题:
/boot:这个分区包括引导设备所需的引导加载程序和 Linux 内核。用户应用无法写入该分区,因为修改该分区的内容可能会导致设备无法再启动。/system:这个分区包含了所有预装在设备上的 Android 系统文件和应用。在升级过程中,这个分区会被最新版本的 Android 平台替换。该分区不可由用户应用写入,尽管 Android Market 应用可以使该分区暂时可写,以便更新预加载的应用。/recovery:这个分区保存了一个恢复镜像,是一个备选的引导分区。它提供维护功能,以便恢复系统或执行其他任务,如进行系统备份。该分区也不能从用户应用写入。/data:这个分区保存用户的应用以及用户的数据,比如联系人、信息和设置。当设备恢复出厂设置时,该分区会被擦除。/cache:该分区用于存储经常访问的文件。在大多数 Android 设备上,cache不是闪存介质上的分区,而是存储在 RAM 中的虚拟分区。当设备重新启动时,该分区的内容不会保留。/sdcard:这是内部存储器上的挂载点,而不是分区。连接到设备的 SD 卡以此名称安装。应用并不总是可以访问这个挂载点,因为当设备通过 USB 连接时,它可能直接挂载在主机 PC 上。
虽然 Android 并不期望应用开发人员直接使用这些分区,但是了解它们的用途在 Android 应用开发过程中非常有用。
安全
与许多其他移动平台一样,从用户的角度来看,Android 的最大要求是用户应用和数据的安全性和完整性。Android 的设计考虑到了安全性。
Android 架构在平台的多个层面提供安全性。这个广泛的安全框架也通过 Android 运行时向开发人员公开。精通安全的开发人员可以很容易地依赖这些 API,以便为他们的应用及其使用的数据提供高级别的安全性。不太熟悉安全性的开发人员已经受到默认安全设置的保护。
Android 通过使用来自硬件和软件的多种安全特性来提供高级别的安全性。虽然它被设计为在各种硬件平台上工作,但 Android 仍然利用了特定于硬件的安全功能,如 ARMv6 的永不执行功能。
Android 平台是建立在 Linux 内核之上的。Linux 内核本身已经在许多安全敏感的环境中使用了很多年。Linux 内核为 Android 提供了几个关键的安全特性,如下所示:
- 基于用户的权限模型
- 进程隔离
- 安全 IPC 机制
- 从内核本身移除不必要功能的能力
Linux 内核是为多用户平台设计的。虽然 Android 是单用户环境,但它仍然利用了基于用户的权限模型。Android 在虚拟机沙箱中运行应用,将它们视为系统中的不同用户。仅仅依靠基于用户的权限模型,Android 就可以通过阻止应用访问其他应用的数据和内存来轻松保护系统。
在 Android 上,服务和硬件资源也通过基于用户的权限模型得到保护。这些资源都有自己的保护组。在应用部署期间,应用请求访问这些资源。如果用户同意请求,应用将成为这些资源组的成员。如果应用不是该资源组的成员,则不允许它访问任何附加资源。
除了操作系统提供的安全功能,Android 还使用 ProPolice 增强了 Android 平台二进制文件,以保护它们免受堆栈缓冲区溢出攻击。
文件系统保护也是自 Android 3.0 以来可用的 Android 新功能之一。它允许 Android 使用 AES-128 算法加密整个存储介质。这可以防止其他人在不知道使用的密钥的情况下访问用户的数据。
设备管理是自 Android 2.2 以来可用的其他安全功能之一。它允许管理员远程实施安全策略,并在设备丢失或被盗时远程擦除设备。
服务
Android 平台不仅限于 Linux 内核提供的特性。Android 运行时为应用开发人员提供了许多服务。以下是提供的主要服务。
- 可访问性服务:该服务通过
android.view.accessibility.AccessibilityManager类提供。它是一个系统级服务,充当可访问性事件的事件调度程序,并提供一组 API 来查询系统的可访问性状态。 - 账户服务:该服务通过
android.accounts.AccountManager类提供。它是用户在线账户的集中注册。它允许应用在用户批准后使用用户的帐户访问在线资源。 - 活动服务:该服务通过
android.app.ActivityManager类提供。它允许应用与系统中运行的活动进行交互。 - 报警服务:该服务通过
android.app.AlarmManager类提供。它允许应用向报警服务注册,以便在将来的某个时间安排执行。 - 音频服务:该服务通过
android.media.AudioManager类提供。它允许应用控制音量和铃声模式。 - 剪贴板服务:该服务通过
android.content.ClipboardManager类提供。它允许应用将数据放入系统剪贴板,并从剪贴板中检索数据。 - 连接服务:该服务通过
android.net.ConnectivityManager类提供。它允许应用查询网络连接的状态。当网络连接发生变化时,它也会生成事件。 - 设备策略服务:该服务通过
android.app.admin.DevicePolicyManager类提供。设备管理 API 提供系统级的设备管理功能。它允许开发在企业环境中有用的安全感知应用。 - 下载服务:该服务通过
android.app.DownloadManager类提供。它处理长时间运行的 HTTP 下载。应用可以使用此服务请求将 URI 下载到特定的目标文件。下载服务负责 HTTP 交互,并在失败、连接更改和系统重启后重试下载。 - 投件箱服务:该服务通过
android.os.DropBoxManager类提供。它提供系统范围的、面向数据的日志存储。它从应用崩溃、内核日志和其他来源收集数据。数据不会直接发送到任何地方,但是调试工具可能会扫描并上传条目进行处理。 - 输入法服务:该服务通过
android.view.inputmethod.InputMethodManager类提供。它允许应用通过提供的方法与输入法框架(IMF)进行交互。 - 通知服务:该服务通过
android.app.NotificationManager类提供。它允许应用通知用户发生的事件。后台运行的服务仅通过此服务与用户通信。 - 位置服务:该服务通过
android.location.LocationManager类提供。它允许应用获得设备当前位置的定期更新。 - 近场通信服务:该服务通过
android.nfc.NfcManager类提供。它允许应用使用设备的近场通信(NFC)功能。 - 包服务:该服务通过
android.content.pm.PackageManager类提供。它允许应用检索与当前安装在系统上的应用包相关的信息。 - 电力服务:该服务通过
android.os.PowerManager类提供。它允许应用控制设备的电源状态。它允许应用保持唤醒锁,以防止设备在执行任务时进入睡眠模式。 - 传感器服务:该服务通过
android.hardware.SensorManager类提供。它允许应用访问设备的传感器。 - 电话服务:该服务通过
android.telephony.TelephonyManager类提供。它允许应用与移动设备的电话功能进行交互。它还为应用生成事件来监视电话状态的变化。 - UI 模式服务:该服务通过
android.app.UiModeManager类提供。它允许应用控制设备的用户界面(UI)模式,例如禁用汽车模式。 - USB 服务:该服务通过
android.hardware.usb.UsbManager类提供。它允许应用查询 USB 的状态,并通过 USB 设备与设备通信。 - 振动器服务:该服务通过
android.os.Vibrator类提供。它允许应用控制设备上的振动器。 - 壁纸服务:该服务通过
android.service.wallpaper.WallpaperService类提供。它允许应用在后台显示动态壁纸。 - Wi-Fi 点对点服务:该服务通过
android.net.wifi.p2p.WifiP2pManager类提供。它允许应用发现可用的对等点,并通过 Wi-Fi 网络建立对等连接。 - Wi-Fi 服务:该服务通过
android.net.wifi.WifiManager类提供。它允许应用管理 Wi-Fi 连接。应用可以列出和更新已配置的网络,访问接入点扫描的结果,以及建立和断开连接。
Android 部署和分发
因为 Android 平台是一个免费的平台,它不收取任何许可费、版税、会员费或认证费来开发和分发平台上的应用。
Android 平台让应用开发者决定如何分发和赚钱他们的应用。应用开发者可以以免费软件、共享软件、广告赞助或付费的方式分发他们的应用。
Android 平台附带了一个默认的市场,Google Play,以前称为 Android Market,是谷歌为 Android 设备开发的在线商店。与 Android 平台不同,Android Market 应用不是开源的。它仅适用于符合谷歌兼容性要求的设备。客户端部分预装在 Android 设备上,名为 Market。用户可以使用该应用搜索和下载 Android 应用。市场应用还通过通知用户软件更新来保持已安装的 Android 应用最新。
应用开发者使用 Android 市场的服务器部分。通过基于 web 的界面,应用开发人员可以上传他们的应用进行发布。
Android Market 对分布式应用运行一组测试,但它不对从 Android Market 下载的应用承担任何责任。在安装过程中,Android Market 应用显示应用请求的列表权限,并在继续安装之前获得用户的隐式权限。
虽然大多数 Android 设备预装了谷歌的 Android Market 应用,但 Android 平台支持其他应用分发渠道。GetJar 和 Amazon Appstore 是 Android 应用分发的两个替代方案。
总结
我们在这一章开始时简要概述了 Android 的历史和现有的 Android 版本。然后,我们探讨了 Android 平台和 Linux 内核的核心,并简要回顾了为交付卓越的移动平台而对 Linux 内核进行的特定于 Android 的更改和添加。
我们解释了 Android 选择 Java 技术作为 Android 应用基础的原因,以及 Dalvik 虚拟机为优化移动计算的 Java 技术而提供的独特功能。我们探索了 Zygote,这个应用进程使 Android 应用具有快速的启动时间和较小的内存占用。我们还研究了 Android 多层安全框架。然后,我们简要介绍了允许应用与 Android 平台交互的 Android 框架服务。最后,我们讨论了 Android 的开发和发行。
在下一章,我们将关注 Android 应用架构。
二、应用架构
理解 Android 应用的架构是开发可靠应用的关键。在这一章中,我们将开始探索 Android 应用架构。
首先,我们将简要回顾 Android 框架提供的基本组件,如活动、服务、广播接收器、内容提供者和用户界面组件。然后,我们将非常详细地检查活动和服务生命周期。接下来,我们将完成打包 Android 应用以进行部署的过程。最后,我们将研究 Android manifest 文件,以及它在 Android 应用开发中的作用和重要性。
安卓组件
Android 和其他移动平台的主要区别在于应用的定义。
其他移动平台将应用定义为在自己的沙箱中运行的独立程序,与周围平台的交互有限。移动平台提供 API,允许应用使用平台服务和数据存储,如地址簿,以提供丰富的用户体验。然而,这种通信总是单向的,这意味着应用可以使用平台服务,但是平台和其他应用不能访问另一个应用提供的服务。
在 Android 上,应用就像是模块。每个应用都由一组组件组成,平台和其他应用都可以访问这些组件。Android 上的每个新应用都通过提供一组新的组件来扩展平台,并为其他应用开发者提供更多机会。应用开发人员不需要决定一组 API 或一个契约来实现应用之间的互操作性。
Android 框架定义了四个主要组件:活动、服务、广播接收器和内容提供者。每个应用不需要使用所有这些组件,但是正确使用它们可以让应用完全融入平台。
活动和意图
活动是应用最重要的组成部分。它对应于一个显示屏。用户只能通过活动与 Android 应用进行交互。一个应用可以由一个或多个活动组成。每个活动都允许用户执行特定的任务。为了模块化,期望每个活动做一个单独的任务。
用户发起一个新的活动是为了完成某项任务。这些意图在 Android 框架中被捕获为意图。意图是对要执行的操作的抽象描述。它使用被动数据结构提供不同组件之间的后期运行时绑定。
Android 保留了从意图到活动的映射,并基于给定的意图发起正确的活动。对于某些意图,可能有多个活动可以完成该任务。在这种情况下,Android 会向用户提供这些活动的列表以供选择。
一项复杂的任务可能涉及不止一项活动。在这种情况下,当用户从一个活动移动到另一个活动时,活动保存在活动堆栈中。
为了更好地理解活动的概念,让我们想象一个简单的用例,其中用户正在发送电子邮件:
- 用户按下屏幕上的撰写电子邮件按钮。
- 该代码捕获用户的意图,将电子邮件消息组成一个意图对象,并将该意图提供给 Android 框架。
- Android 检查它的注册表,提取能够满足这个意图的活动,并将这个新活动添加到活动堆栈的顶部。开始时,活动会占据整个屏幕。
- 用户按下选择接收者按钮。用户的意图在一个新的意图对象中被捕获,Android 框架再次检查它的注册表并启动联系人列表活动。
- 用户从列表中选择一个或多个接收者,并选择完成按钮。结果,该活动将用户的选择返回到 Android 框架,并通过使之前的活动再次可见,将自己从活动堆栈中移除。
- 收到 Android 框架的结果后,撰写电子邮件活动会相应地在用户界面中填充所选收件人的列表。
- 完成消息后,用户单击 Send 按钮,电子邮件就被发送了。
- 撰写电子邮件活动将其自身从活动堆栈中移除,用户返回到他或她开始的屏幕。
应用不仅限于使用自己的活动。在任务流期间,应用可以利用由平台或其他应用提供的其他活动。例如,要从用户的地址簿中选择一个联系人,应用可以使用平台已经提供的活动,而不是编写新的活动。这种方法促进了活动的重用,也提供了整个平台的一致性。
活动是为与用户互动而设计的。当它们对用户不再可见时,Android 可能会随时终止它们,以便释放内存资源。出于这个原因,活动不适合执行预计需要很长时间才能完成的任务,例如从互联网上下载文件。Android 框架提供了运行这些类型任务的服务组件。
服务
服务在后台运行。服务不提供用户界面,它们不能直接与用户交互。Android 并没有限制它们的生存期,只要系统有足够的资源来执行前台任务,它就允许它们继续在后台运行。应用可以提供与用户交互的活动,以便控制服务。
例如,假设我们正在开发一个音乐播放器应用。我们希望让用户选择一个音乐文件,并在继续使用设备的同时收听它。初始活动可以与用户交互以选择歌曲;但是,活动不能直接播放歌曲,因为活动的生存期受到其可见性的限制。我们将需要有一个服务,将在后台运行,以便应用可以继续播放歌曲,而用户正在用设备做其他任务。在任何给定的时间,用户都可以启动一个活动来控制服务,因为服务本身不能直接与用户交互。
与活动一样,应用并不局限于它自己的服务。应用还可以使用由平台或其他应用提供的服务。例如,为了连续接收全球定位系统(GPS)坐标,应用可以启动由平台提供的 GPS 服务。
服务也是通过意向启动的。根据设计,在任何给定时间,只有一个服务实例可以运行。Android 框架在第一个请求到达时启动服务,然后将后续请求交付给已经运行的实例。
有时服务可能需要用户的关注。服务使用通知来通知用户服务的当前状态。例如,在我们的音乐播放器应用中,当新歌曲开始播放时,可以在通知栏上显示带有歌曲名称的通知来通知用户。
广播接收器
应用不仅与用户交互,还通过生成和消费事件与平台和其他应用交互。在 Android 上,这些事件也以意图的形式交付。
为了接收某些类型的事件,应用可以通过提供一个广播接收器来注册一组意图。当系统中生成匹配事件时,Android 会将该事件传递给广播接收器。
例如,假设我们想让我们的应用在手机开机时自动启动。在我们的应用中,我们指定应用对接收设备启动事件感兴趣。当设备启动时,它会广播事件。只有感兴趣的应用通过它们的广播接收器接收该事件。
内容提供商
内容提供商允许 Android 应用与平台和其他应用交换数据。与其他组件不同,内容提供者不依赖意图。相反,内容提供者使用内容 URIs 形式的标准接口,并以一个或多个表的形式提供对数据的访问,这些表类似于关系数据库中的表。这些表的结构通过Contract类与外部应用通信。Contract类不是内容提供商框架的一部分。内容提供商开发人员应该定义并使Contract类对外部应用可用。
当应用发出内容提供商查询时,Android 通过注册中心将给定的 URI 与适当的内容提供商进行匹配。Android 框架检查以确保应用具有必要的特权,并将请求发送给相应的内容提供商。响应以光标的形式返回到发出请求的应用。然后,应用通过光标提供的界面检索和操作数据。
视图、小工具、布局和菜单
视图对象是 Android 平台上用户界面的基本单元。一个视图对象是一个数据结构,其属性存储布局参数和屏幕上矩形区域的内容。它提供了处理其绘图和布局测量所需的方法。
一个小部件是一个视图对象,允许应用与用户交互。Android 运行时提供了一组丰富的小部件,使应用开发人员能够轻松开发全面的用户界面。Android 应用开发人员并不局限于使用 Android 运行时提供的小部件。通过派生新的视图对象,开发人员可以从头开始创建新的小部件,或者基于现有的小部件创建新的小部件。通过基类android.view.View提供一个小部件。
一个布局用于表达视图层次以及每个视图组件应该如何在显示器上定位。由于 Android 设备的大小、分辨率和方向差异很大,这种布局允许应用开发人员根据设备的规格动态定位视图组件。Android 运行时提供了一组丰富的布局组件,允许基于一组不同的约束来定位视图。通过基类android.view.ViewGroup提供布局。以下是一些常见的布局对象:
- *框架布局:*这是最简单的布局对象类型。它是通过
android.widget.FrameLayout类提供的。这是一种基本布局,只能容纳一个视图对象,该对象将占据框架视图所覆盖的整个空间。 - *线性布局:*该布局允许为视图对象分配权重,并相应地定位它们。它是通过
android.widget.LinearLayout类提供的。它可以根据其配置垂直或水平定位视图对象。所有视图对象都被卡住,一个接一个。可以使用配置参数引入余量。此外,可以指定一个视图对象来填充整个空白显示区域。 - *表格布局:*该布局允许视图对象以类似表格的格式按行和列放置。它是通过
android.widget.TableLayout类提供的。尽管它遵循表格格式,但它不在单元格周围提供边框。此外,单元格不能跨列。 - *相对布局:*该布局允许视图对象在显示器上相对定位。它是通过
android.widget.RelativeLayout类提供的。它是最高级的布局组件之一。
除了小部件和布局,应用菜单对于用户界面开发也非常重要。应用菜单为应用功能和设置提供了可靠的界面。
使用 Android 设备上的硬菜单按钮和软菜单按钮来显示菜单。菜单正在慢慢失去其重要性,并被 Android 平台更高版本的动作栏所取代。从 Android 3.0 开始,主机设备不再需要提供硬菜单按钮。
Android 用户界面是根据应用的功能将视图、小部件、布局和菜单结合起来形成的。Android 框架允许应用将它们的用户界面动态定义为应用代码的一部分,或者它们可以依赖于特定于 Android 平台的基于 XML 的用户界面定义语言。这种基于 XML 的语言允许在实际应用逻辑之外设计和管理视图代码。此外,应用开发人员可以在不改变应用逻辑的情况下,为纵向和横向显示设计不同的用户界面。
Android 应用可以从应用代码中控制和填充视图对象。由于 Android 平台的用户界面架构,用户界面预计只能从主 UI 线程进行修改。不支持从应用线程修改用户界面,这可能会在应用运行时导致问题。Android 运行时提供了一个增强的消息队列系统,允许应用开发人员通过主 UI 线程调度与用户界面相关的任务。
尽管 Android 应用可以操作视图对象,但是由于保持用户界面与数据模型一致的复杂性,具有大量用户界面和数据模型组件的应用可能更难开发。为了解决这个问题,Android 运行时提供了将数据绑定到视图的适配器。这允许用户界面组件自动反映对数据模型的任何更改。视图对象android.widget.Gallery、android.widget.ListView和android.widget.Spinner是使用适配器将数据绑定到 Android 平台上的视图对象的很好的例子。
资源
Android 架构鼓励用户尽可能地将应用资源从应用源代码中外部化。通过将资源外部化,Android 应用可以根据设备配置和当前语言环境使用不同的图形和文本资源。Android 平台目前支持以下资源:
- 动画资源
- 色彩资源
- 可提取资源
- 布局资源
- 菜单资源
- 字符串资源
- 样式资源
- 价值资源
应用资源放在应用的res目录中。有不同的子目录来分组不同的资源。
在编译期间,Android 生成一个资源类,允许应用在代码中引用这些资源。
数据存储
Android 平台提供了多种保存持久应用数据的方法。以下是一些备选方案:
- *共享偏好:*这种方法允许应用将数据存储为键/值对。Android 框架带有实用功能,允许开发人员轻松维护共享偏好。共享首选项只支持基本数据类型,应用应该执行将数据转换为基本类型所需的任何封送处理。Android 平台还保证共享的偏好设置会被保存,即使应用被终止。
- *内部和外部存储:*这种方法允许应用开发人员将任何类型的数据作为普通文件存储在平台上。Android 框架提供了一组实用函数,允许应用开发人员轻松地进行这些文件操作,而无需知道这些文件的实际位置。
- *SQLite 数据库:*使用 SQLite 数据库允许应用开发人员轻松地存储和检索结构化数据。SQLite 在应用的进程空间中提供了一个关系数据库。虽然 SQLite 功能是通过本地库提供的,但 Android 框架包括一组实用函数和类,允许应用开发人员轻松地与 SQLite 数据库进行交互。
Android 生命周期
Android 应用的生命周期比桌面应用的生命周期复杂得多。桌面应用的生命周期由用户直接控制。用户可以选择在任何给定的时间启动和终止应用。然而,在 Android 上,平台管理应用的生命周期,以便高效地使用稀缺的系统资源。
活动生命周期
活动生命周期是活动从第一次创建到被销毁所经历的一组状态。
Android 框架提供了一套生命周期方法,允许应用在活动生命周期发生变化时做出适当的调整。例如,如果活动对用户不再可见,它就没有理由消耗 CPU 周期在屏幕上显示动画。在这种情况下,应用应该停止执行任何 CPU 密集型操作,以便让前台应用获得足够的系统资源来提供流畅的用户体验。
在android.app.Activity类中定义了七种生命周期方法:
public class Activity { protected void onCreate(Bundle savedInstanceState); protected void onStart(); protected void onRestart(); protected void onResume(); protected void onPause(); protected void onStop(); protected void onDestroy(); }
图 2-1 用这些方法说明了活动生命周期状态机。
图 2-1。 活动生命周期状态机
这些活动生命周期方法的工作方式如下:
onCreate:创建活动时调用该方法。它初始化活动并创建视图。该方法还接受一个Bundle对象,该对象包含活动上次运行时的冻结状态。活动使用这个包来恢复其先前的状态。这个方法调用后面总是跟着onStart。onStart:当活动变得可见时,调用这个方法。如果该活动处于前台,则随后调用onResume。如果活动变得隐蔽,则接下来是onStop。onRestart:当活动重新显示给用户时,调用这个方法。其次是onStart。onResume:每次活动来到前台与用户交互时,都会调用这个方法。onPause:该方法在活动即将进入后台,但尚未终止时调用。这个回调主要用于保存任何持久状态。这也是停止任何 CPU 密集型操作并释放任何系统资源(如相机)的好地方。当应用处于暂停状态时,如果需要为前台应用回收资源,系统可以随时决定终止应用。由于这个原因,应用应该在这个调用中保存它的当前状态,因为它可能没有第二次机会。根据用户与前台应用的交互,这个调用之后是onResume或onStop。onStop:当用户看不到活动时,调用这个方法。如果活动即将到达前台,则随后调用onRestart,如果活动即将终止,则调用onDestroy。onDestroy:这个方法在活动被销毁的时候调用。这可能是因为活动即将结束,或者因为系统需要释放资源。预计应用将在此时释放其资源。
**注意:**当覆盖这些生命周期方法时,不要忘记调用超类。Android 本身也需要密切监控这些生命周期事件,以便正常运行。
活动应该通过在onPause方法中保存其状态来结束,尽管onStop和onDestroy方法跟在它后面。Android 平台保证应用在执行onPause方法中的任何工作时,应用进程不会被终止;在执行onStop或onDestroy方法时,应用可能会被终止。但是,您应该非常小心,不要在onPause方法上花费太多时间,因为 Android 平台和用户都在等待这个方法完成,然后再将下一个活动放到前台。在onPause方法上花费太多时间会让系统看起来对用户的请求不负责任。
服务生命周期
服务生命周期类似于活动生命周期,但有几个很大的区别。因为服务不直接与用户交互,所以它们的生命周期不像活动那样依赖于用户的操作。因为服务不关心可见性,所以生命周期方法onPause、onResume和onStop对它们不适用。
在android.app.Service类中定义了三种生命周期方法:
public abstract class Service { public void onCreate(); public int onStartCommand(Intent intent, int flags, int startId); public void onDestroy(); }
图 2-2 展示了使用这些方法的服务生命周期状态机。
图 2-2。 服务生命周期状态机
这些服务生命周期方法的工作方式如下:
-
onCreate:当Context.startService(Intent)方法被应用使用并且服务还没有运行时,这个方法被调用。由于服务被设计成单例的,所以服务在其生命周期中只得到一次onCreate调用。 -
onStartCommand: This method is called each time theContext.startService(Intent)method is used by the application. A service may end up processing multiple requests, so it is possible for the service to receive multipleonStartCommandcalls during its lifetime. If the service is already busy processing the previous request, it is expected that the service will queue this new request.**注意:**当前台应用需要更多资源时,Android 平台可能会决定销毁一个正在运行的服务,然后在资源条件改善时重新启动它。如果您的服务需要存储持久数据以便在重启后继续正常运行,那么最好在调用
onStartCommand期间存储这些数据。 -
onDestroy:该方法在服务即将被 Android 平台销毁时调用。
包装
Android 包文件(APK)文件格式用于打包和分发 Android 应用。APK 文件实际上是压缩文件格式的档案文件。除了应用类文件的打包方式之外,它们部分遵循 JAR 文件格式。APK 文件包含以下内容:
META-INF/MANIFEST.MF:这是包文件本身的 JAR 清单文件。META-INF/CERT.SF:包含了包文件中包含的文件的 SHA1 哈希。该文件由应用开发人员的证书签名。META-INF/CERT.RSA:这是用于签署CERT.SF文件的证书的公钥。AndroidManifest.xml:这是应用的清单文件。它是 Android 应用最重要的组件之一,我们将在下一节简要探讨它。classes.dex:这是 DEX 格式的应用类文件。assets:这很特别,因为在生成 APK 文件时,其内容不会被压缩。这允许 Android 平台在运行时直接向 APK 文件提供文件描述符,因此应用可以轻松地访问资源,而无需将它们提取到设备中。Android 开发人员希望将大型资源文件保存在assets目录中,以最小化应用在已安装设备上的占用空间。res:该目录包含应用资源。resources.arsc:包含视图定义和字符串资源。
APK 文件用证书签名,证书的私钥由应用开发人员持有。该证书确定了应用的作者,以及 APK 文件中包含的文件的完整性。与许多其他移动平台相比,Android 不要求这些证书由认证机构签名。Android 开发人员可以生成并使用自签名证书来签署他们的应用。
Android 平台在软件更新期间使用证书,以确保更新来自与创建已经安装在系统上的应用的作者相同的作者。除了更新之外,Android 平台还依赖于证书,同时在安装期间向应用授予签名级别的权限。
安卓清单
Android 应用通过一个名为AndroidManifest.xml的清单文件向系统描述。所有 Android 应用的根目录中都应该有这个文件。Android 清单文件向系统呈现关于应用的基本信息,以便让 Android 平台正确运行应用的代码,并在安装期间授予必要的权限。
Android 清单文件向系统提供以下信息:
- 它包括应用的名称、包名和版本号。
- 它表示运行应用所需的 API 的最低版本。
- 它描述了应用的组件(活动、服务、广播接收器和内容提供者)以及它们在处理意图方面的能力。
- 它声明需要哪些权限才能访问 Android 运行时的受保护部分,并与系统上运行的其他应用进行交互。
- 它声明了其他应用需要拥有的权限,以便与该应用的组件进行交互。
- 它列出了应用在运行时为了运行而必须链接的库。
Android 清单文件是一个 XML 格式的纯文本文件。下面是一个AndroidManifest.xml文件的例子:
` <manifest xmlns:android="schemas.android.cm/apk/res/and…" package="com.apress.example" android:versionCode="1" android:versionName="1.0.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MyActivity" android:label="@string/my_activity">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="9" /> `
总结
本章通过简要回顾最基本的 Android 组件,包括活动、服务、广播接收器、内容提供者和其他用户界面组件,介绍了 Android 应用架构。我们试图阐明 Android 开发中最令人困惑的概念之一:活动和服务生命周期。然后我们探索了打包 Android 应用的过程,并仔细查看了 Android 清单文件。接下来的章节展示了这些概念的应用实例。
三、Eclipse 优先
Eclipse 是一个集成开发环境,我们将在整个 Android 开发过程中使用它。Eclipse 不仅仅是一个简单的代码编辑器。它是一个非常强大和复杂的工具平台。从这个角度来看,这一章非常重要,因为我们将通过为 Eclipse 设置合适的工作环境来为后面的章节建立框架。
本章将介绍 Eclipse,强调最常用的 Eclipse 组件。熟悉 Eclipse 是顺利体验 Android 开发的关键。
Eclipse 历史
1995 年,Sun Microsystems 向公众发布了 Java 编程语言的第一个公共实现。Java 的到来将开发者社区分成了两个群体:一个以微软技术和工具为中心,另一个以 Java 平台为中心。
Visual Studio 是微软的工具平台,以集成的方式提供对所有微软技术的访问。市场上有许多成功的 Java 开发工具,但是它们没有像微软技术那样紧密集成。
在 20 世纪 90 年代末,IBM 是 Java 的主要参与者。IBM 当时的主要目标是让开发人员更接近 Java 中间件。IBM 知道理想的开发环境必须由来自 IBM、第三方和客户内部工具的异构组合组成。IBM 的对象技术国际(OTI)实验室是 VisualAge 产品家族的幕后支持者,他们在构建集成开发环境方面有着丰富的经验。作为第一个步骤,VisualAge for Java Micro Edition 是作为完全使用 Java 编程语言的集成开发环境的重新实现而开发的。后来,VisualAge for Java Micro Edition 代码被用作 Eclipse 平台的基础。
IBM 已经意识到,仅仅在这个新平台上拥有 IBM 产品不足以获得开发人员社区的广泛采用。集成第三方工具是 Eclipse 平台成功的关键。2001 年,IBM 决定为 Eclipse 平台采用开源许可和操作模型。IBM 和其他八个组织一起建立了 Eclipse 联盟。该联盟的主要运营原则是推动 Eclipse 平台的营销和关系,同时将 Eclipse 源代码的控制权留给开放源代码社区。
2003 年,Eclipse 平台及其快速增长的开源和商业扩展集开始受到开发人员的欢迎。2004 年,Eclipse Foundation,一个拥有自己的专业和独立员工的非营利组织,接管了 Eclipse 平台的全部控制权。Eclipse 现在是领先的 Java 开发环境。由于其独特和可扩展的架构,它也被用作许多其他编程语言的开发环境。
Eclipse 架构
作为一名 Android 开发人员,您不需要与 Eclipse 平台的内部交互。然而,了解它的架构将使您更容易概念化和理解 Eclipse 的一般工作方式。
Eclipse 平台主要是为构建集成开发环境而设计的。它是一个高度可扩展的平台,而不是一套特定任务的定制工具。
Eclipse 平台定义了机制和规则,并通过提供一组定义良好的 API,允许在它们之上构建工具。Eclipse 平台是围绕插件的概念构建的,如图 3-1 所示。
图 3-1。 Eclipse 平台架构概述
插件是 Eclipse 平台的最小单元。它们是结构化的代码束,为平台贡献了一组功能。插件可以单独开发、分发和部署。Eclipse 平台也允许插件具有可扩展性。插件可以通过定义良好的 API 提供一组扩展点,供其他插件扩展其功能。
Eclipse 平台中的每个子系统都基于一组插件。例如,Eclipse 的 Java 开发工具是一组插件,以集成的方式为平台提供 Java 开发功能。Java 开发工具插件也是可扩展的。
在本书中,我们将使用 Android 开发工具插件。这些扩展了现有的 Java 开发工具,以便提供特定于 Android 的开发工具和功能。
Eclipse 平台的核心,也称为 Eclipse 运行时,负责提供插件可以工作和互操作的基础设施。Eclipse 运行时还提供了任何实用服务器,这将使开发人员更容易开发新的插件。在撰写本文时,最新版本是 Eclipse Indigo 3.7.2。
在接下来的小节中,我们将为 Eclipse 设置合适的工作环境。
安装 Java 开发工具包
Eclipse 是一个基于 Java 的应用,它需要一个 Java 虚拟机才能运行。在安装 Eclipse 之前,您需要安装 Java 开发工具包(JDK),而不仅仅是 Java 运行时版本(JRE)。Eclipse 支持多种 JDK 风格,比如 IBM JDK、OpenJDK 和 Oracle JDK(以前称为 Sun JDK)。在本节中,我们将假设您正在安装 Oracle JDK,它是最初的 JDK 实现,支持更广泛的平台。
JDK 的版本也需要与 Dalvik 虚拟机兼容,因为我们将使用 Eclipse 进行 Android 开发。在撰写本文时,Dalvik 虚拟机支持 Java 编译器兼容级别 1.5 和 1.6。尽管新版本的 JDK 可以配置为在这些合规级别上工作,但是从相应的 JDK 版本 JDK 6 开始要容易得多。
使用您最喜欢的网络浏览器,导航至[www.oracle.com/technetwork…](http://www.oracle.com/technetwork/java/javase/downloads/index.html)。如图 3-2 所示,你会看到一个下载选项列表。
图 3-2。 甲骨文网站上的 Java 下载页面
因为我们想下载 JDK 6 而不是 JDK 的最新版本,所以向下滚动到下载页面的 Java SE 6 部分。在撰写本文时,JDK 6 的最新版本是 Update 31。点击 JDK 6 旁边的下载按钮继续。
目前,Oracle JDK 不提供 Mac OS X 平台的安装包,因为安装程序是通过苹果的软件更新分发的。对于所有其他主要平台,Oracle JDK 安装程序都列在该页面上,如图 3-3 所示。每个操作系统的安装过程各不相同。为了使安装过程尽可能顺利,我们将在下面的章节中介绍三种主要的操作系统——Windows、Mac OS X 和 Linux。
图 3-3。 主要操作系统的 Oracle JDK 安装包列表
在 Windows 上安装 JDK
Oracle JDK 是作为 Microsoft Windows 操作系统的可执行安装程序提供的。安装向导将指导您在机器上安装 JDK,如图图 3-4 所示。
图 3-4。 甲骨文 JDK 6 安装向导
安装向导将首先安装 JDK,然后安装 JRE。在安装过程中,向导会询问目标目录以及要安装的组件。您可以在这里继续使用默认值。记下 JDK 部件的安装目录。
安装向导将自动进行必要的系统更改。安装过程完成后,JDK 就可以使用了。
在 Windows 上,安装向导不会自动将 Java 二进制目录添加到系统变量Path中,所以您需要这样做。转到控制面板并选择系统(或从开始菜单中选择运行,然后执行sysdm.cpl)以打开系统属性对话框。切换到高级选项卡,点击环境变量按钮,如图图 3-5 所示。
图 3-5。 系统属性对话框的高级标签
如图 3-6 所示,环境变量对话框分为两部分:上面的是用户的,下面的是系统的。
图 3-6。 环境变量对话框
在“系统变量”窗格中,单击“新建”按钮定义新的环境变量。将变量名设置为JAVA_HOME,将变量值设置为 JDK 安装目录,如图 3-7 中所示。单击确定按钮保存变量。
**图 3-7。**设置 JAVA_HOME 系统环境变量
在系统变量列表中,双击Path变量,将;%JAVA_HOME%\bin追加到变量值,如图图 3-8 所示。
**图 3-8。**设置 Path 系统环境变量
现在,可以从命令提示符轻松访问 JDK。为了验证安装,通过选择启动附件命令提示符打开命令提示符窗口。使用命令提示符,执行javac –version。如果安装成功,你会看到 JDK 版本号,如图图 3-9 所示。
图 3-9。 Windows 命令行提示显示 JDK 版本
在 Mac OS X 上安装 JDK
苹果 Mac OS X 操作系统出厂时已经安装了 JDK。它基于 Oracle JDK,但由 Apple 配置以更好地与 Mac OS X 集成。JDK 的新版本可通过软件更新窗口获得,如图 3-10 所示。确保主机上安装了 JDK 6 或更高版本。
图 3-10。 Mac OS X 软件更新窗口显示 JDK
在 Linux 上安装 JDK
JDK 安装过程因 Linux 发行版而异。由于 Oracle JDK 的许可条款,它不包含在任何 Linux 发行版中。某些发行版附带了一个存根应用,它允许您安装 Oracle JDK,而无需通过 web 下载过程。如本章前面的图 3-3 所示,Oracle 的网站为 Linux 系统提供了两种类型的安装包:
- 名称以-rpm.bin 结尾的安装包包含一组 Red Hat Package Manager (RPM)格式的可安装包。如果您使用的是 Linux 发行版,如 Red Hat Enterprise Linux、Fedora、CentOS、SUSE 或 openSUSE,您可以下载这个安装包。
- 名称以结尾的安装包。bin 包含一个自解压 ZIP 存档文件。这个安装包可以在任何 Linux 发行版上运行,尽管在安装之后需要一些手动的系统配置。
在本节中,我们将假设您运行的是支持 RPM 的 Linux 发行版。
下载 RPM 格式的安装包后,打开一个终端窗口。如图 3-11 所示,首先调用chmod +x jdk-6u31-linux-i586-rpm.bin启用安装程序上的执行位。要开始安装,在命令行上调用sudo ./jdk-6u31-linux-i586-rpm.bin。根据 JDK 的版本,用适当的文件名替换jdk-6u31-linux-i586-rpm.bin。
图 3-11。 在 Linux 上安装 Oracle JDK
**注意:**在 Linux 操作系统上,安装软件包需要超级用户(root)权限。在启动rpm之前,sudo命令将提示输入密码以授予超级用户权限。
安装 Eclipse
安装 Eclipse 是一个相当简单的过程,尽管不同操作系统的安装过程有所不同。在这一节中,我们将再次介绍三种主要的操作系统:Windows、Mac OS X 和 Linux。
使用您最喜欢的 web 浏览器,导航到 Eclipse web 站点,[www.eclipse.org](http://www.eclipse.org),如图图 3-12 所示(在您阅读本文时,该站点可能看起来有所不同)。在本书中,我们将使用最新版本的 Eclipse。在撰写本文时,最新版本是 Eclipse Indigo 3.7.1。
图 3-12。 月食网站
点击下载页面的链接。如图 3-13 所示,您将看到一长串您选择的操作系统(在本例中是 Windows)的可下载 Eclipse 版本。在这个列表中,Eclipse Classic 是您可以下载的最基本的 Eclipse 包。它只包含 Eclipse 平台和 Java 开发工具(JDT)。您当然可以从这个包开始,然后安装您选择的其他插件。
图 3-13。 月食下载页面
这个列表中的其他 Eclipse 包只是包含了一组常用的插件,这些插件是用 Eclipse 平台为主要编程语言预先打包的。你可以在[www.eclipse.org/downloads/c…](http://www.eclipse.org/downloads/compare.php)找到这些包的详细对比。
在 Windows 上安装 Eclipse
Microsoft Windows 的 Eclipse 安装包以 ZIP 存档文件的形式提供。只需右击该文件并从上下文菜单中选择 Extract All… 。
如图 3-14 所示,Windows 会提示输入解压文件的目标目录。在本节中,我们将假设目标目录是 c 盘的根目录C:\。
图 3-14。 解压 Eclipse ZIP 包到它的目的地
当这个过程完成后,Eclipse 将被安装在C:\eclipse目录下,如图图 3-15 所示。您现在可以考虑创建一个 Eclipse 应用的快捷方式。
**图 3-15。**安装后的 Eclipse 文件
在 Mac OS X 上安装 Eclipse
Mac OS X 的 Eclipse 安装包以 GZIP 压缩 TAR 文件的形式提供。下载完成后,Eclipse 将出现在您的Downloads目录中。根据操作系统的版本,您可能需要双击以提取存档文件(如果尚未提取)。
您可以将 Eclipse 从Downloads目录拖放到您的Applications目录,如图图 3-16 所示。
图 3-16。 将 Eclipse 从下载目录移动到应用目录
稍后,您还可以将 Eclipse 添加到您的仪表板中,以便于访问。
在 Linux 上安装 Eclipse
Linux 的 Eclipse 安装包以 GZIP 压缩 TAR 文件的形式提供。打开一个终端窗口,将目录切换到您想要安装 Eclipse 的目的地,如图图 3-17 所示。要提取 Eclipse 文件,在命令行上发出tar zxf eclipse-java-indigo-SR1-linux-gtk.tar.gz,根据 Eclipse 的版本替换文件名。
**图 3-17。**在 Linux 上安装 Eclipse
Eclipse 现在已经可以使用了。您可能会发现创建 Eclipse 应用的快捷方式非常方便。
探索月食
您现在已经准备好开始使用 Eclipse 了。在这一节中,我们将开始探索 Eclipse 及其使用的术语。
工作空间
当你启动 Eclipse 时,会提示你选择工作区目录,如图图 3-18 所示。在 Eclipse 术语中,工作区是存储项目、源代码和 Eclipse 设置的目录。
图 3-18。 Eclipse 启动时的工作区选择对话框
如果使用默认设置,Eclipse 将在用户的主目录下创建一个名为workspace的新目录。在 Workspace Launcher 对话框中,您也可以将这个工作区设置为默认工作区,Eclipse 下次不会再提示它。
工作区对于组织项目非常有用。例如,我同时在两个主要工作区工作:一个是与工作相关的项目,另一个是我的车库项目。然而,许多 Eclipse 开发人员可以很好地使用单个工作区。
在您选择了您的工作空间之后,Eclipse 会用欢迎屏幕来欢迎您,如图 3-19 所示。在这里,您可以找到有用的 Eclipse 资源链接,比如教程和示例。在右上角,单击工作台链接进入主屏幕。
图 3-19。 月食的开场欢迎画面
提示:选择帮助欢迎可以随时回到欢迎界面。
工作台
在 Eclipse 术语中, Workbench 指的是桌面开发环境。这是图 3-20 中所示的 Eclipse 窗口的名称。每个工作台都包含一组透视图,以及它们各自的视图、编辑器、菜单和工具栏项。
图 3-20。 Eclipse 工作台
您可以一次打开多个工作台。要打开一个新工作台,选择窗口 新窗口。
视角
一个透视图定义了视图集和视图在工作台中的布局。每个透视图都旨在帮助完成特定类型的任务:
- Java 透视图:这个透视图结合了开发 Java 应用时常用的视图、菜单和工具栏。
- Debug 透视图:这个透视图包含了与故障排除和调试 Java 应用相关的视图。
Eclipse 为常见任务提供了预定义的透视图。您还可以根据自己的需求修改透视图并定义新的透视图。为此,选择窗口 自定义视角。
您可以使用视角切换器,如图 3-21 所示,或者选择窗口打开视角随时切换视角。
图 3-21。 视角切换器
当您启动一个新的任务时,比如调试一个项目,Eclipse 还会提出改变透视图的建议。这允许根据当前任务在透视图之间自动切换。
编辑
编辑器允许您编辑源代码和资源文件。根据文件类型,Eclipse 支持多种编辑器风格。例如,源代码和 XML 资源文件是用不同的编辑器类型处理的。
大多数文件类型已经用 Eclipse 中正确的编辑器进行了映射。如果 Eclipse 找不到特定文件类型的内部编辑器,它就依赖操作系统来找到外部编辑器。例如,如果您试图打开一个 PNG 格式的图形文件,由于 Eclipse 没有内部图形编辑器,它将依靠操作系统的映射来启动 PNG 文件的默认编辑器。根据活动编辑器的类型,仅显示相关的工具栏和菜单项。
可以同时打开任意数量的编辑器。编辑器在编辑器区域中以单独的选项卡出现,如图图 3-22 所示。在任何给定时间,只能有一个编辑器处于活动状态。
图 3-22。 编辑区
您可以通过右键单击编辑器选项卡,从上下文菜单中选择移动 编辑器,并将分离的编辑器移动到您喜欢的角落位置,将编辑器区域分成多个选项卡组。
观点
视图为项目和编辑器提供了可选的表示,允许在工作台中轻松导航和访问信息。例如,Outline 视图提供了当前编辑的源文件中的方法和变量的列表,允许在编辑器中轻松导航。
Eclipse 提供了许多视图。要打开一个新视图,选择窗口 显示视图。如图 3-23 所示,下拉菜单只显示最常用的视图。如需完整列表,请选择其他… 。
图 3-23。 选择视图
根据透视图的布局,视图可以始终可见、堆叠在选项卡式笔记本中或最小化。视图可能在视图区域中嵌入了自己的工具栏和菜单。
快速查看
快速视图是隐藏视图,可以使用工作台左下角状态栏上的快速视图图标快速打开和关闭,如图图 3-24 所示。
图 3-24。 状态栏上的快速查看图标
点击快速查看图标,弹出下拉菜单,如图图 3-25 所示。
图 3-25。 快速查看下拉菜单
快速视图的工作方式与其他视图类似,但在不使用时不会占用任何屏幕空间。您可以将任何视图拖放到快速视图图标上,使其成为快速视图。
**提示:**或者,与快速视图相同类型的行为可以通过最小化视图来实现。最小化视图以类似工具栏的方式显示。它们比快速视图更容易激活,因为每个最小化视图都可以通过其图标容易地识别,该图标在显示器上总是可见的。
快速查看
快速视图是隐藏的视图,当通过组合键触发时,显示在编辑器区域的顶部。快速视图旨在提供对光标下的元素或当前活动编辑器的信息的轻松访问。快速大纲视图和快速类型层次视图就是例子。
菜单
Eclipse 有不同种类的菜单。其中一些菜单比其他的更难发现。最明显的是工作台顶部的主菜单,如图图 3-26 所示。
图 3-26。 主菜单
视图也可以有自己的菜单,如视图工具栏上朝下的三角形图标所示,如图 3-27 所示。单击此图标显示视图的菜单。
图 3-27。 一个查看菜单
工作台中的子窗口也提供了一个菜单,也称为系统菜单,用于窗口相关的操作。右键单击窗口的标题栏可以激活该菜单,如图图 3-28 所示。
图 3-28。 一个系统菜单
编辑器和大多数视图还为各种任务集提供了上下文菜单。您可以通过右击该视图上的任意位置来访问该菜单,如图图 3-29 所示。
图 3-29。 一个查看菜单
工具栏
工具栏为常见任务提供快捷方式。工作台包含多种类型的工具栏。最重要的是屏幕上方主菜单下方的工具栏,如图图 3-30 所示。该工具栏包含最常用的 Eclipse 任务的图标。
图 3-30。 顶部工具栏
根据所关注的编辑器或视图,工具栏项目可能会在启用和禁用状态之间切换,以反映任务在当前上下文中的可用性。
另一个工具栏出现在工作台的右下角。它包含欢迎屏幕中提到的资源的快捷方式。
视图也可能有工具栏。这些工具栏位于视图内,视图标题的正下方,如图图 3-31 所示。
图 3-31。 大纲视图工具栏
Eclipse 还提供了一个工具栏,可以方便地访问最小化视图,如图 3-32 所示。
图 3-32。 工具栏用于最小化视图
项目
一个项目是 Eclipse 中最大的结构单元,用于分组和组织相关的文件、文件夹、资源、设置和其他工件。例如,Java 项目是一组源文件、资源和设置。
当前工作区中可用的项目通过项目浏览器视图呈现给用户,如图图 3-33 所示。
图 3-33。 项目浏览器视图
项目可以是打开的,也可以是关闭的。当项目处于关闭状态时,它需要较少的内存,在构建期间不会被检查,并且在工作台中不可编辑。关闭项目以缩短活动项目的构建时间始终是一个好的做法。
要创建新项目,从顶部菜单栏中选择文件 新项目。您将看到新项目对话框,其中列出了可用的项目类型,如图 3-34 所示。
图 3-34。 新建项目向导对话框
总结
在本章中,我们首先介绍了在 Microsoft Windows、Mac OS X 和 Linux 系统上为 Eclipse 建立合适的工作环境的步骤。接下来,我们简要回顾了 Eclipse 架构,以便更好地从概念上理解 Eclipse 平台通常是如何工作的。然后我们探索了最常用的用户界面组件。例如工作区、工作台、透视图、编辑器和视图。
这一章为后面的章节奠定了基础。在下一章,我们将探索导航、重构、原型和 Eclipse 平台提供的其他高级特性。
参考文献
本章使用了以下参考资料:
[www.ibm.com/developerwo…](http://www.ibm.com/developerworks/rational/library/nov05/cernosek)Eclipse 简史- 关于 Eclipse 基金会,
[www.eclipse.org/org/](http://www.eclipse.org/org/) - Eclipse 平台技术概述,
[www.eclipse.org/whitepapers…](http://www.eclipse.org/whitepapers/eclipse-overview.pdf) - Eclipse 文档,
[help.eclipse.org/indigo/index.jsp](http://help.eclipse.org/indigo/index.jsp)
四、掌握 Eclipse
在前一章中,我们探索了最常用的 Eclipse 组件。然而,Eclipse 提供了更多的东西。
大型复杂的项目,尤其是当涉及多个开发人员时,可能很快变得难以跟踪和导航。在这一章中,我们将探索高级的 Eclipse 导航特性,例如大纲、类型和调用层次结构以及标记,这些特性可以帮助开发人员轻松地找到代码。
除了导航之外,日常的软件开发还涉及到大量耗时且多余的任务,比如为每个成员字段编写 getters 和 setters,重构代码,以及更新对它的所有引用。在这一章中,我们将探索 Eclipse 提供的用于处理这些劳动密集型任务的大量代码生成器和代码操纵器。使用这些强大的特性使开发人员能够更快地编写代码,因为他们可以将更多的时间投入到实际的应用中。
导航
在一个复杂项目的不同组件之间导航,甚至在一个大的源代码文件中导航,很容易成为一项非常耗时的工作。能够在复杂的项目中轻松导航是任何图形开发环境的最大需求之一。Eclipse 提供了许多高级功能来简化日常开发体验;然而,这些漂亮的功能大部分都隐藏在平台中。在这里,您将学习如何使用一些导航功能,包括工作集、大纲视图、类型层次结构视图、调用层次结构视图、标记和搜索。
工作集
工作集允许对元素进行进一步分组,例如项目、文件、资源和断点,用于显示和操作目的。工作集是 Eclipse 最重要的特性之一,有助于在工作空间中导航。工作集可以用作许多视图的过滤标准,也可以使用构建系统构建工作空间的某个部分。
默认情况下,工作区中的每个元素都被视为窗口工作集的成员。为了定义一个新的工作集,首先在工作台上选择一个元素,比如一个文件,然后右键激活上下文菜单,选择分配工作集…,如图图 4-1 所示。
图 4-1。 从上下文菜单中选择分配工作集
将出现“工作集分配”对话框,显示现有工作集的列表。单击右边的 New 按钮定义一个新的工作集。Eclipse 将显示新建工作集向导,从适用于所选元素的可用工作集类型列表开始。在图 4-2 中的例子中,我们选择了一个 Java 源文件,可用的工作集类型是基于这个源文件填充的。选择工作集的类型,然后单击 Next 进入下一步。
图 4-2。 启动新工作集向导
在下一步中,为这个新的工作集命名。你也可以给它添加其他元素,如图图 4-3 所示。
图 4-3。 命名并添加元素到工作集
当选择完成时,工作集分配对话框将再次显示,这一次新定义的工作集在列表中并被选中,如图图 4-4 所示。
图 4-4。 工作集分配对话框显示新的工作集
这个新的工作集可以在多个地方用作过滤标准。作为一个例子,让我们用新定义的工作集来过滤 Package Explorer 视图的内容。点击 Package Explorer 工具栏上的展开箭头图标,选择视图的下拉菜单,如图图 4-5 所示。选择Select Working Set...设置要使用的工作集。最近使用的工作集被添加到上下文菜单中,以便于访问。
图 4-5。 包浏览器查看菜单
现在,Package Explorer 视图将过滤其内容,只反映所选工作集成员的元素,如图 4-6 所示。
图 4-6。 被工作集过滤的包资源管理器视图
大纲视图
Outline 视图提供了编辑器中当前打开文件的结构视图。它允许快速浏览编辑器的内容。大纲视图工具栏提供了对视图内容进行过滤和排序的选项。
Outline 视图的内容是特定于编辑器的。一些编辑器,例如纯文本文件编辑器,不支持大纲视图。使用 Java 编辑器时,Outline 视图将当前 Java 文件中的类、变量和方法显示为结构化元素,如图图 4-7 所示。
**图 4-7。**Java 文件的概要视图
默认情况下,Outline 视图在 Java 透视图中是可见的。要将 Outline 视图添加到另一个透视图,请选择窗口 显示视图
Outline 。
除了大纲视图,还有一种快速视图,称为快速大纲视图。默认情况下,此视图不可见。要显示它,在 Windows 和 Linux 上按 Ctrl +O,或者在 Mac OS X 上按 Command+O,它就会出现在编辑器区域,如图图 4-8 所示。
**图 4-8。**快速大纲视图显示在编辑器的顶部。
*默认情况下,快速大纲视图显示类字段和方法。第二次按 Ctrl +O会扩展这个列表,覆盖继承的字段、方法和类型。继承的元素以灰色显示,以便于区分。
快速大纲视图还支持自动过滤,允许用户键入元素的首字母来缩小其内容。像其他视图一样,Quick Outline 视图有自己的下拉菜单,允许进一步定制。
类型层次视图
类型层次结构视图是特定于 Java 的,显示所选 Java 对象的子类型和超类型。它允许您快速发现类型层次结构并在类型间导航。
为了启动 Type Hierarchy 视图,您需要首先从 Package Explorer 视图或编辑器中选择一个 Java 对象。选择对象后,可以用三种方式打开类型层次结构视图:
- 按 F4。
- 点击右键,从右键菜单中选择打开类型层次,如图图 4-9 所示。
- 在顶部菜单栏选择窗口
显示视图
类型层次。
图 4-9。 从上下文菜单中选择开放式层级
类型层次视图分为两个窗格,如图图 4-10 所示。顶部窗格显示所选 Java 对象的类型层次结构。底部窗格显示成员列表。
图 4-10。 类型层次视图分为两个窗格。
Type Hierarchy 视图有自己的菜单,可以通过单击右上角的展开箭头来激活它。从此菜单中,您可以通过工作集进一步过滤类型层次结构。
除了菜单之外,类型层次结构视图还有两个工具栏:每个窗格一个。顶部窗格的工具栏提供了在子类型层次、超类型层次和完整类型层次之间切换的图标。底部窗格的工具栏提供了对成员列表进行过滤和排序的图标。
双击该视图中的任何元素都允许您在编辑器区域中自动打开它。
与大纲视图一样,快速查看也是可用的。要打开快速类型层次视图,在 Windows 和 Linux 上按 Ctrl+T,或在 Mac OS X 上按 Command+T。它出现在编辑器区域的顶部,如图 4-11 所示。
图 4-11。 快速类型层次视图编辑器中显示的
调用层次视图
另一个特定于 Java 的视图是 Call Hierarchy 视图,它显示所选 Java 成员对象的调用者和被调用者。它允许您快速发现代码中的调用层次结构,并在调用中导航。
要启动调用层次视图,首先选择一个 Java 成员对象,然后使用以下方法之一:
- 在 Windows 和 Linux 上按 Ctrl+Alt+H,或者在 Mac OS X 上按 Control+Alt+H。
- 右键单击并从上下文菜单中选择打开调用层次,如图图 4-12 所示。
- 在顶部菜单栏中,选择窗口
显示视图
其他 …
调用层次结构。
图 4-12。 从上下文菜单中选择开放呼叫层级
“呼叫层次结构”视图也有自己的菜单。与其他视图一样,您可以通过单击展开箭头来激活此菜单。此下拉菜单允许您在呼叫者和被呼叫者层次结构之间更改呼叫层次结构模式。它还提供了筛选功能,例如在探索字段访问调用层次结构时按字段访问类型进行筛选。
如图 4-13 所示,调用层次结构以树形方式显示在左侧。视图的右侧用于显示行号和被调用的函数。当您单击树项目左侧的加号图标时,呼叫层次结构发现会继续前进一步。当到达调用层次结构中的最后一个方法时,加号图标会消失。
图 4-13。 调用层次视图
标记
标记是可以与工作台资源相关联的元数据。标记显示在编辑器区域左边界的标记栏上。Eclipse 支持不同的标记类型。在这一部分,我们将回顾三种标记风格:书签、问题和任务。
书签视图
书签提供了一种方法来标记经常使用的资源,以便于以后访问。当在一个复杂的项目中工作时,代码的某些部分,比如主 API,可能是书签的很好的候选。您可以为文件中的特定行或整个资源添加书签。
书签视图以表格形式提供了这些书签的列表,如图图 4-14 所示。
图 4-14。 书签视图
如果书签视图不可见,可以通过选择窗口 显示视图
**其他… **
书签将其添加到当前透视图中。
要添加新的书签,在编辑器区域的标记栏上单击鼠标右键,从快捷菜单中选择添加书签… ,如图图 4-15 所示。蓝色书签图标将出现在选定行的标记栏中,表示该行已被书签标记。然后,您可以使用书签视图管理书签。
图 4-15。 添加新书签
问题视图
Problems 视图为各种 Eclipse 组件记录问题、错误和警告提供了一个中心位置。问题视图以表格的形式呈现这些信息,如图 4-16 所示。
图 4-16。 问题视图显示存在的问题
例如,在编译期间,任何错误首先通过一个标记与相应的资源相关联,然后通过 Problems 视图报告给用户。双击错误消息可以快速跳转到相应的资源。
默认情况下,Problems 视图显示所有问题,并根据它们的严重性对它们进行分组。使用“视图”菜单(通过窗口的展开箭头访问),您可以过滤列表并更改分组和排序。问题解决后,它们会自动从 problems 视图中删除。
通过快速修复功能,问题视图还提供了修复报告问题的帮助。要启动快速修复,在 Windows 和 Linux 上按 Ctrl+1,或在 Mac OS X 上按 Command+1,或从所选问题项的上下文菜单中选择快速修复。快速修复提供了一套解决问题的建议,如图图 4-17 所示。
图 4-17。 快速修复提供修复问题的建议。
任务视图
Tasks 视图允许您将任务与工作台资源相关联。例如,需要解决的缺失代码段或已知错误可以通过将任务与相关资源相关联来表达。任务视图以表格的形式显示这些信息,如图 4-18 所示。
图 4-18。 任务视图
使用 Tasks 视图的下拉菜单,您可以组织这个列表。例如,您可以根据任务优先级对列表重新排序,或者过滤列表以仅显示特定类型的任务。
与大多数标记一样,可以通过右键单击相应行上的标记栏来定义新任务。您也可以在资源中使用某些关键字,如图图 4-19 所示。开发人员更经常使用后一种方法。
图 4-19。 任务由待办事项关键字自动定义
以下是用于自动定义任务的最常用关键字:
TODO:该关键字用于记录任何需要以后实现的缺失代码部分。开发人员大多使用TODO来记录他们当前推迟并计划以后解决的任务。FIXME:这个关键字主要用于记录代码中任何已知的需要解决的 bug。
向资源中添加任务时,并不局限于这些关键字。其他关键字可以通过任务标签首选项对话框定义,如图图 4-20 所示。要打开这个对话框,在 Windows 和 Linux 上选择窗口首选项,或者在 Mac OS X 上选择 Eclipse
首选项,导航到 Java,然后是编译器,然后是任务标签。
图 4-20。 任务标签首选项对话框
搜索
有效的搜索是在开发环境中轻松导航的关键。Eclipse 提供了多层搜索功能,这些功能专门针对某些用例进行了优化。
Eclipse 提供的最基本的搜索特性,也称为文件搜索,是在工作台中搜索文本字符串。要打开搜索对话框,在 Windows 和 Linux 上按 Ctrl+H,或者在 Mac OS X 上按 Control+H,或者从顶部菜单中选择搜索 搜索… 。
如图 4-21 所示,搜索对话框为搜索提供了广泛的定制。虽然这是一个非常强大的功能,但它仅针对在通用文件中搜索文本进行了优化。不推荐以这种方式搜索 Java 资源,因为已经有了专门针对 Java 资源的最佳解决方案。
图 4-21。 使用搜索对话框进行文件搜索
对于 Java 资源,Java 搜索比文件搜索快得多,因为它依赖于现有的代码索引。您可以通过从顶部菜单中选择搜索 Java … 或单击搜索对话框中的 Java 搜索选项卡来启动 Java 搜索。如图 4-22 所示,Java 搜索选项卡提供了特定于 Java 的附加参数,您可以使用这些参数来进一步定制搜索。
图 4-22。??【Java 搜索】选项卡的搜索对话框
搜索结果通过搜索视图呈现,如图图 4-23 所示。搜索视图下拉菜单和工具栏提供了进一步的过滤功能,以根据用户偏好组织搜索结果。
图 4-23。 搜索视图
搜索菜单也提供了一些样板搜索,如图图 4-24 所示。当前选择的 Java 资源可用于快速开始新的参考、减速和实施者搜索。
图 4-24。??【样板文件搜索】??
能够在项目中轻松导航无疑加快了编码过程,但这还不够。开发者在开发应用的同时,仍然需要编写相当数量的代码。在下一节中,我们将探索 Eclipse 为快速编码提供的高级特性。
快速编码
在大多数软件项目中,开发人员的大部分时间并不用于开发实际的应用逻辑。开发人员花费大量时间处理简单但劳动密集型的编码任务,例如实现 getters 和 setters,或者在进行代码重构后更新源代码中的所有引用。Eclipse 提供了一组高级特性,如模板和代码生成器,以自动化部分编码,并减少开发人员需要生成的代码量。在这一节中,我们将回顾一些方便的 Eclipse 特性。
模板
在开发任何类型的应用时,我们每天都在使用许多编码模式和代码结构。大多数时候,我们发现自己在复制和粘贴代码段,并试图通过操作它们的参数名来适应它们的新家。例如,日志记录是每个项目的必备条件之一。在开发应用时,开发人员通常会在很多地方复制日志记录器的启动代码。
大多数文本编辑器中的复制粘贴功能无疑使任务变得简单;但是,它要求您能够立即访问原始代码段,以便首先复制它们。因此,开发人员可能会花费大量时间搜索以前的项目,以便提取那些宝贵的代码段。
通过对代码模板的支持,Eclipse 为这个问题提供了一个更加优雅的解决方案。代码模板允许您在 Eclipse 中存储常用的代码模式和代码片段。Eclipse 处理这些模板的存储和索引,并使它们易于使用。
Eclipse 支持多种代码模板类型。用户可以定义自己的模板,也可以使用插件附带的预定义模板(可以由用户自定义)。
为了更好地了解 Eclipse 中模板支持的程度,在顶部菜单栏中,选择 Windows 和 Linux 上的窗口 首选项,或者在 Mac OS X 上的 Eclipse 首选项来启动 Eclipse 首选项对话框。开始输入模板来过滤广泛的首选项列表,只过滤模板,如图图 4-25 所示。
图 4-25。 为模板过滤的 Eclipse 首选项对话框
您可能已经注意到,在首选项对话框的 Java 部分列出了两组模板:代码样式下的代码模板和编辑器下的模板。我们将在本节中研究这两种类型。
代码模板
代码模板主要在自动代码生成过程中使用。最基本的代码模板用于放置在新文件顶部的注释行。在许多公司,你会被要求在你开发的每个源文件的顶部包含一个版权声明和一个许可证。要使用代码模板轻松实现这一点,在 Windows 和 Linux 上选择窗口 首选项,或者在 Mac OS X 上选择 Eclipse
首选项,导航到 Java,然后是代码样式,然后是代码模板。您将看到一个可用代码模板的列表,如图图 4-26 所示。
图 4-26。 Java 代码模板列表
代码模板以树状方式呈现在两个主要组下:注释和代码。单击 Comments 组左侧的三角形图标,展开可用注释代码模板的列表。我们将为这个示例修改的代码模板是一个名为 Files 的模板。从列表中选择该模板后,对话框的底部窗格将立即显示所选代码模板的当前模式,如图 4-27 所示。
图 4-27。 文件注释代码模板
如您所见,目前它不包含任何文本,而只包含注释装饰。要对其进行修改,请单击右侧的编辑按钮。这将弹出编辑模板对话框,如图图 4-28 所示。
图 4-28。 编辑模板对话框
您现在可以修改文件注释,如下所示:
/** * Copyright © 2012 Apress Media LLC. All Rights Reserved. */
代码模板都是关于可重用性的,开发人员喜欢使它们尽可能通用,以避免需要保持它们最新。在我们的例子中,模板中有一个硬编码的年份 2012。最好让这个版权行反映当前年份,而不是总是显示 2012 年。这可以通过添加一个变量来实现,利用 Eclipse 的模板支持很容易做到这一点。要用正确的变量替换 2012,请单击模式文本区域下方的插入变量…按钮。您将看到一个可用变量列表,这些变量可以在模板中使用,如图图 4-29 所示。
图 4-29。 代码模板可用的变量
对于本例,从变量列表中选择year来替换 2012。我们的文件注释现在将如下所示:
/** * Copyright © ${year} Apress Media LLC. All Rights Reserved. */
从现在开始,你添加到你的项目中的任何新的 Java 文件都将在文件注释中生成版权行,如图图 4-30 所示。
图 4-30。 新的 Java 文件用其文件中的版权注释
编辑器模板
因为代码模板只能通过代码生成器使用,所以 Eclipse 不允许用户向列表中添加新模板。然而,第二种模板类型,编辑器模板,主要供用户定义新模板,并在开发应用时使用它们。如前所述打开 Eclipse 首选项对话框,导航到 Java,然后是编辑器,然后是模板,如图 4-31 所示。
图 4-31。 编辑模板
要定义新的编辑器模板,请单击右侧的新建按钮。新建模板对话框将被启动,如图图 4-32 所示。
图 4-32。 新建模板对话框用于编辑模板
对于代码模板,该对话框比编辑模板对话框多两个字段:一个用于新模板的名称,另一个用于上下文。该名称主要用于在编辑器中使用该模板时引用它,它更像是一个关键字。Eclipse 使用上下文根据当前上下文过滤模板,以便只提供适用的模板。
例如,我们将为记录器启动代码定义一个新的编辑器模板。将新模板命名为 Logger ,并选择 Java 上下文。我们首先将一个现有的日志初始化行复制到模板的模式编辑器中。
private static final Logger logger = Logger.getLogger(Author.class.getName());
为了使这个编辑器模板更加通用,对类文件的引用应该被转换成一个变量。单击 Insert Variable…按钮,您将看到一个比代码模板可用的变量更大的变量列表。从变量列表中选择enclosing_type来替换模板中的Author。新模板将如下所示:
private static final Logger logger = Logger.getLogger(${enclosing_type}.class.getName());
Logger类是在java.util.logging包中定义的,它不是自动导入的 Java 包集的一部分。为了使编辑器模板更加通用,让我们指示 Eclipse 在将模板插入代码时导入Logger类。为此,从变量列表中选择import,并添加参数java.util.logging.Logging。如图 4-33 所示,修改后的模板如下图所示:
private static final Logger logger = Logger.getLogger(${enclosing_type}.class.getName()); ${:import(java.util.logging.Logger)}
图 4-33。 对话框中编辑模板完全定义
编辑器模板现在可以使用了。要将其插入到代码中,开始键入 logger ,然后在 Windows 和 Linux 上按 Ctrl+空格键,或者在 Mac OS X 上按 Control+空格键来启动内容辅助功能(下一节讨论),如图图 4-34 所示。
图 4-34。 内容辅助提示编辑模板
您将看到一个建议列表,包括我们在本例中定义的编辑器模板。从列表中选择logging,将测井初始化代码模板插入编辑器,如图 4-35 中的所示。
图 4-35。 编辑模板插入代码
内容辅助
如果您需要记住每一个类型和方法的名称,那么使用第三方 API 或处理复杂的项目将会非常困难。大多数时候,开发人员确实记得一个方法的存在,但不记得它的完整签名。在这些时刻,Eclipse 的内容辅助特性变得非常方便。
触发内容辅助最简单的方法是通过点字符。例如,开始输入 System.out. 并等待一秒钟。点字符调出内容帮助,显示一个建议列表来完成当前代码行,如图图 4-36 所示。
图 4-36。 内容协助制作建议完成行
内容辅助通过使用光标左侧的第一个单词来准备建议列表,该列表可能很长。为了缩小建议的范围,请继续键入更多字符,内容助手将相应地过滤列表。在我们的例子中,输入 p ,列表将只包含以字母p开头的建议,如图 4-37 中的所示。
图 4-37。 内容协助建议进一步筛选
您还可以浏览建议列表。当您选择一个建议时,建议的代码将自动插入该行。如果这些建议都不适用,您可以按 Esc 键关闭内容帮助列表。
虽然点字符会自动触发内容辅助,但在 Windows 和 Linux 上也可以通过使用组合键 Ctrl+空格键或在 Mac OS X 上使用 Control+空格键随时手动启动内容辅助。Content Assist 是一个非常强大和方便的工具,用于简化日常 Eclipse 开发。
代码生成器
为了便于编码,Eclipse 提供了一组代码生成器,可以为常用的编码模式自动生成代码。这些代码生成器选项可通过顶部菜单栏上的源代码菜单获得:
- **覆盖/实现方法:**提供了超类和实现接口的方法列表,用于覆盖和实现。
- **生成 getter 和 setter:**为选中的字段生成 getter 和 setter 方法。
- **生成委托方法:**为当前类型的字段生成方法委托。
- 生成 toString(): 使用所选字段和方法的内容生成
toString()方法。 - 生成 hashCode()和 equals(): 根据选择的字段生成
hashCode()和equals()方法。 - **使用字段生成构造函数:**添加一个初始化所选字段的构造函数。
- **从超类生成构造函数:**添加一个在当前类的超类中定义的构造函数。
Eclipse 代码生成器最好的例子是 getter 和 setter 生成器。在面向对象编程中,mutator 方法是用于控制变量变化的方法。像 getters 和 setters 这样的方法就是 mutator 方法的例子。类变量总是被声明为私有的,而 getter 和 setter 方法是被定义来操作这些字段的公共方法。在大多数开发项目中,getter 和 setter 方法占据了源代码的很大一部分,开发人员可能会花费相当多的时间为这些简单但耗时的方法编写代码。
Eclipse 的 getter 和 setter 代码生成器为这个问题提供了一个优雅的解决方案。定义完类中的字段后,从顶部菜单栏中选择SourceGenerate Getters and Setters…,弹出生成 Getters and Setters 对话框,如图图 4-38 所示。
图 4-38。 生成 Getters 和 Setters 对话框
“生成 Getters 和 Setters”对话框以树状方式提供了成员字段列表。每个成员字段左侧的复选框允许您为 getter 和 setter 生成标记一个字段。复选框的左边是一个三角形图标,用于展开选择,进一步显示将要生成的各个方法。默认情况下,getter 和 setter 都会生成,除非你指定生成哪个 mutator 方法,如图 4-39 中的例子所示。
图 4-39。 选择要生成的个别方法
“生成 Getters 和 Setters”对话框还提供了额外的可配置参数来指定自动生成的方法的插入点、排序和访问修饰符。
图 4-40 显示了自动生成的 getters 和 setters。getters 和 setters 的格式基于 Java 代码模板,可以通过 Eclipse Preferences 对话框进行定制,方法是导航到 Java,然后是代码样式,然后是代码模板,如本章前面所讨论的。
图 4-40。 自动生成 getters 和 setter
重构
重构是指在不改变代码功能的情况下,对代码进行转换的过程。重构经常在开发周期中进行,以便根据新的需求改进设计和代码的效率。
重命名是最简单的重构操作。然而,在重命名之后,确实需要大量的手工工作来调整代码。为了能够使用代码,每个对重命名对象的引用都需要修改。现有的搜索和替换功能不适用于此操作,因为它可能会导致代码的其他部分发生意外更改。由于需要大量的手动操作,这个过程也容易出现用户错误。
Eclipse 为这个问题提供了一个更加优雅的解决方案。要重命名 Java 对象,在 Windows 和 Linux 上按 Alt+Shift+R,或者在 Mac OS X 上按 Alt+Command+R,或者从顶部菜单栏中选择重构 重命名。如图图 4-41 所示,Eclipse 将允许您重命名对象,并且它会相应地自动重构应用代码。
图 4-41。 重命名一个 Java 对象
重命名并不是 Eclipse 支持的唯一重构操作,你可以在Refactor菜单中看到,如图 4-42 中的所示。
**图 4-42。**Eclipse 支持的重构操作
此菜单提供了对许多重构操作的简单访问:
- **重命名:**重命名选中的 Java 对象,并修正所有引用。
- **移动:**移动选中的 Java 对象,修正所有引用。
- **更改方法签名:**更改参数名称和类型,并相应地更新所有引用。
- **提取方法:**将当前选中的代码段提取为一个新的模块,并用对新定义方法的引用替换选中的代码段。
- **提取局部变量:**提取当前选择的变量作为新的局部变量,并用新定义的局部变量的引用替换选择。
- **提取常量:**提取当前选择的表达式作为新的常量,并用新常量的引用替换选择。
- **内联:**内联局部变量、方法或常量。
- **将匿名类转换为嵌套类:**将选定的匿名类转换为成员类。
- **将类型移动到新文件:**将所选类型移动到它自己的 Java 源文件,并相应地更新引用。
- **将局部变量转换为字段:**将选定的局部变量转换为字段,并相应地更新引用和初始化。
- **提取超类:**从一组兄弟中提取一个超类,并将兄弟更改为新定义的超类的直接子类。
- **提取接口:**用一组选中的方法提取一个接口,让选中的类实现这个新接口。
- **尽可能使用超类型:**尽可能用超类型替换一个类型的出现。
- 下推:在超类和类之间移动方法。
- Pull Up: 在类和它的超类之间移动方法。
- **提取类:**提取一组字段作为新的类,并用新的类替换对这些字段的引用。
- **引入参数对象:**用一个新类替换一组参数,更新方法的所有调用方,用参数传递这个新类的一个实例。
- **引入间接方法:**生成一个静态间接方法,委托给选中的方法。
- **引入工厂:**为所选类型生成新的工厂方法。
- **引入参数:**用对新方法参数的引用替换表达式,并更新所有调用方。
- **封装字段:**用 getters 和 setters 替换所有对字段的引用。
- **泛化声明的类型:**允许用户选择引用当前类型的超类型,如果引用可以安全地更改为该超类型的话。
- **推断泛型类型参数:**尽可能用参数化类型替换泛型类型的原始类型。
一些重构任务可能涉及这些重构操作的组合。Eclipse 保存了重构任务的历史,允许您撤销特定的重构步骤。要查看重构历史,从顶部菜单栏中选择重构 历史。
通过选择重构 创建脚本… ,还可以将选定的重构任务保存到脚本文件中以备后用。然后,您可以选择重构
应用脚本… 来再次应用这些重构步骤。
剪贴簿
剪贴簿功能允许用户轻松地试验代码片段,而无需处理编写完整 Java 代码的额外负担。剪贴簿就像一个代码解释器。它允许您只键入一段代码进行实验,然后它可以快速执行代码并显示结果。在剪贴簿页面中,您可以使用项目中定义的类以及 Java 系统类。
要启动剪贴簿页面,从顶部菜单栏选择文件 新建
其他…
Java
Java 运行/调试
剪贴簿页面。一个空白的剪贴簿页面将被添加到编辑区,如图图 4-43 所示。
图 4-43。【scrapbook page】
开始键入以下示例表达式:
java.util.Date date = new java.util.Date(); date
剪贴簿提供了以下三种执行类型:
- 显示:评估表达式,并将其值直接打印到剪贴簿页面。
- Inspect :对表达式求值,并显示一个检查窗口,显示对象提供的所有信息。
- 执行:将表达式作为普通 Java 代码进行求值。
为了使用这些执行类型,首先突出显示表达式。然后在 Windows 和 Linux 上按 Ctrl + Shift + D,或者在 Mac OS X 上按 Shift+Command+D,或者从顶部菜单中选择运行显示,开始显示执行。表达式将被求值,其值将显示在剪贴簿页面中,如图图 4-44 所示。
图 4-44。 剪贴簿展示
保持表达式高亮显示,在 Windows 和 Linux 上按 Ctrl + Shift + I,或者在 Mac OS X 上按 Shift+Command+I,或者从顶部菜单中选择运行检查,开始检查执行。表达式将被计算,检查窗口将出现在剪贴簿页面的顶部,如图图 4-45 所示。
图 4-45。 剪贴簿查看功能
将表达式更改如下:
java.util.Date date = new java.util.Date(); System.out.println(date.toString());
高亮显示表达式,在 Windows 和 Linux 上按 Ctrl + U,在 Mac OS X 上按 Command+U,或者选择运行 执行来执行。表达式将像普通 Java 代码一样执行,输出将显示在控制台视图中,如图图 4-46 所示。
图 4-46。 剪贴簿执行功能
总结
在这一章中,我们讨论了可以加速开发周期的强大的 Eclipse 特性。本章一开始,我们深入探讨了 Eclipse 提供的高级导航特性,包括视图和不同类型的标记,以便轻松定位代码部分。然后,我们研究了 Eclipse 的快速编码特性。这些包括代码和编辑器模板,可以用来维护代码的一致性,以及 Eclipse 提供的代码生成器和重构功能,用来处理耗时的开发任务。后面的章节将演示这些功能的实际应用。*
五、Eclipse 的 Android 开发工具
在前四章中,我们已经非常详细地研究了 Android 框架和 Eclipse 集成开发环境。在这一章中,我们将使用 Eclipse 的 Android 开发工具(ADT)插件把这两个世界粘在一起。我们将从安装 ADT 和 Android 软件开发工具包(SDK)开始我们的旅程。然后我们将开始探索它们提供的视图和工具。在下一章,在开发我们的第一个 Android 项目时,我们将开始把这些视图和工具付诸实践。
准备 Eclipse
尽管 Eclipse 附带了用于 Java 开发的工具,但是为了使用 Eclipse 开发 Android 应用,还需要特定于 Android 的平台 API 和应用打包工具。
安装 Android 开发工具
正如在第三章中所解释的,Eclipse 平台是围绕插件的概念构建的。ADT 是一组用于 Eclipse 平台上 Android 应用开发的插件。
ADT 扩展了 Eclipse 集成开发环境的功能,允许应用开发人员执行以下任务:
- 快速建立新的 Android 项目
- 可视化设计高级用户界面
- 访问和使用 Android 框架组件
- 调试、单元测试和发布 Android 应用
ADT 是在开源 Apache 许可下提供的免费软件。关于最新 ADT 版本和最新安装步骤的更多信息可以在 Eclipse 的 ADT 插件页面([developer.android.com/sdk/eclipse-adt.html](http://developer.android.com/sdk/eclipse-adt.html))上找到。
我们将使用 Eclipse 的安装新软件向导来安装 ADT。从顶部菜单栏选择帮助安装新软件启动向导,如图图 5-1 所示。
图 5-1。 选择安装新软件
向导将启动并显示可用插件的列表。由于 ADT 不是官方 Eclipse 软件存储库的一部分,您需要首先添加 Android 的 Eclipse 软件存储库作为一个新的软件站点。为此,点击添加按钮,如图图 5-2 所示。
图 5-2。 开始添加新软件
将出现“添加存储库”对话框。在“名称”字段中,输入引用该存储库的唯一名称。在位置字段中,输入 Android 的 Eclipse 软件仓库的 URL:[dl-ssl.google.com/android/eclipse/](https://dl-ssl.google.com/android/eclipse/),如图图 5-3 所示。
图 5-3。 添加存储库对话框完成,带有 ADT 信息
添加新软件站点后,安装新软件向导会显示可用的 ADT 插件列表,如图图 5-4 所示。这些插件中的每一个对 Android 应用开发都至关重要,强烈建议您安装所有的插件。(我们将在本章后面的“探索 ADT”一节中讨论这些插件。)单击 Select All 按钮选择所有 ADT 插件,然后单击 Next 按钮进入下一步。
图 5-4。 安装 ADT 开发工具
Eclipse 将检查所选插件的列表,将所有依赖项添加到列表中,然后提交最终的下载列表以供审查。单击“下一步”按钮进入下一步。
ADT 还包含一组具有不同许可条款的其他第三方组件。在安装过程中,Eclipse 会显示每个软件许可,并要求用户接受许可协议的条款,以便继续安装。查看许可协议,选择接受其条款,然后单击完成按钮开始安装过程。Eclipse 将报告安装的进度,如图 5-5 中的所示。
图 5-5 。ADT 安装进度
ADT 插件来自未签名的 JAR 文件,这可能会触发安全警告,如图图 5-6 所示。单击“确定”按钮消除警告并继续安装。当 ADT 插件安装完成后,Eclipse 将需要重启以应用更改。
图 5-6。 由于未签名的 ADT 插件导致的安全警告
安装 Android SDK
ADT 是一组插件,将 Android 开发工具融入 Eclipse 集成开发环境;它不是 Android SDK 的替代品。
Android SDK 是一套全面的开发工具,包括 Android 平台 Java 库、应用打包程序、调试器、仿真器和大量文档。为了使用 ADT 做任何有用的事情,需要在机器上安装 Android SDK。重新启动后,ADT 会用 SDK 配置向导欢迎您,如图图 5-7 所示。
图 5-7。 Android SDK 配置向导
SDK 配置向导允许您将 ADT 指向现有的 Android SDK(如果之前已安装),或者指示 ADT 为您下载并安装 Android SDK。单击“完成”按钮继续 SDK 配置过程。SDK 配置向导将指导您完成将 Android SDK 安装到您的主机上的过程。请注意 Android SDK 的安装目录,因为您将需要它来更新系统变量Path,如下所述。
更新路径
在 Android SDK 安装期间,Path变量不会自动添加到系统中。ADT 不要求在系统变量Path中包含 SDK 二进制文件,但是为了使这些文件易于访问,强烈建议您添加它们。
更新 Microsoft Windows 上的路径
正如我们在第三章中所做的那样,将 JDK 添加到系统Path变量中,打开控制面板并选择系统以启动系统属性对话框。切换到高级选项卡,并单击环境变量按钮。从系统变量窗格中选择变量Path,并点击编辑按钮。将;<*sdk-dir*>\tools;<*sdk-dir*>\platform-tools追加到Path变量值,将<*sdk-dir*>替换为 Android SDK 安装目录,如图图 5-8 所示。单击确定按钮保存更改。
图 5-8。 添加 Android SDK 目录到 Windows 系统路径变量
更新 Mac OS X 和 Linux 上的路径
要将 Android SDK 二进制目录附加到您的系统Path变量中,请在 Mac OS X 上打开一个终端窗口,或者在 Linux 上打开一个 shell 窗口,然后输入以下命令(将<*sdk-dir*>替换为 Android SDK 安装目录):
export PATH=$PATH:<sdk-dir>/tools:<sdk-dir>/platform-tools >> ~/.bashrc
图 5-9 显示了终端窗口中的命令。
图 5-9。 添加 Android SDK 目录到 Mac OS X 系统路径变量
安装平台 API
默认情况下,SDK 配置向导将安装最新版本的 Android APIs 但是,您可以随时使用 Android SDK 管理器安装不同版本的 Android APIs。要启动 SDK 管理器,从顶部菜单栏选择窗口 ** Android SDK 管理器**,如图图 5-10 所示。
图 5-10。 打开 Android SDK 管理器
如图 5-11 所示,Android SDK 管理器显示了一个 Android SDK 组件列表,如工具、API 和插件,可以下载。该列表以树状方式构建。列表中的第一项是工具。这些是 Android SDK 的通用和必需组件。强烈建议您使用最新版本的工具组件。列表中的其他组件按照 Android 版本和 API 级别分组,它们是可选的。
图 5-11。 使用 Android SDK 管理器
单击这些 Android 版本旁边的加号,查看可用组件列表。根据所选的版本,您将看到一个核心组件列表,以及可用的附加组件。尽管此列表会因所选版本而异,但以下是最常见的组件:
- SDK 平台:这是为所选 Android 版本开发应用必须安装的核心组件。Android SDK 管理器将 SDK 平台安装在
<*sdk-dir*>/platforms/android-<*api-level*>目录下,仿真器系统镜像安装在<*sdk-dir*>/system-img/android-<*api-level*>目录子文件夹下。Android SDK 基于您的应用的目标平台为您的应用提供这些资源。Android 开发人员不希望直接与这些文件交互。 - Android SDK文档:这提供了在
[developer.android.com](http://developer.android.com)网站上可获得的 Android 资源的离线版本。如果选择安装,Android SDK 管理器会将文档安装在<*sdk-dir*>/docs目录下。您可以通过将 web 浏览器指向file:///<*sdk-dir*>/docs/index.html来访问文档的主页。为了快速和脱机访问,您可以考虑安装此组件。 - SDK的样例:这些样例应用演示了 Android APIs 的使用。Android SDK 管理器将示例应用安装在
<*sdk-dir*>/samples/android-<*api-level*>目录子文件夹下。强烈建议您安装示例应用,因为它们是了解 Android API 特性并进行实验的绝佳资源。 - 【Android SDK 的源代码:这些提供了 Android 框架的源代码。Android SDK 管理器将源代码安装在
<*sdk-dir*>/sources/android-<*api-level*>目录下。在对 Android 应用进行故障诊断时,这些源文件非常方便,因为它们允许开发人员深入 Android 框架,以快速确定许多模糊问题的根本原因。 - 谷歌公司的谷歌 API:这不是核心组件的一部分,是作为一个附加组件发布的。Android SDK 管理器将这些 API 安装在
<*sdk-dir*>/add-ons目录下。这个插件允许你使用谷歌的 API 和服务开发应用,比如谷歌地图。它还附带了一个扩展的模拟器系统映像,其中包含默认模拟器系统映像中没有的 Google 系统组件。
你可能还记得第一章的内容,Android 市场高度分散,Android 的新版本传播非常缓慢。为了覆盖更大的用户群,在最广泛支持的 API 级别之上构建应用是一种常见的做法。在撰写本文时,使用最广泛的 Android 版本是 2.3.3,它支持 API 级。
要安装 API level 10,单击列表中 Android 2.3.3 (API 10)旁边的加号将其展开。选择 SDK 平台组件和您想要安装的任何其他组件,然后单击“安装软件包”按钮。Android SDK 管理器将要求您接受所选组件的许可条款。选择 Accept All,然后单击 Install 按钮继续安装软件包。
某些组件可能要求您在制造商的网站上注册并提供下载凭据。在这些情况下,Android SDK 管理器会显示相应的对话框来指导您完成整个过程。Android SDK 管理器将选定的组件安装在 SDK 目录下相应的目录中。SDK 目录的位置显示在 Android SDK 管理器对话框的顶部,标记为“SDK 路径”(参见图 5-11 )。
探索 ADT
ADT 提供了从 Eclipse 内部对 Android SDK 组件的访问。在本节中,我们将探索这些组件:Android 虚拟设备管理器、Dalvik 调试监视器、Traceview、层次结构查看器和 Android Lint。
安卓虚拟设备管理
Android SDK 带有一个全功能模拟器,一个在你的机器上运行的虚拟设备。Android 模拟器允许您在本地机器上开发和测试 Android 应用,而无需使用物理设备。
Android 模拟器运行完整的 Android 系统堆栈,包括 Linux 内核。这是一个完全虚拟化的设备,可以模仿真实设备的所有硬件和软件功能。用户可以使用 Android 虚拟设备(AVD)管理器定制这些功能。要启动 AVD 管理器,从顶部菜单栏选择窗口 ** AVD 管理器**,如图图 5-12 所示。
**图 5-12。**启动 AVD 管理器
AVD 管理器允许您定义多个虚拟设备配置。AVD 管理器对话框列出了之前定义的配置,如图图 5-13 所示。
**图 5-13。**AVD 管理器对话框中列出的现有虚拟设备
配置新的虚拟设备
要定义新的虚拟机实例,请单击 AVD 管理器对话框右侧的新建按钮。这将打开创建新的 Android 虚拟设备(AVD)对话框,如图图 5-14 所示。
图 5-14。 配置新的虚拟设备
该对话框包含以下字段:
- 名称:这是新虚拟设备配置的唯一名称。
- 目标:这是虚拟设备的 Android 版本号和 API 级别。下拉列表仅显示使用 Android SDK 管理器安装的 Android 版本。如果首选版本不可用,您将需要使用 Android SDK 管理器安装它。
- CPU/ABI :这是新虚拟设备的机器架构。目前只支持 ARM 机器架构。
- SD 卡:这或者是 SD 卡的大小,或者是现有磁盘镜像的位置。如果此虚拟设备配置不需要 SD 卡,此字段可以为空。
- 快照:这是为了允许在会话之间保持虚拟设备的状态。
- 皮肤:这是虚拟设备的皮肤和屏幕尺寸。下拉列表是根据已安装的版本和附件填充的。也可以定义自定义屏幕尺寸。
- 硬件:这是虚拟设备支持的硬件特性列表,比如 GPS 和摄像头。您可以通过点击新建按钮并选择单个项目来启用功能,如图图 5-15 所示。
图 5-15。 添加硬件特性
在接下来的章节中,我们将使用 Android 模拟器。建议使用以下虚拟机配置来执行这些章节中的示例代码片段:
- Name 参数应该设置为 Android_10 。
- 目标参数应该设置为Android 2 . 3 . 3–API Level 10。如果此目标在下拉列表中不可用,请使用 Android SDK 管理器下载它。
- SD 卡的大小至少应设置为 128MB。
其他设置可以保持不变。
设置参数后,单击创建 AVD 按钮存储虚拟设备配置。
启动模拟器
虚拟设备配置可用于随时启动模拟器实例。选择虚拟设备配置后,单击 Start 按钮使用所选的虚拟设备配置启动一个新的模拟器实例。在启动仿真器之前,AVD 管理器显示启动选项对话框,如图图 5-16 所示。
图 5-16。 模拟器启动选项对话框
模拟器屏幕可能看起来太大,这取决于您的屏幕大小和分辨率。使用“启动选项”对话框,选中“按实际大小缩放显示”框,并设置监视器大小和分辨率来缩放模拟器。
“启动选项”对话框还允许您擦除用户数据,以将模拟器恢复到初始状态。如果在配置期间将快照设置为 Enabled,则 Launch Options 对话框还允许您从现有快照启动模拟器,并决定模拟器状态是应该存储在快照中还是在终止时丢弃。
点击启动选项对话框中的启动按钮,启动模拟器,如图图 5-17 所示。Android 模拟器可能需要一些时间来启动,这取决于您的主机平台的 CPU 能力。
图 5-17。 仿真器实例
控制仿真器
模拟器窗口的左窗格显示模拟器显示,右窗格包含软键。可以使用鼠标模拟触摸事件。此外,在表 5-1 中列出的组合键可用于控制硬件功能。
Android 控制台
基于鼠标和键盘的控制方法允许用户与仿真器交互并执行常见任务。但是,通过这种方法无法直接控制硬件功能,如网络连接。Android 控制台提供了一个广泛的界面,允许用户控制仿真器和硬件功能。在一台机器上,多个模拟器实例可以并行运行。每个模拟器实例都被自动分配一个唯一的端口号,介于 5554 和 5584 之间。该编号出现在仿真器窗口标题栏上的配置名称之前(参见图 5-17)。
模拟器监听该端口号以提供对 Android 控制台的访问。可以使用 telnet 应用连接到该端口来访问 Android 控制台。Telnet 应用建立到给定端口的 TCP 连接,并允许用户与远程服务交互。在 Mac OS X 和 Linux 平台上,telnet 应用由操作系统提供。对于 Windows 系统,可以下载一个免费的 telnet 应用,比如 PuTTY ( [www.chiark.greenend.org.uk/~sgtatham/p…](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html))。
使用基于您的系统的 telnet 应用,连接到与模拟器实例相关联的地址localhost和端口号。连接到 Android 控制台后,基于文本的界面允许您控制模拟器和硬件功能。输入 help ,可以得到可用命令列表,如图图 5-18 所示。
图 5-18。 仿真器控制端口命令列表
达尔维克调试监控服务器
Android SDK 附带了一个名为 Dalvik Debug Monitor Server (DDMS)的调试工具。DDMS 允许开发人员监控连接的设备和仿真器并与之交互。它提供端口转发、屏幕捕获、访问进程和线程状态、堆信息、文件浏览器、日志和许多其他功能。
DDMS 还充当运行在设备或仿真器上的 Dalvik 虚拟机与 Eclipse 调试器之间的桥梁。它处理底层通信设置,以允许 Eclipse 调试器与 Dalvik 虚拟机通信。这使得开发人员可以轻松调试 Android 应用,就像它们是运行在主机上的普通 Java 应用一样。
尽管 DDMS 是作为一个独立的应用与 Android SDK 一起提供的,但它被 ADT 分解成多个 Eclipse 视图,并作为一个结合了这些单独视图的 Eclipse 透视图提供。在这一章中,我们将重点介绍 DDMS 的 Eclipse 透视风味。
要启动 DDMS 透视图,从顶部菜单栏中选择窗口 打开透视图
其他… ,并从打开透视图对话框中选择 DDMS。 DDMS 透视图由多个特定于 Android 的视图组成,如图图 5-19 所示,并在以下章节中描述。
图 5-19 。DDMS 视角
设备视图
Devices 视图提供了连接的设备和仿真器的列表。通过单击每个设备左侧的加号,可以展开每个设备以显示正在运行的应用列表。设备视图还提供了一个工具栏和下拉菜单来启动所选设备或应用的常用操作,如图图 5-20 所示。
图 5-20。 设备视图下拉菜单
设备视图下拉菜单提供以下选项:
- 调试过程:该选项为选定的应用启动一个调试会话。
- 更新堆:该选项支持收集所选应用的堆信息。
- 转储 HPROF 文件:该选项将所选应用的堆转储到 HPROF 格式的文件中,以便进行更深入的内存调查。
- Cause GC :该选项触发所选应用的垃圾收集,以释放未使用的内存。
- 更新线程:该选项支持跟踪所选应用的线程状态。
- Start Method Profiling :该选项允许从所选的应用中收集方法调用的分析数据。
- 停止进程:该选项停止选中的申请进程。
- 截屏:该选项将设备的当前显示捕捉到一个文件中。
- 重置 adb :该选项重置 Android 调试桥(adb),该调试桥提供主机和设备之间的连接。
仿真器控制视图
如果所选设备是仿真器,仿真器控制视图允许模拟语音和数据网络以及位置状态,用于调试和测试目的,如图图 5-21 所示。
图 5-21。 仿真器控制视图
模拟器控件视图功能分为三个部分:
- 电话状态:此部分允许更改设备网络状态的不同方面,如连接状态、网络速度和延迟。
- 电话操作:该部分允许针对设备生成呼叫和 SMS 消息,以便测试应用与传入语音呼叫和 SMS 消息的交互。
- 位置控制:该部分允许为设备设置一个模拟位置,以测试应用与位置变化的交互。可以将模拟位置指定为固定坐标,或者可以使用 GPX 或 KML 格式的坐标文件将多个位置注入到设备中。
日志猫视图
图 5-22 中的所示的 LogCat 视图提供了对设备日志信息的访问。它以表格的形式实时显示日志消息。该表分为多列,包括级别、时间、PID、应用、标签和消息。
图 5-22。 显示日志消息的 LogCat 视图
LogCat 视图允许按日志级别和基于消息过滤标准过滤日志消息。常用的日志过滤器也可以存储和重用。您可以使用 LogCat 视图界面将显示的日志消息保存到文件中。
线程视图
Threads 视图提供对所选应用的线程状态和堆栈跟踪的访问。默认情况下,不启用线程跟踪。要访问线程信息,请使用设备视图选择应用,然后单击更新线程按钮。线程视图以表格的形式呈现现有线程的列表,如图图 5-23 所示。
图 5-23。 选中应用的线程视图
线程视图列提供每个线程的以下信息:
- ID :这是分配给线程实例的虚拟机。
- TID :这是 Linux 操作系统分配的线程 ID。
- 状态:线程的当前状态,可以是以下任意一种状态:
- 运行中,当执行代码
- 睡觉,当睡在
Thread.sleep()叫 - 监视器,当等待监视器锁定
- 等待,当在等待时
Object.wait()呼叫 - 本机,当执行本机代码时
- Vmwait,当等待虚拟机资源
- Utime :这是以 jiffies 为单位运行用户代码所花费的时间。
- Stime :这是以 jiffies 为单位运行系统代码所花费的时间。
- 名称:这是应用赋予线程的名称。
注: A jiffy 是系统定时器中断一个滴答持续时间的时间单位。在 Android 系统中,一瞬间等于 4 毫秒。
堆视图
堆视图提供关于所选应用正在使用的内存量的信息。它是研究记忆问题的一个非常重要的工具。它以表格的形式显示堆分配的列表,如图图 5-24 所示。此表显示了每个堆分配的计数、总大小和统计信息,按类类型分组。堆视图的底部窗格包含一个直方图,展示了每个分配大小的分配计数。
图 5-24。 堆视图
默认情况下,不会从每个应用收集堆信息。要开始收集堆分配信息,请在 Devices 视图中选择应用,然后单击 Update Heap 按钮。堆视图将开始从应用收集堆分配信息。堆分配信息是在虚拟机进行垃圾收集时收集的。要获得堆分配的快速快照,单击 Cause GC 按钮触发垃圾收集。
分配跟踪器视图
分配跟踪器视图允许跟踪所选应用的内存分配。对于研究复杂应用中的内存问题,这是一个非常有用的工具。该视图以表格形式提供分配列表,如图图 5-25 所示。这些列显示关于分配的信息,包括分配的类类型;分配大小;以及分配发生在哪个类、方法和线程中。
图 5-25。 分配跟踪器视图
要开始从选定的应用收集分配数据,请单击开始跟踪按钮。使用该应用,执行作为内存调查主题的任何操作。在此过程中,您可以通过单击“获取分配”按钮来获取分配的快照。完成调查后,单击“停止跟踪”按钮停止分配跟踪器。
文件浏览器视图
文件资源管理器视图允许用户与选定设备上的文件系统进行交互。如图 5-26 所示,以树和表相结合的形式展示了设备的文件系统。你可以通过点击目录左边的加号来展开目录。该列表还显示了每个文件和目录的大小、权限、修改日期和时间。
图 5-26。 文件浏览器查看所选设备上的列表文件
文件资源管理器视图还通过其工具栏和下拉菜单提供文件操作,下拉菜单有以下选项:
- 拉文件:该选项将文件从设备下载到主机。
- 推送文件:该选项将文件从主机上传到设备。
- 删除:该选项从设备中删除所选文件。
- 新文件夹:该选项向设备添加新文件夹。
这些文件操作在一个受限制的用户帐户下运行,该帐户称为 shell 用户。因此,可以在设备上执行的操作受到该用户帐户权限的限制。如果操作由于限制而无法完成,文件资源管理器视图将显示一个错误对话框来通知用户。
Traceview
由于堆和分配跟踪器视图允许您分析其应用的内存消耗,Traceview 允许您分析应用执行期间 CPU 时间消耗的细分。Traceview 附带了 Android SDK,既可以作为独立的应用,也可以作为 Eclipse 编辑器插件。
Traceview 对记录的跟踪文件进行操作。默认情况下,Dalvik 虚拟机不会生成这些跟踪文件。要创建跟踪文件,您可以使用通过android.os.Debug API 提供的跟踪方法,或者您可以通过单击 Devices 视图中的 Start Method Profiling 按钮启用 DDMS 跟踪。Traceview 分析跟踪文件并给出结果,如图图 5-27 所示。
图 5-27。 Traceview 分析一个跟踪文件
Traceview 有两个面板:
- 时间线面板:顶部面板在自己的行中显示每个线程的执行,时间向右增加。在这个线程中执行的每个方法都用颜色编码,并在时间轴上显示为一条细线。
- Profile panel :底部面板显示每种方法所用时间的详细汇总。它显示了包容性和排他性的时代。独占时间是运行方法本身所花费的时间。Inclusive time 是运行该方法和从此方法调用的其他方法所花费的总时间。配置文件面板还显示了方法被调用的次数。该面板提供了大量信息,用于识别在应用执行期间消耗最多 CPU 时间的方法。
层级查看器
- Android 用户界面构建在布局组件之上,布局组件根据可用的屏幕空间动态定位其子视图。当这些布局和视图的结构不正确时,它们很容易降低整个应用的速度,并且很难在复杂的应用中找到这些瓶颈。
- Android SDK 附带了一个名为 Hierarchy Viewer 的工具,允许您调试和优化用户界面。它提供了布局和视图层次的可视化表示。它还决定了测量、布局和绘制视图所需的时间。瓶颈是用颜色标记的,这使得它们很容易被发现。
- 层次查看器还提供了像素完美工具,它放大了用户界面。这允许您检查实际显示的像素属性,以便进行最后的处理。
尽管 Hierarchy Viewer 是 Android SDK 中的一个独立应用,但它被 ADT 分解成多个 Eclipse 视图,并作为一个合并了这些单独视图的 Eclipse 透视图提供。在这一章中,我们将关注 Eclipse 透视图风格的层次结构查看器。
要启动层次结构查看器透视图,从顶部菜单栏中选择窗口 打开透视图
其他… ,并从打开透视图对话框中选择层次结构查看器。层次结构查看器透视图由多个特定于 Android 的视图组成,如图 5-28 所示,并在以下章节中描述。
图 5-28。 层级视图
Windows 视图
Windows 视图以树状格式列出连接的设备和仿真器。点击左边的加号可以展开每个设备以显示活动窗口,如图图 5-29 所示。
图 5-29。 显示活动窗口的窗口视图
需要选择一个窗口才能使用层级查看器。如果您的应用在列表中不可见,请单击视图的刷新按钮以重新加载活动窗口列表。
树形视图
树形视图以树状方式显示所选窗口的布局结构(参见图 5-28 )。每个树项目都使用线条连接到其父项目,这使得更容易可视化视图层次结构。
每个视图项目都以其名称和资源 ID 作为标题。您可以拖动内容在视图中导航。在标题下面,测量、布局和绘制步骤所花费的时间用颜色编码,并显示为用绿色、黄色或红色填充的圆圈。红色表示视图组件在这些步骤中花费了太多时间。当您点击一个视图项目时,这些步骤的实际测量以毫秒为单位显示,如图图 5-30 所示。
图 5-30。 树形视图显示查看项目详情
树形总览视图
根据视图层次结构的大小,可能无法显示树视图中的所有视图和组件。对于视图层次内的导航,树形总览视图提供了代表整个树形视图窗口的较小地图,如图图 5-31 所示。当前选择的视图在地图上高亮显示。
图 5-31。 将整个树形视图表示为地图的树形视图
查看属性视图
“视图属性”视图提供了对选定视图组件的属性的访问。“视图属性”视图在左窗格中显示为一个选项卡。使用 View Properties 视图,您可以检查所有的属性,而不需要查看应用源代码。为了使导航更容易,属性以按属性类别组织的树形格式显示,如图图 5-32 所示。
图 5-32。 视图属性视图列出活动视图的所有属性
布局视图
布局视图提供了整个窗口的框图表示,如图 5-33 所示。选择视图块时,将在树视图和视图属性视图中选择相应的视图。
图 5-33。 显示块表示的布局视图
块的轮廓颜色也提供了关于视图的额外信息:
- 红色粗体表示当前在树视图中选择的视图。
- 浅红色代表当前选中视图的父视图。
- 白色表示不是当前选定视图的父视图或子视图的可见视图。
Android Lint
Android Lint 是一个工具,用于扫描 Android 应用项目中的潜在错误和最常见的错误。它还会查找布局、资源和清单文件中的任何不一致之处。它是一个非常强大的工具,应该在开发周期中使用,以保持应用源代码的整洁和健壮。
Android Lint 工具可以检测以下问题:
- 缺失和未使用的翻译
- 未使用和不一致的资源
- 字符串资源的排版建议
- 可访问性和国际化问题,如硬编码字符串
- 布局性能问题
- 布局和输入框的可用性问题
- 图标和图形问题,如重复的图标和错误的大小
- 清单错误
- 使用不推荐使用的 API
Android Lint 既作为独立的应用提供,用于快速集成到现有的构建系统中,也作为 Eclipse 插件集成到开发环境中。在这一节中,我们将关注 Lint Eclipse 插件。
要启动 Android Lint,选择一个项目,从顶部菜单栏选择窗口 运行 Android Lint ,如图图 5-34 所示。
图 5-34。 选择运行 Android Lint
Android Lint 遍历项目文件,并通过 Lint Warnings 视图显示其结果,如图图 5-35 所示。
图 5-35。 安卓线头警告视图
Lint 警告以表格形式列出。这些列显示 Lint 警告消息以及相关的文件和行号。从表中选择一个警告项会在右窗格中显示已识别问题的详细描述。
通过其工具栏,Lint Warnings 视图还允许您对列出的警告启动以下操作:
- 刷新:再次检查项目文件,刷新 Lint 警告列表。
- 修复:如果解决方案已知,这将自动修复警告。
- 忽略类型:忽略所有相同类型的警告。例如,您可以忽略所有与图像密度相关的警告。
- 删除:从列表中删除选中的警告。
- 删除所有:从列表中删除所有警告。
Lint 也可以通过其首选项对话框进行配置。在 Windows 和 Linux 上选择窗口 首选项,或者在 Mac OS X 上选择 Eclipse
首选项,并从首选项类别列表中选择 Android,然后选择 Lint 错误检查以访问 Lint 属性。Lint 首选项对话框提供了可以通过 Lint 检测到的问题列表。使用此列表,您可以更改与这些问题相关的严重级别,如图图 5-36 所示。如果某个问题与项目无关,可以将其严重性级别设置为 Ignore,以便在 Lint 警告中隐藏这些问题。
图 5-36。 设置线头偏好
发布申请
如前几章所述,Android 平台要求每个应用都要经过其作者的签名,才能在 Android 平台上部署。ADT 提供了一个向导来指导开发人员完成签名过程。
在开发阶段,Android SDK 透明地生成一个调试密钥来自动签署应用,以简化流程。但是当应用要向公众发布时,Android 要求用发布密钥对其进行签名。
与其他移动平台不同,Android 不依赖认证机构向开发者颁发数字证书。每个 Android 开发者都可以在其主机上生成一个密钥并签署一个 Android 应用。当一个应用被安装在 Android 上时,它的签名被用来检查应用更新的真实性。如果应用更新没有使用相同的密钥签名,Android 不允许新版本作为更新部署。ADT 插件提供了一组向导,用于在公开发布之前生成密钥和签署应用。
要对您的应用进行发布签名,使用包资源管理器,选择应用项目,右键单击,从上下文菜单中选择Android ToolsExport Signed Application Package…,启动导出 Android 应用向导,如图图 5-37 所示。
图 5-37。 导出安卓应用向导
确认要导出的项目,然后单击“下一步”按钮继续。如图 5-38 所示,向导将询问要使用的密钥库的位置。如果这是您第一次签署应用,请选择“创建新的密钥库”单选按钮来生成新的密钥库。密钥库持有一个或多个私钥。使用浏览按钮,选择密钥库的位置和文件名。定义保护密钥库的密码,然后单击“下一步”按钮继续。
图 5-38。 用于导出签名应用的密钥库选择
如果您选择创建一个新的密钥库,导出 Android 应用向导会显示一个表单,以获取足够的信息来正确地生成密钥,如图 5-39 所示。填写完必要的信息后,单击“下一步”按钮继续。
图 5-39。 键创建信息表单
如果您已经有一个要使用的密钥库,向导将要求您从给定的密钥库中选择要使用的密钥,如图 5-40 所示。
图 5-40。 从给定的密钥库中通过别名选择密钥
作为最后一步,导出 Android 应用向导将询问将要发布的已签名的 APK 文件的目标位置,如图图 5-41 所示。单击浏览按钮,并选择位置和文件名。然后单击“完成”按钮开始该过程。
图 5-41。 设置已签名的 APK 文件的发布目的地
向导将在发布模式下编译 Android 应用,并用所选密钥对其进行签名。签署的 APK 文件可以向公众发布。
总结
本章介绍了 Eclipse 的 ADT 插件。我们通过安装 ADT 和 Android SDK 开始了我们的旅程。然后,我们配置了一个 Android 虚拟机,并探索了它的控制界面。接下来,我们看了 DDMS、Traceview、Hierarchy Viewer 和 Android Lint,探索如何在日常 Android 开发中使用这些工具。最后,我们讲述了如何使用 ADT 为 Android 应用签名以供发布。
资源
以下资源可用于本章涵盖的主题:
- Android Lint,
[tools.android.com/tips/lint](http://tools.android.com/tips/lint) - 调试和分析用户界面
- 达尔维克调试监视器,
[www.netmite.com/android/myd…](http://www.netmite.com/android/mydroid/dalvik/docs/debugmon.html) - Android 工具项目,
[tools.android.com/](http://tools.android.com/)
六、项目:电影播放器
在前一章中,我们探索了 Eclipse 的 ADT。我们回顾了 ADT 视图和工具,以及如何在日常 Android 开发中使用它们。在本章中,我们将开始把我们在前面章节中讨论的所有工具和概念付诸实践。
我们的第一个 Android 项目是一个简单的电影播放器应用。因为这个实验的目的是看 Android 在 Eclipse 上的实际开发,所以我们不会太深入地研究 Android 框架 API。在接下来的章节中,我们将继续构建这个简单的项目。
电影播放器概述
我们的电影播放器应用将是一个简单的单活动应用,它将呈现一个电影文件列表,这些文件位于外部存储中。该列表将显示每个电影文件的缩略图、名称和持续时间。当您单击列表中的电影项目时,电影播放器应用将依赖 Android 平台启动相应的视频播放器活动来播放所选电影。尽管这是一个非常简单的项目,但它将允许我们试验我们在前面章节中讨论过的大多数工具和概念。
我们将从使用新的 Android 项目向导来生成框架项目开始。然后我们将使用 ADT 提供的编辑器来创建用户界面。通过清单编辑器,我们将根据项目的需求修改AndroidManifest.xml文件。使用布局编辑器,我们将定义电影列表的用户界面布局,以及电影列表项目的布局。我们将使用资源编辑器来正确定义我们用户界面中需要的字符串资源。在生成必要的布局和资源的同时,我们将使用 Android Lint 并行验证代码。应用将依靠媒体商店内容提供者来获取外部存储器中的电影文件列表。获取的信息将被保存到我们将在本章中定义的电影对象中。我们还将实现电影列表适配器,将信息输入列表视图进行显示。
为了播放选中的电影文件,我们将依靠 Android 平台,利用Activity类的startActivity方法启动相应的视频播放器。在做所有这些的时候,我们将非常依赖 Eclipse 的代码模板、自动代码生成器和重构特性,通过让 Eclipse 处理耗时的操作来简化开发过程。
开始电影播放器项目
要启动我们新的 Android 项目,从顶部菜单栏选择文件 新建
其他打开新建项目对话框,如图图 6-1 所示。
图 6-1。 Eclipse 新项目对话框
“新建项目”对话框按项目类别组织。展开 Android 项目类别,选择 Android 项目作为项目类型,然后单击 Next 按钮。这将启动新建 Android 项目向导。作为第一步,您需要提供项目名称及其位置。通过选择相应的单选按钮,您还可以选择是从一个空项目开始,在现有项目的基础上构建一个项目,还是从一个 Android 示例应用开始。对于本例,将项目命名为MoviePlayer,如图图 6-2 所示,然后点击下一步按钮。
图 6-2。 新建 Android 项目向导
接下来,新建 Android 项目向导询问新项目的 Android 平台目标,如图图 6-3 所示。列表将只显示已经安装的 SDK。如果您的目标平台不在列表中,您可能需要使用 Android SDK 管理器下载它。对于这个项目,选择 Android 2.3.3,API Level 10 作为目标平台。这意味着新项目将在任何支持 API 级别 10 及以上的 Android 设备上运行。单击“下一步”按钮继续。
图 6-3。 为新项目选择目标平台
Android 应用被打包成具有唯一包名的包。包命名概念和命名约定是从 Java 编程语言中借鉴来的。包名通常使用分层命名模式来定义,层次结构的各个级别用点分隔。虽然 Android 应用代码可能包含多个包,但应该仍然有一个主包供 Android 引用该应用。
作为定义新项目的最后一步,新的 Android 项目向导将要求应用名和唯一的包名。在我们的例子中,包名是com.apress.movieplayer,如图 6-4 中的所示。除了包名之外,这个对话框还要求最小的 SDK。最低 SDK 确定了在 Android 设备上运行该应用所需的最低 API 级别。
图 6-4。 输入应用名称和唯一的包名
在图 6-4 中可以看到,新的 Android 项目向导还可以生成大部分默认组件,比如主活动和单元测试项目,提供足够的骨架代码,使其更快地启动一个新项目。因为我们的电影播放器应用需要一个活动来与用户交互,所以选择 Create Activity 选项。
单击“完成”按钮。新建 Android 项目向导会自动生成项目布局以及所需的项目文件,如图图 6-5 所示。
图 6-5 。项目布局和所需项目文件
将创建以下项目目录和文件:
src:该目录包含 Java 源文件。应用包是由新建 Android 项目向导在这个目录中自动生成的。gen:该目录包含自动生成的项目文件,如资源索引的 R 类。用户不应修改该目录的内容。每次编译项目时,都会重新生成该目录的内容。assets:该目录包含应用素材。bin:这个目录包含了这个应用编译后的类文件和可安装的 Android 包文件。用户不应修改该目录的内容。res:该目录包含不同类型应用资源的子目录。新的 Android 项目向导将自动在相应的资源目录中为主活动生成布局、字符串资源和图标。资源组织如下:- 动画资源保存在 anim 子目录中。
- 颜色资源保存在颜色子目录中。
- 根据目标屏幕分辨率,图像文件保存在相应的可绘制子目录中。
- 用户界面资源保存在布局子目录中。
- 菜单资源保存在菜单子目录中。
- 其他资源(如字符串资源和用户界面样式)保存在 values 子目录中。
AndroidManifest.xml:这是应用清单文件。新的 Android 项目向导自动生成这个文件,其中的内容来自通过向导对话框收集的信息。proguard.cfg:这是 ProGuard 配置文件,在对发布版本的应用包进行模糊处理时由 ProGuard 使用。project.properties:这是一个 Android SDK 构建系统在编译和打包应用时使用的属性文件。
使用 ADT 编辑器
ADT 提供了各种编辑器来操作项目文件。在接下来的小节中,我们将使用这些编辑器根据我们的项目需求定制项目框架。
清单编辑器
- 双击
AndroidManifest.xml文件将其打开。ADT 附带了一个用于操作清单文件的定制编辑器。Eclipse 将检测文件的类型并用清单编辑器打开它,如图 6-6 所示。
图 6-6。 安卓清单文件编辑器
清单编辑器提供了一组选项卡,允许操作 Android 清单文件的各个方面。由于用户界面提供了所有可能的值,这使得编辑清单文件变得更容易和更健壮。在任何时候,您都可以切换到 XML 选项卡(AndroidManifest.xml)来处理 XML 源文件。
布局编辑器
Android 应用用户界面是使用基于 XML 的布局文件定义的。对于复杂的用户界面,维护这些 XML 文件是一项非常具有挑战性的任务。ADT 附带了一个用于 Eclipse 的可视化用户界面编辑器插件,它允许您设计和维护布局 XML 文件。
要查看布局编辑器的运行,使用项目浏览器,导航到res目录,然后是layout目录,选择main.xml文件。main.xml文件是我们主活动的布局文件。Eclipse 会自动检测这个文件的类型,并在 ADT 的布局编辑器中打开它,如图图 6-7 所示。代码生成器已经用“Hello World”消息填充了这个布局文件。
图 6-7。 安卓视觉布局编辑器
可视化布局编辑器有三个窗格:
- 右边的窗格显示了当前的布局,就像在真实的 Android 设备上一样。
- 顶部窗格提供了一组下拉菜单来更改显示的大小和方向,以便查看布局如何根据这些更改进行自我调整。
- 左侧窗格包含可用小部件和布局组件的列表。您可以将此窗格中的任何视图组件拖放到右窗格中,以将视图组件添加到当前布局中。
右键单击视图组件显示可用参数列表,您可以更改这些参数。除了提供可视化设计功能,编辑器还允许您直接与底层 XML 格式的布局代码进行交互。要切换到 XML 编辑模式,选择编辑器底部的main.xml选项卡。
现在让我们使用布局编辑器来更改我们的电影播放器应用的布局。
电影列表布局
我们希望让我们的电影播放器应用以列表形式显示电影文件。通过选择main.xml选项卡切换到 XML 编辑器模式,并在清单 6-1 中键入代码。
**清单 6-1。**main . XML 文件
` <LinearLayout xmlns:android="schemas.android.com/apk/res/and…" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" >
<ListView android:id="@+id/movieListView" android:layout_width="fill_parent" android:layout_height="fill_parent" > `
这个 XML 组件只包含一个全屏的android.widget.ListView。属性,我们将 ID movieListView分配给android.widget.ListView组件。任何视图对象都可以有一个与之相关联的 ID,以便在视图层次结构中唯一地标识它。IDs 允许您在应用代码中引用视图组件。ID 字符串开头的 at 符号(@)表示 XML 解析器应该将其扩展并标识为 ID 资源。at 符号后面的加号(+)表示这是一个新的资源名称,必须添加到 ID 资源中。
现在回到视觉设计模式,看看布局的效果,如图图 6-8 所示。
图 6-8。??【列表视图】添加到布局
电影项目布局
默认情况下,ListView允许您快速将数据显示为文本项。然而,对于我们的电影播放器应用,我们还希望在左侧显示电影缩略图,以便用户更容易做出选择。
要定义这个自定义列表项布局,从顶部菜单栏选择文件 新建
其他,从列表中选择 Android XML 布局文件,如图图 6-9 所示。
图 6-9。 选择新的 Android XML 布局文件
下一步,Android XML 布局文件向导将要求输入文件名和根元素。该布局的文件名将为movie_item.xml。我们希望列表项在左边有缩略图,在右边有电影标题,在标题下面有电影时长。我们能够描述布局的方式强烈表明android.widget.RelativeLayout是项目布局的正确根元素。从列表中选择RelativeLayout,如图图 6-10 所示,然后点击完成按钮。
图 6-10。 选择新的布局根元素
在这个布局中,我们将使用一个android.widget.ImageView视图来显示电影缩略图,使用两个android.widget.TextView视图来显示电影标题和时长。切换到 XML 编辑器模式,在清单 6-2 中键入 XML 代码。
**清单 6-2。**movie _ item . XML 文件
` <RelativeLayout xmlns:android="schemas.android.com/apk/res/and…" android:layout_width="match_parent" android:layout_height="match_parent">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginRight="16dp"
android:src="@drawable/ic_launcher" /> <TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/thumbnail"
android:layout_toRightOf="@+id/thumbnail"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/duration" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/title" android:layout_below="@+id/title" android:text="Small Text" android:textAppearance="?android:attr/textAppearanceSmall" />
`
你现在可以切换到可视化编辑器模式来查看布局的运行,如图图 6-11 所示。
图 6-11: 电影项目添加到布局
您可能已经注意到,在可视化布局编辑器的右上角有一个小小的警告图标。如果你将鼠标悬停在这个图标上,你会看到 Android Lint 警告你这个布局可能存在的问题。点击警告图标,弹出 Android Lint 的警告对话框,如图图 6-12 所示。
图 6-12。 Android Lint 警告对话框显示布局问题
对于前两个错误,Android Lint 告诉我们,我们在 XML 布局文件中使用的字符串是硬编码的,它们应该在字符串资源中。Lint 可以自动为我们修复这些错误,如第五章所述。
选择与硬编码的"Large Text"字符串相关的第一个问题,并单击 Fix 按钮。Lint 将显示提取 Android 字符串对话框以确认提议的更改,如图 6-13 所示。单击“确定”按钮继续。
图 6-13。 Lint 用字符串引用替换硬编码字符串
对与"Small Text"字符串相关的第二个错误重复相同的程序。现在,布局 XML 文件的相关部分将如下所示:
`<TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/thumbnail" android:layout_toRightOf="@+id/thumbnail" ** android:text="@string/large_text"** android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_alignLeft="@+id/title"
android:layout_below="@+id/title"
** android:text="@string/small_text"**
android:textAppearance="?android:attr/textAppearanceSmall" />`
对于这两个错误,Lint 定义了一个字符串资源,并用相应的字符串资源 ID 替换布局文件中的android:text属性值。在布局文件中不使用任何硬编码的字符串是定义 Android 布局的正确方式。
与其让 Lint 为我们修复第三个错误,不如让我们手动修复它。在布局编辑器中,用字符串引用thumbnail_description定义缩略图的android:contentDescription属性。经过这一更改后,ImageView组件将如下所示:
<ImageView android:id="@+id/thumbnail" android:layout_width="64dp" android:layout_height="64dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginRight="16dp" ** android:contentDescription=”@string/thumbnail_description”** android:src="@drawable/ic_launcher" />
由于字符串资源尚未定义,错误标记将显示在thumbnail_description旁边。我们将使用资源编辑器来定义这个字符串资源。
资源编辑器
Android 应用字符串资源存储在 XML 格式的文件中。ADT 提供了一个自定义编辑器来操作这些资源文件。导航到res目录,然后是values目录,选择strings.xml资源文件。Eclipse 将在自定义编辑器中打开资源文件,如图图 6-14 所示。
图 6-14。 资源编辑
在编辑器的顶部窗格中,您将看到一组字母来过滤资源列表,使其只包含某些类型的元素。通过单击右边的按钮,您可以操作资源列表。在任何时候,通过切换到 XML 选项卡,您可以直接与资源 XML 源文件进行交互。
要定义thumbnail_description字符串资源,请单击添加按钮。在出现的对话框中,选择字符串作为资源类型,如图图 6-15 所示,然后点击确定按钮继续。
图 6-15。 选择资源类型
使用右侧窗格,定义thumbnail_description字符串资源,如图图 6-16 所示。
图 6-16。 定义字符串资源
定义类别
我们已经完成了用户界面和必要资源的定义。我们现在将开始实现必要的模型类来保存将在用户界面中显示的数据。
电影课
对于我们的电影播放器应用,我们需要一个名为Movie的模型类来存储每个电影项目的信息。从顶部菜单栏选择文件 新建
类来定义一个新类。Eclipse 将询问类名及其包。将类名字段设置为
Movie,将包名设置为com.apress.movieplayer。在编辑器区域,输入清单 6-3 中的 Java 代码(现在不用担心错误)。
清单 6-3。【Movie.java 档案】??
`package com.apress.movieplayer;
/**
* Movie file meta data.
* @author Onur Cinar
/
class Movie {
/ Movie title. */
private final String title;
/** Movie file. */ private final String moviePath;
/** MIME type. */ private final String mimeType;
/** Movie duration in ms. */ private final long duration;
/** Thumbnail file. */ private final String thumbnailPath;
/** * Constructor. * * @param mediaCursor * media cursor. * @param thumbnailCursor * thumbnail cursor. */ public Movie(Cursor mediaCursor, Cursor thumbnailCursor) { title = mediaCursor.getString(mediaCursor .getColumnIndexOrThrow(MediaStore.Video.Media.TITLE));
moviePath = mediaCursor.getString(mediaCursor .getColumnIndex(MediaStore.Video.Media.DATA));
mimeType = mediaCursor.getString(mediaCursor .getColumnIndex(MediaStore.Video.Media.MIME_TYPE));
duration = mediaCursor.getLong(mediaCursor .getColumnIndex(MediaStore.Video.Media.DURATION));
if ((thumbnailCursor != null) && thumbnailCursor.moveToFirst()) { thumbnailPath = thumbnailCursor.getString(thumbnailCursor .getColumnIndex(MediaStore.Video.Thumbnails.DATA)); } else { thumbnailPath = null; } } }`
这定义了一个新的Movie类,它有五个成员字段:
- 电影名称
- 电影文件 URI
- 电影文件的 MIME 类型
- 持续时间(毫秒)
- URI 电影
我们将从android.provider.MediaStore内容提供商那里获取信息,这是一个系统内容提供商,用于向应用提供有关设备上媒体文件的信息。当你在编辑器中输入代码时,你会开始看到来自 Eclipse 的错误标记,指示代码中的错误,如图 6-17 所示。
图 6-17。 Eclipse 代码中指示错误
当您将鼠标悬停在代码中带红色下划线的错误上时,Eclipse 将自动显示 Quick Fix 视图,其中包含修复问题的可能操作的建议。在我们的应用中,问题是我们没有导入所有被引用的类。您可以使用快速修复来手动修复它们,或者在 Windows 和 Linux 上按 Ctrl+O,或者在 Mac OS X 上按 Command+O 来组织和修复所有导入。
为了访问成员字段,我们现在需要定义 getter 和 setter 方法。如第四章所述,我们可以让 Eclipse 自动生成这些 getters 和 setters,如图图 6-18 所示。
图 6-18。 自动生成电影类的 getters 和 setters
现在,Movie类的源代码将看起来像清单 6-4 中的。
**清单 6-4。**Movie.java 产生后的和
`package com.apress.movieplayer;
import android.database.Cursor; import android.provider.MediaStore;
/** * Movie file meta data. * * @author Onur Cinar / class Movie { /* Movie title. */ private final String title;
/** Movie file. /
private final String moviePath; /* MIME type. */
private final String mimeType;
/** Movie duration in ms. */ private final long duration;
/** Thumbnail file. */ private final String thumbnailPath;
/** * Constructor. * * @param mediaCursor * media cursor. * @param thumbnailCursor * thumbnail cursor. */ public Movie(Cursor mediaCursor, Cursor thumbnailCursor) { title = mediaCursor.getString(mediaCursor .getColumnIndexOrThrow(MediaStore.Video.Media.TITLE));
moviePath = mediaCursor.getString(mediaCursor .getColumnIndex(MediaStore.Video.Media.DATA));
mimeType = mediaCursor.getString(mediaCursor .getColumnIndex(MediaStore.Video.Media.MIME_TYPE));
duration = mediaCursor.getLong(mediaCursor .getColumnIndex(MediaStore.Video.Media.DURATION));
if (thumbnailCursor.moveToFirst()) { thumbnailPath = thumbnailCursor.getString(thumbnailCursor .getColumnIndex(MediaStore.Video.Thumbnails.DATA)); } else { thumbnailPath = null; } }
/** * Get the movie title. * * @return movie title. */ public String getTitle() { return title; }
/**
* Gets the movie path. *
* @return movie path.
*/
public String getMoviePath() {
return moviePath;
}
/** * Gets the MIME type. * * @return MIME type. */ public String getMimeType() { return mimeType; }
/** * Gets the movie duration. * * @return movie duration. */ public long getDuration() { return duration; }
/** * Gets the thumbnail path. * * @return thumbnail path. */ public String getThumbnailPath() { return thumbnailPath; }
/* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "Movie [title=" + title + ", moviePath=" + moviePath + ", mimeType=" + mimeType + ", duration=" + duration + ", thumbnailPath=" + thumbnailPath + "]"; } }`
电影列表适配器类
用户界面组件需要一个适配器来使用它的数据。尽管 Android 框架提供了默认适配器,但是由于自定义的项目布局,这些默认适配器在电影播放器应用中是不可用的。
要定义新的适配器类,从顶部菜单栏中选择文件 新建
类。将新的类文件命名为
MovieListAdapter,同时将其超类设置为android.widget.BaseAdapter,如图图 6-19 所示。
图 6-19。 将超类设置为 BaseAdapter
Eclipse 将自动为每个需要在MovieListAdapter类中实现的抽象方法生成空体。实现这些方法后,MovieListAdapter代码将看起来像清单 6-5 中的。
清单 6-5。【MovieListAdapter.java 档案】??
`package com.apress.movieplayer;
import java.util.ArrayList;
import android.content.Context; import android.net.Uri; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView;
/** * Movie list view adapter. * * @author Onur Cinar / class MovieListAdapter extends BaseAdapter { /* Context instance. */ private final Context context;
/** Movie list. */ private final ArrayList movieList;
/** * Constructor. * * @param context * context instance. * @param movieList * movie list. */ public MovieListAdapter(Context context, ArrayList movieList) { this.context = context; this.movieList = movieList; }
/**
* Gets the number of elements in movie list.
*
* @see BaseAdapter#getCount() */
public int getCount() {
return movieList.size();
}
/** * Gets the movie item at given position. * * @param poisition * item position * @see BaseAdapter#getItem(int) */ public Object getItem(int position) { return movieList.get(position); }
/** * Gets the movie id at given position. * * @param position * item position * @return movie id * @see BaseAdapter#getItemId(int) */ public long getItemId(int position) { return position; }
/** * Gets the item view for given position. * * @param position * item position. * @param convertView * existing view to use. * @param parent * parent view. */ public View getView(int position, View convertView, ViewGroup parent) { // Check if convert view exists or inflate the layout if (convertView == null) { LayoutInflater layoutInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = layoutInflater.inflate(R.layout.movie_item, null); }
// Get the movie at given position Movie movie = (Movie) getItem(position);
// Set thumbnail ImageView thumbnail = (ImageView) convertView
.findViewById(R.id.thumbnail);
if (movie.getThumbnailPath() != null) { thumbnail.setImageURI(Uri.parse(movie.getThumbnailPath())); } else { thumbnail.setImageResource(R.drawable.ic_launcher); }
// Set title TextView title = (TextView) convertView.findViewById(R.id.title); title.setText(movie.getTitle());
// Set duration TextView duration = (TextView) convertView.findViewById(R.id.duration); duration.setText(getDurationAsString(movie.getDuration()));
return convertView; }
/** * Gets the given duration as string. * * @param duration * duration value. * @return duration string. */ private static String getDurationAsString(long duration) { // Calculate milliseconds long milliseconds = duration % 1000; long seconds = duration / 1000;
// Calculate seconds long minutes = seconds / 60; seconds %= 60;
// Calculate hours and minutes long hours = minutes / 60; minutes %= 60;
// Build the duration string String durationString = String.format("%102d:%303d", hours, minutes, seconds, milliseconds);
return durationString; } }`
MovieListAdapter构造函数获取一组Movie类,并在android.widget.ListView请求时提供给它们。MovieListAdapter的getView方法使用Movie对象的成员字段填充我们的定制列表项布局。
活动类
现在我们已经满足了所有的先决条件,我们可以开始为 activity 类编写代码了。MoviePlayerActivity将提供android.widget.ListView组件向用户显示电影列表。电影信息将来自android.provider.MediaStore内容提供商。
使用Activity类的managedQuery方法,我们将首先向android.provider.MediaStore查询一组电影信息。对于每部电影,我们将对android.widget.MediaStore进行第二次查询以获得电影缩略图。结果稍后将存储在Movie类实例中,并显示在列表视图中。当您选择一个电影项目时,它将由默认的视频播放器根据其类型播放。将清单 6-6 中的代码输入到MediaPlayerActivity的编辑区。
清单 6-6。【MediaPlayerActivity.java 档案】??
`package com.apress.movieplayer;
import java.util.ArrayList;
import android.app.Activity; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView;
/**
* Movie player.
*
* @author Onur Cinar
/
public class MoviePlayerActivity extends Activity implements OnItemClickListener
{
/* Log tag. /
private static final String LOG_TAG = "MoviePlayer"; /*
* On create lifecycle method.
*
* @param savedInstanceState saved state.
* @see Activity#onCreate(Bundle)
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ArrayList movieList = new ArrayList();
// Media columns to query String[] mediaColumns = { MediaStore.Video.Media._ID, MediaStore.Video.Media.TITLE, MediaStore.Video.Media.DURATION, MediaStore.Video.Media.DATA, MediaStore.Video.Media.MIME_TYPE };
// Thumbnail columns to query String[] thumbnailColumns = { MediaStore.Video.Thumbnails.DATA };
// Query external movie content for selected media columns Cursor mediaCursor = managedQuery( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, mediaColumns, null, null, null);
// Loop through media results if ((mediaCursor != null) && mediaCursor.moveToFirst()) { do { // Get the video id int id = mediaCursor.getInt(mediaCursor .getColumnIndex(MediaStore.Video.Media._ID));
// Get the thumbnail associated with the video Cursor thumbnailCursor = managedQuery( MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, thumbnailColumns, MediaStore.Video.Thumbnails.VIDEO_ID + "=" + id, null, null);
// New movie object from the data Movie movie = new Movie(mediaCursor, thumbnailCursor); Log.d(LOG_TAG, movie.toString());
// Add to movie list movieList.add(movie);
} while (mediaCursor.moveToNext());
} // Define movie list adapter
MovieListAdapter movieListAdapter = new MovieListAdapter(this,
movieList);
// Set list view adapter to movie list adapter ListView movieListView = (ListView) findViewById(R.id.movieListView); movieListView.setAdapter(movieListAdapter);
// Set item click listener movieListView.setOnItemClickListener(this); }
/** * On item click listener. */ public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // Gets the selected movie Movie movie = (Movie) parent.getAdapter().getItem(position);
// Plays the selected movie Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse(movie.getMoviePath()), movie.getMimeType()); startActivity(intent); } }`
运行应用
我们的示例应用现在已经可以试用了。你可以在 Android 设备或模拟器上运行它。如果您要在 Android 模拟器中运行电影播放器应用,请确保模拟器配置了第五章中讨论的设置。
电影播放器应用需要一组电影文件存在于外部存储器(SD 卡)中,以便显示任何内容。如果您没有任何电影文件,请在启动应用之前使用相机应用录制一些示例电影文件。
当您准备好测试应用时,从顶部菜单栏中选择运行 运行。因为这是你第一次运行这个应用,Eclipse 会询问你想如何运行它,如图 6-20 所示。
**注意:**默认情况下,某些 Android 设备通过 USB 连接到主机时,会被配置为充当存储介质。这可能会阻止电影播放器应用访问 SD 卡。使用 USB 设置,将 USB 操作模式更改为仅充电,以防止 SD 卡被锁定。
图 6-20。 运行方式对话框询问应用应该如何运行
从运行方式对话框中选择 Android 应用。如果当前连接了不止一个设备或模拟器,Eclipse 将要求您选择执行应用的目标设备,如图 6-21 所示。
图 6-21。 安卓设备选择器对话框
在对话框中点击确定后,应用将在所选的 Android 设备或仿真器上启动,如图图 6-22 所示。
图 6-22。 电影播放器应用列表电影
您可以从设备上的应用列表中选择电影播放器,再次启动应用。
总结
在本章中,我们开始开发一个电影播放器应用,以熟悉典型的 Android 项目开发周期。我们将前面章节中涉及的一些核心概念和组件付诸实践。我们使用了新的 Android 项目向导、清单编辑器、布局编辑器、Android Lint 和资源编辑器。我们还定义了一个 Android 活动,并从内容提供商那里获取数据。在整个章节中,我们使用了 Eclipse 的代码模板、代码生成器和重构特性来自动化一些开发过程。在接下来的章节中,我们将扩展这个项目来演示 Android 应用开发的其他方面。