Android 4高级编程(第3版)


移动开发经典丛书 Android 4 高级编程(第 3 版) [英] Reto Meier 著 佘建伟 赵凯 译 北 京 Reto Meier Professional Android 4 Application Development EISBN:978-1-118-10227-5 Copyright © 2012 by John Wiley & Sons, Inc. , Indianapolis, Indiana All Rights Reserved. This translation published under license. 本书中文简体字版由 Wiley Publishing, Inc. 授权清华大学出版社出版。未经出版者书面许可,不得以任何方式 复制或抄袭本书内容。 北京市版权局著作权合同登记号 图字:01-2012-4749 本书封面贴有 Wiley 公司防伪标签,无标签者不得销售。 版权所有,侵权必究。侵权举报电话:010-62782989 13701121933 图书在版编目(CIP)数据 Android 4 高级编程/(美)迈耶(Meier, R.) 著;佘建伟,赵凯 译. —3 版. —北京:清华大学出版社,2013.4 (移动开发经典丛书) 书名原文:Professional Android 4 Application Development ISBN 978-7-302-31558-2 I. ①A… Ⅱ. ①迈… ②佘… ③赵… Ⅲ. ①移动终端—应用程序—程序设计 Ⅳ. ①TN929.53 中国版本图书馆 CIP 数据核字(2013)第 030567 号 责任编辑:王 军 于 平 装帧设计:牛静敏 责任校对:邱晓玉 责任印制: 出版发行:清华大学出版社 网 址:http://www.tup.com.cn,http://www.wqbook.com 地 址:北京清华大学学研大厦 A 座 邮 编:100084 社 总 机:010-62770175 邮 购:010-62786544 投稿与读者服务:010-62776969,c-service@tup.tsinghua.edu.cn 质 量 反 馈:010-62772015,zhiliang@tup.tsinghua.edu.cn 印 刷 者: 装 订 者: 经 销:全国新华书店 开 本:185mm×260mm 印 张:45.25 字 数:1214 千字 版 次:2013 年 4 月第 1 版 印 次:2013 年 4 月第 1 次印刷 印 数:1~4000 定 价:98.00 元 —————————————————————————————————————————————— 产品编号: 作 者 简 介 Reto Meier 目前是 Google Android 团队的一名 Android 开发人员倡导者,帮助 Android 开发人员 创建最优秀的应用程序。Reto 是一位经验丰富的软件开发人员,拥有逾 10 年的 GUI 应用程序开发 经验。进入 Google 之前,他曾在多种行业中工作过,包括海洋石油、天然气以及金融业。 Reto 始终不渝地追求掌握新技术,从 2007 年 Android 发布之初 Reto 就迷恋上了此项技术。 在 Reto 的个人网站 Radioactive Yak(http://blog. radioactiveyak.com)上可以了解 Reto 的兴趣和爱 好。他还在 Google+(http://profiles.google.com/reto.meier)和 Twitter(www.twitter.com/retomeier)上分享 各种信息。 技术编辑简介 Dan Ulery 是一名软件工程师,具有.NET、Java 和 PHP 的开发经验,并且十分熟悉软件部署。 他毕业于爱达荷大学,获得了计算机科学学士学位,并且辅修了数学专业。 译 者 序 如今的智能手机,功能越来越强大。CUP 主频 1.5G、双核甚至四核已经成为主流,智能手机已 然成为一个“小型 PC”。在 PC 上能够完成的应用功能,手机上基本都可以完成,这给从事手机端 应用开发的程序员提供了很大的空间和挑战。智能手机最核心的部分当然是操作系统,目前使用最 多的操作系统包括 Android、iOS、Symbian、Windows Phone 和 BlackBerry OS。 Android 是一种以 Linux 为基础的开放源代码操作系统。 Google 于 2011 年 10 月 19 日正式发布 Android 4.0(代号:Ice Cream Sandwich),距离 Android 操作系统中的第一个正式版本(于 2008 年 9 月 23 日发布,代号为铁臂阿童木(Astro))的发布,仅仅 过去四年。该版本在前一个版本的基础上修复了 bug 并且添加了一些新功能。较之前的版本,Android 4.0 统一了手机和平板电脑使用的系统,UI 更加人性化,速度也有了很大的提升。 对于 Android 应用开发者而言,他们更关注 Android 4.0 支持的新功能,以开发出更炫、更实用 的应用。 本书可谓是学习 Android 4.0 的少见的精品,结构清晰、内容新颖且覆盖面广。不仅涵盖了开发 Android 应用所需的基本知识,如 Android 平台的基本概念、构建用户界面、消息和广播机制、网络 资源的使用、文件系统、构建多任务应用、数据库和数据的搜索,而且还介绍了 Android 应用开发 的高级深入的技术和经验,如怎样才能开发出拥有良好而高级的用户体验和个性 UI 的应用、地图、 人脸识别、传感器、摄像头、蓝牙 WIFI NFC 等硬件的使用,以及多媒体、云客户端开发、应用程 序内收费等。书中的代码示例,通用性很强,甚至可以直接应用到项目当中。该书非常适合要进一 步提高自己 Android 开发水平的从业人员和对 Android 开发有兴趣的读者,也适合作为高校的教材。 在翻译本书的过程中,我们尽量遵照作者的原意。一些新的 Android 词汇我们甚至保留为英文 单词,这样更利于开发人员理解!为了保证翻译技术的准确性,大部分的技术点我们都亲自编码上 机调试,确保给广大的读者提供最准确的翻译内容和技术指导。确保翻译技术的准确性之外,我们 也在内容的通俗易懂上下了功夫,常把译稿交给第一线的开发人员阅读,不断听取他们的意见,期 望能给读者带来一本 Android 编程方面的精品书籍。 在这期间,我投入了几乎所有的业余时间,感谢我的爱人对我工作的支持和理解,更要感谢我 的翻译合作伙伴的鼎力相助。特别要感谢的是清华大学出版社编辑,他们给了我很多的指导。没有 大家的帮助本书就不可能得以成功出版。 我们希望广大的读者能够从该书中受益。虽然,我们竭尽所能让译文准确通俗,但由于水平有 限,时间有限,书中难免有疏漏的地方,敬请广大读者给予批评和指正。 译 者 前 言 对移动开发人员来说,现在是一个令人心潮澎湃的时代。手机从来没有像今天这样流行,强大 的智能手机产品已经为消费者所普遍接受,而且 Android 生态系统已经扩展到了平板电脑和电视设 备,进一步增加了您的 Android 应用程序的受众。 现在,外观时尚且用途广泛的手机带有 GPS、加速计、NFC 和触摸屏等硬件功能,并且具有固 定费率且定价合理的数据计划,因此,它们成为了吸引越来越多的开发者创建各种新颖有趣的 Android 应用程序的平台。 Android 为移动应用程序开发提供了一个开放的平台。因为没有了人为制造的障碍,所以 Android 开发人员可以自由地编写能够充分利用日益强大的手机硬件的应用程序,并在一个开放的市场上销 售它们。因此,随着移动设备的销售量不断增长,开发者对 Android 设备的兴趣也出现了爆炸性的 增长。截止到 2012 年,市场上有数百个手机和平板电脑 OEM,包括 HTC、Motorola、LG、Samsung、 ASUS 和Sony Ericsson。有超过 3 亿的 Android 设备已被激活,并且这个数字仍在以每天新激活 850 000 个设备的速度增长。 通过使用 Google Play,开发人员可以利用开放的市场向所有兼容的 Android 设备发布免费或者 收费的应用程序,而不需要经历审查过程。Android 构建在一个开源框架之上,并且有强大的 SDK 库,已经使开发人员在 Google Play 上发布了超过 450 000 个应用程序。 本书将指导你使用 Android SDK 的版本 4 来构建移动应用程序。每章的讲解将通过一系列示例 项目帮助你逐步掌握 Android 中的各种新功能和技术,以便你能够最大限度地利用 Android。本书 介绍了 Android 编程入门所需的所有基础知识,同时为有经验的移动开发人员讲解了如何利 用 Android 的独特功能来增强现有应用程序或者创建新的、创造性的应用程序。 Google 的理念是尽快发布,然后不断更新。自从 2008 年 10 月 Android 第一次完整发布以来, 共推出了 19 个平台和 SDK 版本。由于发布周期如此之快,软件和开发库很可能会有定期的修改和 丰富。虽然 Android 的开发团队会尽可能地保持向后兼容性,但在未来的版本中,本书提供的某些 信息仍可能会过时。类似地,并不是每个用户的 Android 设备都在运行最新的平台版本。 只要有可能,本书就会指出哪些平台版本支持书中所介绍的功能,以及可以使用哪些方法为早 期设备的用户提供支持。本书的内容和示例提供了如何使用当前 SDK 来编写优秀的移动应用程序 所需要的基础知识,同时也保持了快速适应未来版本更强大功能的灵活性。 0.1 读者对象 本书适合所有对在 Android 移动手机平台上创建应用程序感兴趣的人。不管是经验丰富的移动 开发人员,还是想通过 Android 开发移动应用程序的新手,都能够从本书中获得十分有价值的信息。 Android 4 高级编程(第 3 版) VI 如果读者使用过手机(特别是运行 Android 的手机),那么这些使用经验会对阅读本书有所帮助, 但这不是必需的。同样,如果以前有过手机开发经验,那么也有一定的帮助,但这也不是必需的。 不过希望读者具有一定的软件开发经验,并且熟悉基本的面向对象开发实践。对 Java 语法的了 解是必需的。深入理解 Java 并具有 Java 开发经验会带来明显的优势,不过没有这些知识和经验也 没太大影响。 第 1 章和第 2 章简要介绍移动应用程序的开发过程,并包含如何在 Android 上开始开发的说明。 除了这两章之外,对其他章节的阅读顺序不做要求。如果对第 3~9 章中描述的基本组件有所理解, 将有利于你对其他章节的学习。第 10 章和第 11 章详细介绍了如何创建应用程序来提供丰富而一致 的用户体验。第 12~19 章讨论了各种可选功能和高级功能,可以按照顺序阅读,也可以按需阅读。 0.2 本书内容 第 1 章简要介绍 Android,包括它是什么,以及它如何适应当前的移动开发。然后详细讲述了 Android 作为一个开发平台能够提供什么功能,并解释了它为什么是一个创建移动应用程序的良机。 第 2 章讲述了移动开发的一些最佳实践,并解释了如何下载 Android SDK 和开始开发应用程序。 该章同时也介绍了 Android 开发工具,并说明了如何从头创建新的应用程序。 第 3~9 章深入探讨了基本的 Android 应用程序组件。首先讲述了组成 Android 应用程序和它的 生命周期的每个部分,然后介绍了应用程序清单和外部资源,以及活动及其生存期与生命周期。 之后将学习如何使用布局、视图和 Fragment 创建用户界面,并且还将了解在应用程序组件之间 执行动作和发送消息的 Intent 和 Broadcast Receiver 机制。接着将介绍 Internet 资源,之后详细讲 述了数据存储、检索和共享。读者在此将了解首选项保存机制、文件处理、数据库和游标。还将学 习如何使用内容提供器来共享应用程序数据,以及如何访问原生内容提供器的数据。这一部分最后 介绍了如何使用 Service 和后台线程在后台工作。 第 10 章和第 11 章以第 4 章介绍的 UI 知识为基础,介绍了如何使用操作栏、菜单系统和通知 来增强用户体验。在这里将学习如何让应用程序适合各种显示屏(针对多种屏幕尺寸和分辨率进行优 化),如何使应用程序更易于访问,以及如何在应用程序内使用语音识别。 第 12~18 章涉及较高级的主题。在这里将学习如何使用罗盘、加速计和其他硬件传感器来让应 用程序能够对环境做出响应,然后介绍了地图以及基于位置的服务。接着介绍了如何使用动态 Widget、Live Wallpaper 和快速搜索框,使你的应用程序通过主屏幕与用户直接交互。 在介绍了播放和录制多媒体以及使用摄像头以后,你将了解到 Android 的通信功能。在介绍了 蓝牙、NFC、Wi-Fi Direct 和网络管理(包括 Wi-Fi 和移动数据连接)之后,讨论了电话服务和用来发 送及接收 SMS 消息的 API。 第 18 章介绍几个高级开发主题,其中包括安全、IPC、Cloud to Device Messaging、License Verification Library 和 Strict Mode。 最后,第 19 章介绍了在发布和分发应用程序以及利用应用程序盈利时面临的机会和可以采用的 选择,重点讨论了 Google Play。 前 言 VII 提示: 第 2 章更详细地列出了这些要求,并讲述了每个组件的下载地址和安装方法。 提示: 由于许多图书的书名都很类似,所以按 ISBN 进行搜索是最简单的, 本书英文版 的 ISBN 是 978-1-118-10227-5。 0.3 本书结构 本书按照一种合理的顺序进行组织,从而帮助具有不同开发背景的读者更好地学习编写高级 Android 应用程序的方法。尽管对阅读每个章节的顺序不做要求,但是请注意,某些示例项目是跨 越多个章节开发的,在其中每个阶段都会添加一些新功能并做一些改进。 富有移动开发经验且拥有能正常工作的 Android 开发环境的开发人员可以跳过前两章的内容——这 两章简要介绍了移动开发的基本知识以及如何创建开发环境——直接学习第 3~9 章。因为这几章涵 盖了 Android 开发的基础知识,所以深入理解这几章所讲述的概念非常重要。 在学习这几章之后,读者就可以继续学习其余章节了,它们主要介绍了地图、基于位置的服务、 后台应用程序以及诸如硬件交互和联网这样的更高级主题。 0.4 使用本书的要求 要使用本书中的示例代码,你需要通过下载 Android SDK 库和开发工具以及 Java 开发包,来创 建一个 Android 开发环境。你可能还希望通过下载和安装 Eclipse 和 Android 开发工具插件来简化开 发工作,但是这些都不是必需的。 Windows、Mac OS 和Linux 系统环境都支持 Android开发,可以从 Android 站点下载相应的 SDK。 学习本书内容或者开发 Android 应用程序并不需要 Android 设备,但是有一台 Android 设备的帮 助很大,尤其是在测试应用程序时。 0.5 源代码 读者在学习本书中的示例时,既可以手工输入所有代码,也可以使用本书附带的源代码文件。 本书使用的所有源代码都可以从本书合作站点 http://www.wrox.com/或 http://www.tupwk.com. cn/downpage 上下载。只要登录到站点 http://www.wrox.com/,使用 Search 工具或使用书名列表就可 以找到本书。接着单击本书细目页面上的 Download Code 链接,就可以获得所有源代码。 下载了代码后,只需用自己喜欢的解压缩软件对它进行解压缩即可。另外,也可以进入 http://www.wrox.com/dynamic/books/download.aspx 上的 Wrox 代码下载主页,查看本书和其他 Wrox Android 4 高级编程(第 3 版) VIII 提示: 不加入 P2P 也可以阅读论坛上的消息,但要发布自己的消息,就必须加入该论坛。 图书的所有代码。 0.6 勘误表 尽管我们已经尽了最大的努力来保证文章或代码中不出现错误,但是错误总是难免的,如果你 在本书中找到了错误,例如拼写错误或代码错误,请告诉我们,我们将非常感激。通过勘误表,可 以让其他读者避免走入误区,当然,这还有助于提供更高质量的信息。 要在网站上找到本书英文版的勘误表,可以登录 http://www.wrox.com,通过 Search 工具或书名 列表查找本书,然后在本书的细目页面上,单击 Book Errata 链接。在这个页面上可以查看到 Wrox 编辑已提交和粘贴的所有勘误项。完整的图书列表还包括每本书的勘误表,网址是 www.wrox.com/misc-pages/booklist.shtml。 如果你在勘误表上没有找到错误,那么可以到 www.wrox.com/contact/techsupport.shtml 上,完成 上面的表格,并把找到的错误发送给我们。我们将会核查这些信息,如果无误的话,会把它放置到 本书的勘误表中,并在本书的后续版本中更正这些问题。 0.7 p2p.wrox.com 要与作者和同行讨论,请加入 p2p.wrox.com 上的 P2P 论坛。这个论坛是一个基于 Web 的系统, 便于你发布与 Wrox 图书相关的消息和相关技术,与其他读者和技术用户交流心得。该论坛提供了订 阅功能,当论坛上有新的消息时,它可以给你传送感兴趣的论题。Wrox 作者、编辑和其他业界专家 和读者都会到这个论坛上来探讨问题。 在 http://p2p.wrox.com 上,有许多不同的论坛,它们不仅有助于阅读本书,还有助于开发自己 的应用程序。要加入论坛,可以遵循下面的步骤: (1) 进入 p2p.wrox.com,单击 Register 链接。 (2) 阅读使用协议,并单击 Agree 按钮。 (3) 填写加入该论坛所需要的信息和自己希望提供的其他信息,并单击 Submit 按钮。 (4) 你会收到一封电子邮件,其中的信息描述了如何验证账户和完成加入过程。 加入论坛后,就可以发布新消息,回复其他用户发布的消息。可以随时在 Web 上阅读消息。如 果要让该网站给自己发送特定论坛中的消息,可以单击论坛列表中该论坛名旁边的 Subscribe to this Forum 图标。 关于使用 Wrox P2P 的更多信息,可阅读 P2P FAQ,了解论坛软件的工作情况以及 P2P 和 Wrox 图书的许多常见问题。要阅读 FAQ,可以在任意 P2P 页面上单击 FAQ 链接。 前 言 IX P2P.WROX.COM 要与作者和同行讨论,请加入 p2p.wrox.com 上的 P2P 论坛。这个论坛是一个基于 Web 的系统, 便于您张贴与 Wrox 图书相关的消息和相关技术,与其他读者和技术用户交流心得。该论坛提供了 订阅功能,当论坛上有新的消息时,它可以给您传送感兴趣的论题。Wrox 作者、编辑和其他业界专 家和读者都会到这个论坛上来探讨问题。 在 http://p2p.wrox.com 上,有许多不同的论坛,它们不仅有助于阅读本书,还有助于开发自己 的应用程序。要加入论坛,可以遵循下面的步骤: (1) 进入 p2p.wrox.com,单击 Register 链接。 (2) 阅读使用协议,并单击 Agree 按钮。 (3) 填写加入该论坛所需要的信息和自己希望提供的其他信息,单击 Submit 按钮。 (4) 您会收到一封电子邮件,其中的信息描述了如何验证账户,完成加入过程。 加入论坛后,就可以张贴新消息,响应其他用户张贴的消息。可以随时在 Web 上阅读消息。如 果要让该网站给自己发送特定论坛中的消息,可以单击论坛列表中该论坛名旁边的 Subscribe to this Forum 图标。 关于使用 Wrox P2P 的更多信息,可阅读 P2P FAQ,了解论坛软件的工作情况以及 P2P 和 Wrox 图书的许多常见问题。要阅读 FAQ,可以在任意 P2P 页面上单击 FAQ 链接。 注释:不加入 P2P 也可以阅读论坛上的消息,但要张贴自己的消息, 就必须加入 该论坛。 目 录 第 1 章 Android 简介 ................................1 1.1 一些背景信息 .................................. 2 1.1.1 不远的过去....................................2 1.1.2 未来的前景....................................2 1.2 对 Android 的误解........................... 3 1.3 Android:开放的移动开发平台 .... 3 1.4 原生 Android 应用程序................... 4 1.5 Android SDK 的特征....................... 5 1.5.1 访问硬件(包括摄像头、GPS 和 传感器)...........................................5 1.5.2 使用 Wi-Fi、蓝牙技术和 NFC 进行数据传输................................6 1.5.3 地图、地理编码和基于位置的 服务 ................................................6 1.5.4 后台服务........................................6 1.5.5 使用 SQLite 数据库进行数据 存储和检索....................................7 1.5.6 共享数据和应用程序间通信........7 1.5.7 使用 Widget 和 Live Wallpaper 增强主屏幕....................................7 1.5.8 广泛的媒体支持和 2D/3D 图形....7 1.5.9 Cloud to Device Messaging ...........8 1.5.10 优化的内存和进程管理..............8 1.6 开放手机联盟简介 .......................... 8 1.7 运行 Android 的环境....................... 9 1.8 从事移动开发的原因...................... 9 1.9 从事 Android 开发的原因............... 9 1.9.1 推动 Android 普及的因素 ..........10 1.9.2 Android 的独到之处....................10 1.9.3 改变移动开发格局......................11 1.10 开发框架简介 .............................. 11 1.10.1 开发包中的资源........................12 1.10.2 理解 Android 软件栈.................12 1.10.3 Dalvik 虚拟机 ............................14 1.10.4 Android 应用程序架构 .............14 1.10.5 Android 库..................................15 第 2 章 开始入手.................................... 17 2.1 Android 开发 ..................................18 2.1.1 开始前的准备工作......................18 2.1.2 创建第一个 Android 应用程序.....24 2.1.3 Android 应用程序的类型 ...........31 2.2 面向移动设备和嵌入式设备的 开发 .................................................32 2.2.1 硬件限制带来的设计考虑事项 ....32 2.2.2 考虑用户环境..............................35 2.2.3 Android 开发................................36 2.3 Android 开发工具..........................40 2.3.1 Android 虚拟设备管理器 ...........41 2.3.2 Android SDK 管理器...................42 2.3.3 Android 模拟器............................42 2.3.4 Dalvik 调试监控服务(DDMS) ...42 2.3.5 Android 调试桥(ADB) ................43 2.3.6 Hierarchy Viewer 和 Lint 工具....43 2.3.7 Monkey 和 Monkey Runner ........43 第 3 章 创建应用程序和 Activity............. 45 3.1 Android 应用程序的组成部分......46 3.2 应用程序 Manifest 文件简介........47 3.3 使用 Manifest 编辑器....................54 3.4 分离资源.........................................55 3.4.1 创建资源 ......................................55 3.4.2 使用资源 ......................................63 Android 4 高级编程(第 3 版) XII 3.4.3 为不同的语言和硬件创建 资源 ..............................................66 3.4.4 运行时配置更改..........................68 3.5 Android 应用程序生命周期.......... 70 3.6 理解应用程序的优先级和进程 状态................................................. 70 3.7 Android Application 类简介.......... 72 3.7.1 扩展和使用 Application 类.........72 3.7.2 重写应用程序的生命周期 事件 ..............................................73 3.8 深入探讨 Android Activity............ 74 3.8.1 创建 Activity................................74 3.8.2 Activity 的生存期........................76 3.8.3 Android Activity 类......................81 第 4 章 创建用户界面.............................83 4.1 Android UI 基本设计..................... 84 4.2 Android UI 的基础知识................. 84 4.3 布局简介......................................... 85 4.3.1 定义布局......................................86 4.3.2 使用布局创建设备无关的 UI ....87 4.3.3 优化布局......................................90 4.4 To-Do List 示例.............................. 93 4.5 Fragment 介绍.............................. 100 4.5.1 创建新的 Fragment................... 101 4.5.2 Fragment 的生命周期 .............. 101 4.5.3 Fragment Manager 介绍 ........... 105 4.5.4 向 Activity 中添加 Fragment ... 105 4.5.5 Fragment 和 Activity 之间的 接口 ........................................... 110 4.5.6 没有用户界面的 Fragment .......111 4.5.7 Android Fragment 类............... 112 4.5.8 对 To-Do List 示例使用 Fragment.................................... 112 4.6 Android widget 工具箱............... 116 4.7 创建新视图 .................................. 117 4.7.1 修改现有的视图....................... 118 4.7.2 创建复合控件........................... 122 4.7.3 使用布局创建简单的复合 控件 ........................................... 124 4.7.4 创建定制的视图....................... 124 4.7.5 使用定制的控件....................... 137 4.8 Adapter 简介.................................137 4.8.1 部分原生 Adapter 简介............ 138 4.8.2 定制 ArrayAdapter.................... 138 4.8.3 使用 Adapter 绑定数据到 视图 ........................................... 139 第 5 章 Intent和 Broadcast Receiver ..145 5.1 Intent 简介 ....................................145 5.1.1 使用 Intent 来启动 Activity...... 146 5.1.2 Linkify 简介 .............................. 153 5.1.3 使用 Intent 广播事件................ 155 5.1.4 Local Broadcast Manager.......... 159 5.1.5 Pending Intent 简介................... 160 5.2 创建 Intent Filter 和 Broadcast Receiver.........................................161 5.2.1 使用 Intent Filter 为隐式 Intent 提供服务................................... 161 5.2.2 使用 Intent Filter 作为插件和 扩展 ........................................... 170 5.2.3 监听本地 Broadcast Intent........ 173 5.2.4 使用 Broadcast Intent 监控设备的 状态变化................................... 174 5.2.5 在运行时管理 Manifest Receiver..................................... 176 第 6 章 使用 Internet 资源....................177 6.1 下载和分析 Internet 资源............177 6.1.1 连接 Internet 资源..................... 178 6.1.2 使用 XML Pull Parser 分析 XML .......................................... 179 6.1.3 创建一个地震查看器............... 180 6.2 使用 Download Manager .............186 6.2.1 下载文件 ................................... 186 6.2.2 自定义 Download Manager Notification................................ 187 目 录 XIII 6.2.3 指定下载位置........................... 188 6.2.4 取消和删除下载....................... 189 6.2.5 查询 Download Manager.......... 189 6.3 使用 Internet 服务........................ 192 6.4 连接到 Google App Engine ......... 192 6.5 下载数据而不会耗尽电量的 最佳实践....................................... 194 第 7 章 文件、保存状态和首选项.........195 7.1 保存简单的应用程序数据.......... 195 7.2 创建并保存 Shared Preference.... 196 7.3 检索 Shared Preference................ 197 7.4 为地震查看器创建一个设置 Activity.......................................... 197 7.5 首选项框架和 Preference Activity 概述............................................... 205 7.5.1 在 XML 中定义一个 Preference Screen 布局............. 206 7.5.2 Preference Fragment 简介 ........ 208 7.5.3 使用 Preference Header 定义 Preference Fragment 的层次 结构 ........................................... 208 7.5.4 Preference Activity 简介........... 209 7.5.5 向后兼容性与 Preference Screen ........................................ 210 7.5.6 找到并使用 Preference Screen 设置的 Shared Preference......... 210 7.5.7 Shared Preference Change Listener 简介............................. 211 7.6 为地震查看器创建一个标准的 Preference Activity........................ 211 7.7 持久化应用程序实例的状态...... 215 7.7.1 使用 Shared Preference 保存 Activity 状态............................. 215 7.7.2 使用生命周期处理程序保存和 还原 Activity 实例.................... 215 7.7.3 使用生命周期处理程序保存和 还原 Fragment 实例状态 ......... 216 7.8 将静态文件作为资源添加.......... 218 7.9 在文件系统下工作 ......................218 7.9.1 文件管理工具........................... 218 7.9.2 使用特定于应用程序的文件夹 存储文件..............................219 7.9.3 创建私有的应用程序文件....... 219 7.9.4 使用应用程序文件缓存........... 220 7.9.5 存储公共可读的文件............... 220 第 8 章 数据库和 Content Provider......223 8.1 Android 数据库简介....................223 8.1.1 SQLite 数据库简介 .................. 224 8.1.2 Content Provider 简介............... 224 8.2 SQLite 简介..................................224 8.3 Content Value 和 Cursor...............225 8.4 使用 SQLite 数据库.....................225 8.4.1 SQLiteOpenHelper 简介........... 226 8.4.2 在不使用 SQLiteOpenHelper 的 情况下打开和创建数据库....... 228 8.4.3 Android 数据库设计注意 事项 ........................................... 228 8.4.4 查询数据库............................... 228 8.4.5 从 Cursor 中提取值.................. 229 8.4.6 添加、更新和删除行............... 230 8.5 创建 Content Provider..................232 8.5.1 注册 Content Provider............... 233 8.5.2 发布 Content Provider 的 URI 地址 ........................................... 233 8.5.3 创建 Content Provide 的 数据库 ....................................... 234 8.5.4 实现 Content Provider 查询 ..... 235 8.5.5 Content Provider 事务............... 236 8.5.6 在 Content Provider 中存储 文件 ........................................... 239 8.5.7 一个 Content Provider 的实现 框架 ........................................... 240 8.6 使用 Content Provider..................244 8.6.1 Content Resolver 简介.............. 244 8.6.2 查询 Content Provider............... 244 Android 4 高级编程(第 3 版) XIV 8.6.3 使用 Cursor Loader 异步查询 内容 ........................................... 247 8.6.4 添加、删除和更新内容........... 249 8.6.5 访问 Content Provider 中存储的 文件 ........................................... 251 8.6.6 创建一个 To-Do List 数据库和 Content Provider........................ 253 8.7 将搜索功能添加到应用程序中.... 260 8.7.1 使 Content Provider 可搜索 ..... 261 8.7.2 为应用程序创建一个搜索 Activity ...................................... 261 8.7.3 将搜索 Activity 设置为应用 程序的默认搜索 Provider........ 263 8.7.4 使用搜索视图微件................... 266 8.7.5 由 Content Provider 支持搜索 建议 ........................................... 267 8.7.6 在快速搜索框中显示搜索 结果 ........................................... 270 8.8 创建可搜索的地震 Content Provider ......................................... 270 8.8.1 创建 Content Provider............... 270 8.8.2 使用地震 Content Provider ...... 276 8.8.3 搜索 EarthquakeContent Provider ..................................... 279 8.9 本地 Android Content Provider ... 285 8.9.1 使用 Media StoreContent Provider ..................................... 285 8.9.2 使用 Contacts Contract Content Provider ..................................... 286 8.9.3 使用 Calendar Content Provider ..................................... 293 第 9 章 在后台操作...............................297 9.1 Service 简介.................................. 298 9.1.1 创建和控制 Service.................. 298 9.1.2 将 Service 绑定到 Activity....... 302 9.1.3 地震监控 Service 示例............. 304 9.1.4 创建前台 Service...................... 308 9.2 使用后台线程...............................309 9.2.1 使用 AsyncTask 运行异步任务.... 310 9.2.2 Intent Service 简介.................... 312 9.2.3 Loader 简介............................... 313 9.2.4 手动创建线程和 GUI 线程 同步 ........................................... 313 9.3 使用 Alarm ...................................315 9.3.1 创建、设置和取消 Alarm ....... 316 9.3.2 设置重复 Alarm........................ 317 9.3.3 使用重复 Alarm 调度网络 刷新 ........................................... 318 9.4 使用 Intent Service 简化 Earthquake 更新 Service..................................320 第 10 章 扩展用户体验.........................323 10.1 操作栏简介.................................324 10.1.1 自定义操作栏........................ 325 10.1.2 自定义操作栏来控制应用 程序的导航行为 ................... 328 10.1.3 操作栏操作简介.................... 333 10.2 向地震监控程序添加一个 操作栏.........................................333 10.3 创建并使用菜单和操作栏 操作项.........................................339 10.3.1 Android 菜单系统简介......... 340 10.3.2 创建菜单................................ 341 10.3.3 指定操作栏的操作................ 342 10.3.4 菜单项选项............................ 343 10.3.5 添加操作 View 和操作提供 程序........................................ 344 10.3.6 在 Fragment 中添加菜单项... 345 10.3.7 使用 XML 定义菜单层次 结构........................................ 345 10.3.8 动态更新菜单项.................... 347 10.3.9 处理菜单选择........................ 347 10.3.10 子菜单和上下文菜单简介.... 348 10.4 更新地震监控程序 ....................351 10.5 全屏显示.....................................353 10.6 对话框简介.................................355 10.6.1 创建一个对话框.................... 356 目 录 XV 10.6.2 使用 AlertDialog 类 .............. 356 10.6.3 使用专门的输入对话框 ....... 357 10.6.4 通过 Dialog Fragment 管理和 显示对话框............................ 358 10.6.5 通过 Activity 事件处理程序 管理和显示对话框 ............... 360 10.6.6 将 Activity 用作对话框 ........ 361 10.7 创建 Toast ................................... 361 10.7.1 自定义 Toast .......................... 362 10.7.2 在工作线程中使用 Toast...... 364 10.8 Notification 简介........................ 365 10.8.1 Notification Manager 简介.... 366 10.8.2 创建 Notification ................... 366 10.8.3 设置和自定义通知托盘 UI.... 369 10.8.4 配置持续和连续的 Notification ............................ 373 10.8.5 触发、更新和取消 Notification ............................ 374 10.9 向地震监控程序中添加 Notification 和对话框................ 376 第 11 章 高级用户体验 .........................381 11.1 为每个屏幕尺寸和分辨率做 设计 ............................................ 382 11.1.1 分辨率无关............................ 382 11.1.2 为不同的屏幕大小提供支持和 优化........................................ 383 11.1.3 创建可缩放的图形资源 ....... 386 11.1.4 创建优化的、自适应的、 动态的设计 ........................... 390 11.1.5 反复测试................................ 390 11.2 确保可访问性 ............................ 391 11.2.1 为非触屏设备提供导航 ....... 391 11.2.2 为每个 View 提供文本 描述........................................ 391 11.3 Android Text-to-Speech 简介..... 392 11.4 使用语音识别 ............................ 394 11.4.1 使用语音识别进行语音 输入........................................ 395 11.4.2 使用语音识别进行搜索........ 396 11.5 控制设备振动.............................396 11.6 使用动画.....................................397 11.6.1 补间 View 动画 ..................... 397 11.6.2 创建和使用逐帧动画............ 400 11.6.3 插值属性动画........................ 400 11.7 强化 View ...................................404 11.7.1 高级 Canvas 绘图.................. 404 11.7.2 硬件加速................................ 419 11.7.3 Surface View 简介 ................. 420 11.7.4 创建交互式控件.................... 423 11.8 高级 Drawable 资源...................428 11.9 复制、粘贴和剪贴板 ................431 11.9.1 向剪贴板中复制数据............ 431 11.9.2 粘贴剪贴板数据.................... 431 第 12 章 硬件传感器 ............................433 12.1 使用传感器和传感器 管理器 ........................................433 12.1.1 受支持的 Android 传感器.... 434 12.1.2 虚拟传感器简介.................... 435 12.1.3 查找传感器............................ 435 12.1.4 监视传感器............................ 436 12.1.5 解释传感器值........................ 438 12.2 监视设备的移动和方向............439 12.2.1 确定设备的自然方向 ........... 440 12.2.2 加速计简介............................ 441 12.2.3 检测加速度变化.................... 442 12.2.4 创建一个重力计.................... 443 12.2.5 确定设备方向........................ 446 12.2.6 创建一个指南针和人工 地平仪 .................................... 450 12.2.7 陀螺仪传感器简介................ 453 12.3 环境传感器简介 ........................454 12.3.1 使用气压计传感器................ 454 12.3.2 创建气象站............................ 455 第 13 章 地图、地理编码和基于位置的 服务........................................461 13.1 使用基于位置的服务 ................461 Android 4 高级编程(第 3 版) XVI 13.2 在模拟器中使用基于位置的 服务 ............................................ 462 13.2.1 更新模拟器位置提供器中的 位置........................................ 463 13.2.2 配置模拟器来测试基于位置的 服务........................................ 463 13.3 选择一个位置提供器................ 464 13.3.1 查找位置提供器.................... 464 13.3.2 通过指定条件查找位置 提供器.................................... 464 13.3.3 确定位置提供器的能力 ....... 465 13.4 确定当前位置 ............................ 466 13.4.1 位置的隐私性........................ 466 13.4.2 找出上一次确定的位置 ....... 466 13.4.3 Where Am I 示例................... 466 13.4.4 刷新当前位置........................ 469 13.4.5 在 Where Am I 中跟踪 位置........................................ 472 13.4.6 请求单独一次位置更新 ....... 473 13.5 位置更新的最佳实践................ 474 13.6 使用近距离提醒 ........................ 477 13.7 使用地理编码器 ........................ 478 13.7.1 反向地理编码........................ 479 13.7.2 前向地理编码........................ 480 13.7.3 对“Where Am I”示例 进行地理编码........................ 481 13.8 创建基于地图的 Activity.......... 482 13.8.1 MapView 和 MapActivity 简介........................................ 482 13.8.2 获得地图的 API key ............. 483 13.8.3 创建一个基于地图的 Activity................................... 483 13.8.4 地图和 Fragment ................... 485 13.8.5 配置和使用 MapView........... 486 13.8.6 使用 MapController............... 486 13.8.7 对“Where Am I”示例使用 地图........................................ 487 13.8.8 创建和使用覆盖(Overlay).... 490 13.8.9 MyLocationOverlay 简介...... 497 13.8.10 ItemizedOverlay 和 OverlayItem 简介 .................. 498 13.8.11 将视图固定到地图和地图的 某个位置上.......................... 500 13.9 对 Earthquake 示例添加地图 功能..............................................501 第 14 章 个性化主屏幕.........................507 14.1 主屏幕 Widget 简介...................507 14.2 创建 App Widgets ......................509 14.2.1 创建 Widget 的 XML 布局资源................................ 509 14.2.2 定义 Widget 设置.................. 511 14.2.3 创建 Widget Broadcast Receiver 并将其添加到应用程序的 manifest 文件中..................... 512 14.2.4 AppWidgetManager 和 RemoteView 简介 ................. 513 14.2.5 刷新 Widget ........................... 518 14.2.6 创建并使用 Widget 配置 Activity................................... 521 14.3 创建地震 Widget........................522 14.4 Collection View Widget 简介.....528 14.4.1 创建 Collection View Widget 的 布局........................................ 529 14.4.2 创建 RemoteViewsService.... 530 14.4.3 创建一个 RemoteViews- Factory.................................... 531 14.4.4 使用 RemoteViewsService 填充 CollectionViewWidget.... 533 14.4.5 向 Collection View Widget 中的项添加交互性 ............... 534 14.4.6 将 Collection View Widget 绑定到 Content Provider....... 535 14.4.7 刷新 Collection View Widget .................................... 537 14.4.8 创建 Earthquake Collection View Widget........................... 537 14.5 Live Folder 简介.........................543 14.5.1 创建 Live Folder.................... 544 目 录 XVII 14.5.2 创建 Earthquake Live Folder... 548 14.6 使用快速搜索框显示应用程序 搜索结果 .................................... 551 14.6.1 在快速搜索框中显示搜索 结果........................................ 551 14.6.2 将 Earthquake 示例的搜索 结果添加到快速搜索框中 ... 552 14.7 创建 Live Wallpaper................... 553 14.7.1 创建 Live Wallpaper 定义 资源........................................ 553 14.7.2 创建 Wallpaper Service ......... 554 14.7.3 创建 Wallpaper Service 引擎........................................ 555 第 15 章 音频、视频以及摄像头的 使用 .......................................557 15.1 播放音频和视频 ........................ 558 15.1.1 Media Player 简介 ................. 559 15.1.2 准备音频播放........................ 559 15.1.3 准备视频播放........................ 560 15.1.4 控制 Media Player 的 播放........................................ 564 15.1.5 管理媒体播放输出 ............... 566 15.1.6 响应音量控制........................ 566 15.1.7 响应 Media 播放控件........... 567 15.1.8 请求和管理音频焦点 ........... 569 15.1.9 当音频输出改变时暂停 播放........................................ 571 15.1.10 Remote Control Client 简介.... 572 15.2 操作原始音频 ............................ 574 15.2.1 使用 AudioRecord 录制 声音........................................ 574 15.2.2 使用 AudioTrack 播放音频 .... 575 15.3 创建一个 Sound Pool................. 577 15.4 使用音效..................................... 578 15.5 使用摄像头拍摄照片................ 579 15.5.1 使用 Intent 拍摄照片 ............ 579 15.5.2 直接控制摄像头.................... 581 15.5.3 读取并写入 JPEG EXIF 图像 详细信息................................ 588 15.6 录制视频..................................... 589 15.6.1 使用 Intent 录制视频 ............ 589 15.6.2 使用 MediaRecorder 录制 视频........................................ 590 15.7 使用媒体效果 ............................593 15.8 向媒体库中添加新媒体............594 15.8.1 使用媒体扫描仪插入媒体 ... 594 15.8.2 手动插入媒体........................ 595 第 16 章 蓝牙、NFC、网络和 Wi-Fi....597 16.1 使用蓝牙.....................................597 16.1.1 管理本地蓝牙设备适配器 ... 598 16.1.2 可发现性和远程设备发现 ... 600 16.1.3 蓝牙通信................................ 604 16.2 管理网络和 Internet 连接..........609 16.2.1 Connectivity Manager 简介... 609 16.2.2 支持用户首选项以进行后台 数据传输................................ 609 16.2.3 查找和监视网络连接 ........... 611 16.3 管理 Wi-Fi ..................................612 16.3.1 监视 Wi-Fi 连接 .................... 613 16.3.2 监视活动的 Wi-Fi 连接的 详细信息................................ 613 16.3.3 扫描热点................................ 613 16.3.4 管理 Wi-Fi 配置 .................... 614 16.3.5 创建 Wi-Fi 网络配置............ 615 16.4 使用 Wi-Fi Direct 传输数据......615 16.4.1 初始化 Wi-Fi Direct 框架 ..... 615 16.4.2 启用 Wi-Fi Direct 并监视其 状态........................................ 617 16.4.3 发现对等设备........................ 618 16.4.4 连接对等设备........................ 618 16.4.5 在对等设备之间传输数据 ... 620 16.5 近场通信.....................................621 16.5.1 读取 NFC 标签...................... 622 16.5.2 使用前台分派系统................ 623 16.5.3 Android Beam 简介............... 625 第 17 章 电话服务和 SMS ...................629 17.1 电话服务的硬件支持 ................629 17.1.1 将电话功能指定为必需的 硬件功能................................ 629 Android 4 高级编程(第 3 版) XVIII 17.1.2 检查电话硬件........................ 630 17.2 使用电话服务 ............................ 630 17.2.1 启动电话呼叫........................ 630 17.2.2 替换本机拨号程序 ............... 631 17.2.3 访问电话服务的属性及状态 .... 632 17.2.4 使用 PhoneStateListener 监视电话状态的变化 ........... 635 17.2.5 使用 Intent Receiver 监视传入的 电话呼叫................................ 639 17.3 SMS 和 MMS 简介.................... 640 17.3.1 在应用程序中使用 SMS 和 MMS ...................................... 640 17.3.2 使用 Intent 从应用程序中 发送 SMS 和 MMS............... 640 17.3.3 使用 SMS Manager 发送 SMS 消息............................... 641 17.3.4 监听传入的 SMS 消息 ......... 644 17.3.5 紧急响应程序 SMS 示例 ..... 646 17.3.6 自动紧急响应程序 ............... 654 17.4 SIP 和 VOIP 简介...................... 662 第 18 章 Android高级开发...................663 18.1 Android 的安全性...................... 664 18.1.1 Linux 内核安全..................... 664 18.1.2 权限简介................................ 664 18.2 Cloud to Device Messaging 简介 ... 666 18.2.1 C2DM 的局限性 ................... 667 18.2.2 注册使用 C2DM ................... 667 18.2.3 在 C2DM 服务器上注册 设备........................................ 667 18.2.4 向设备发送 C2DM 消息...... 670 18.2.5 接收 C2DM 消息 .................. 672 18.3 使用 License Verification Library 实现版权保护............................ 673 18.3.1 安装 License Verification Library.................................... 673 18.3.2 获得 License Verification 公钥........................................ 673 18.3.3 配置 License Validation Policy...................................... 674 18.3.4 执行许可验证检查................ 674 18.4 应用程序内收费 ........................675 18.4.1 应用程序内收费的局限性 ... 676 18.4.2 安装 IAB 库........................... 676 18.4.3 获得公钥和定义可购买的 物品........................................ 676 18.4.4 开始 IAB 交易....................... 677 18.4.5 处理 IAB 购买请求的响应 .. 678 18.5 使用 Wake Lock .........................679 18.6 使用 AIDL 支持 Service 的 IPC..............................................680 18.7 处理不同硬件和软件的可用性.....686 18.7.1 指定硬件的要求.................... 686 18.7.2 确认硬件可用性.................... 687 18.7.3 构建向后兼容的应用程序 ... 687 18.8 利用 STRICT 模式优化 UI 性能 ............................................689 第 19 章 推广和发布应用程序并从中 获利.......................................691 19.1 签名和发布应用程序 ................691 19.2 发布应用程序 ............................693 19.2.1 Google Play 简介................... 693 19.2.2 开始使用 Google Play........... 694 19.2.3 发布应用程序........................ 695 19.2.4 开发者控制台上的应用程序 报告........................................ 697 19.2.5 查看应用程序错误报告 ....... 697 19.3 如何通过应用程序赚钱............698 19.4 应用程序销售、推广和分发的 策略.............................................699 19.4.1 应用程序的起步策略 ........... 699 19.4.2 在 Google Play 上推广.......... 700 19.4.3 国际化.................................... 700 19.5 分析数据和跟踪推荐人............701 19.5.1 使用移动应用程序的 Google Analytics................................. 702 19.5.2 使用 Google Analytics 追踪 推荐........................................ 703 Android 简 介 本章内容 ● 移动应用程序开发简介 ● Android 简介 ● Android SDK 特征简介 ● 适用 Android 的设备 ● 从事移动开发和 Android 开发的原因 ● Android SDK 和开发架构简介 无论你是一名经验丰富的移动开发工程师、一个桌面开发人员或者 Web 开发人员还是一个初出 茅庐的编程新手,Android 都为编写具有创新性的移动应用程序带来了令人兴奋的新机遇。 虽然它被命名为 Android(机器人),但是它并不会帮助你打造一支势不可挡的铁血机器人部队来 修复人类在地球上造成的破坏。事实上,Android 是一个开源的软件栈,它包含了操作系统、中间 件和关键的移动应用程序,以及一组用于编写移动应用程序的 API 库。所编写的移动应用程序将决 定移动设备的样式、观感和功能。 小巧玲珑、外观时尚且功能丰富的现代移动设备已经成为了集触摸屏、摄像头、媒体播放器、 GPS 系统和近场通信(Near Field Communications,NFC)硬件为一体的强大工具。随着技术的发展, 手机的功能已不再仅仅是打电话那么简单。在添加了对平板电脑和 Google TV 的支持后,Android 已经不再只是一个手机操作系统,它为在越来越多的硬件上进行应用开发提供了一个一致的平台。 在 Android 中,本地应用程序和第三方应用程序使用相同的 API 编写,并且在相同的运行时(run time)上执行。这些 API 的功能包括硬件访问、视频录制、基于位置的服务(location- based service)、后 台服务支持、基于地图的 Activity、关系数据库、应用程序间的通信、蓝牙、NFC 以及 2D 和 3D 图形。 通过本书,你可以学习到如何使用这些 API 来开发自己的 Android 应用程序。在本章中,你将 会学习到一些移动和嵌入式硬件开发的准则以及 Android 开发平台提供的一些功能。 Android 拥有功能强大的 API、出色的文档以及茁壮成长的开发人员社区,而且不需要为开发或 1 第 章 Android 4 高级编程(第 3 版) 2 发布支付费用。随着移动设备的日益普及,以及越来越多的设备采用 Android 作为系统,不管具有 什么样的开发背景,使用 Android 来开发新颖的手机应用程序都是一个令人为之振奋的良机。 1.1 一些背景信息 在 Twitter 和 Facebook 出现之前,当 Google 还只是一个想法的时候,手机只是足够小的便携电 话,能够放在一个公文包中,电池足够用上几个小时。虽然没有多余的功能,但是手机确实使人们 可以不通过物理通信线路就能自由通信。 现在,小巧、时尚而且功能强大的手机已经相当普及并且不可或缺。硬件的发展使手机在拥有 更大更亮的屏幕和越来越多的外围设备的同时也变得更加小巧和高效。 继集成了摄像头和媒体播放器以后,现在的手机更是包含了 GPS 系统、加速计、NFC 硬件和 高分辨率触摸屏。虽然这些硬件上的创新为软件开发提供了广泛的应用基础,但实际情况却不容乐 观,手机应用程序的开发已经落后于相应的硬件水平了。 1.1.1 不远的过去 过去,那些通常使用 C 或者 C++进行编程的开发人员必须理解在其上编写代码的特定硬件。这 些硬件通常是一个设备,但也可能是来自于同一家生产商的一系列设备。随着硬件技术和移动互联 网接入技术的发展,这种封闭的方法很难追赶硬件发展的步伐。 后来,人们开发出了像 Symbian 这样的平台,从而给开发人员提供了更广泛的目标用户群(target audience)。在鼓励移动开发人员开发更加丰富的应用程序以便更高效地利用硬件方面,这些系统比 上述那种封闭的方法更加成功。 这些平台提供了一些访问设备硬件的接口,但是要求编写复杂的 C/C++代码,而且严重依赖那 些因难以使用而著称的专有 API。当开发那些必须运行在不同的硬件实现上的应用程序以及使用特 定的硬件功能(如 GPS)的应用程序时,这些困难就呈现在了开发人员面前。 近几年,移动开发的最大亮点在于引入了由 Java 承载(java-hosted)的 MIDlet。MIDlet 是在一个 Java 虚拟机上执行的,它把底层的硬件抽象出来,从而使开发人员可以开发出能运行在多种硬件上 的应用程序,只要这些硬件支持 Java 运行时(Java run time)就可以。遗憾的是,这种便利是以对设备 硬件的访问限制为代价的。 在移动开发中,通常第三方应用程序的硬件访问和执行权限与手机制造商编写的本机应用程序 的权限是不同的,而 MIDlet 则通常不具有这两种权限。 Java MIDlet 的引入扩大了开发人员的目标用户群,但是由于缺乏对低级硬件的访问权限以及沙 盒式的执行等原因,大部分移动应用程序都是运行在较小屏幕上的桌面程序或 Web 站点,而没有充 分利用移动平台的固有移动性。 1.1.2 未来的前景 最近出现的一些手机操作系统设计理念的创新与突破,都是为了适应手机硬件日益强大的计算 能力,Android 也是在这样一种背景下应运而生的。Microsoft 的 Windows Mobile 和 Apple 的 iPhone 也都为移动应用程序开发提供了一个功能更丰富、使用更简捷的开发环境。然而,与 Android 不同, 它们是构建在专有操作系统的基础上的,而专有操作系统通常使本地应用程序具有比第三方创建的 第 1 章 Android 简 介 3 应用程序更高的优先级,并且限制了应用程序和本地手机数据之间的通信,以及可以在其平台上发 布的第三方应用程序。 Android 通过提供一个以开源的 Linux 内核为基础而构建的开放的开发环境,为移动应用程序的 开发提供了新机遇。通过一系列 API 库,所有应用程序都可以访问硬件,并且在严格受控的条件下 完全支持应用程序之间的交互。 在 Android 中,所有应用程序有相同的优先级。第三方和本地应用程序都使用相同的 API 进行 编写,而且都在相同的运行时上执行。用户可以删除任何本地应用程序,并使用相应的第三方应用 程序对其进行代替,甚至连拨号程序和主屏幕都可以进行替换。 1.2 对 Android 的误解 作为对一个成熟领域破坏性的补充,不难理解为什么一些人会对 Android 具体是什么这个问题 存在很多疑惑。Android 不是: ● 一个 Java ME 实现 Android 应用程序是使用 Java 语言编写的,但是它们并不是运行在一 个 Java ME 虚拟机上的,而且已编译的 Java 类和可执行程序不能在不经过修改的情况下就 运行在 Android 上。 ● Linux 手机标准论坛(Linux Phone Standards Forum,LiPS)或者开放移动联盟(Open Mobile Alliance,OMA)的一部分 Android 运行在一个开源的 Linux 内核的基础上。尽管它们的目 标很相似,但是 Android 的完全软件栈方法和这些标准定义组织的关注点是不同的。 ● 一个简单的应用层(如 UIQ 或者 S60) 尽管 Android 确实包含一个应用层,但是它也描述了 整个软件栈,这个软件栈包含了底层操作系统、API 库和应用程序本身。 ● 一个手机设备 Android 包含了一个移动设备制造商的参考设计,但是并不存在一个 “Android 手机”。相反,Android 是为了支持多种硬件设备而设计的。 ● Google 对 iPhone 的回应 iPhone 是由 Apple 公司发布的完全专有的硬件和软件平台,而 Android 是由开放手机联盟( Open Handset Alliance,OHA )生产和支持的一个开源的软件栈, 是为了能在任何满足要求的手机上运行而设计的。 1.3 Android:开放的移动开发平台 Google 的 Andy Rubin 把 Android 描述为: 为移动设备设计的第一个真正开放的综合平台,包含操作系统、用户界面和应用程序 ——所有软 件都能运行在手机上,从而消除了阻碍移动创新的障碍。(摘自 Where’s My GPhone(http://googleblog. blogspot.com/2007/11/wheres-my-gphone.html))。 最近,Android 的功能得以扩展,不再是一个纯粹的手机平台,而是能够为越来越多的硬件类 型提供一个开发平台,例如平板电脑和电视。 概括地讲,Android 由 3 个组件构成: ● 一个针对嵌入式设备的免费开源操作系统。 Android 4 高级编程(第 3 版) 4 ● 一个用于创建应用程序的开源开发平台。 ● 运行 Android 操作系统以及为这种操作系统编写的应用程序的设备,特别是手机。 确切地讲,Android 由以下几个不可或缺且相互依赖的部分组成: ● 一个兼容性定义文档(Compatibility Definition Document,CDD)和兼容性测试包(Compatibility Test Suite,CTS),它们描述了移动设备为了支持软件栈而需要具备的性能。 ● 一个 Linux 操作系统内核,它提供了与硬件之间的低级接口、内存管理和进程控制,且全 都为移动设备进行了优化。 ● 应用程序开发的开源库,包括 SQLite、WebKit、OpenGL 以及一个媒体管理器。 ● 用来运行和承载 Android 应用程序的运行时,包括 Dalvik 虚拟机和提供 Android 特定功 能的核心库。为了在移动设备上使用,将其设计成为了小巧而高效的运行时。 ● 一个把系统服务隐式地显示给应用层的应用程序框架,包括窗口管理器、位置管理器、数 据库、电话和传感器。 ● 一个用来承载和启动应用程序的用户界面框架。 ● 一套核心的预装应用程序。 ● 用来开发应用程序的软件开发包,包括工具、插件和文档。 真正使 Android 引人注目的是它的开放理念,这就保证了用户界面或者本地应用程序的所有不 足之处都可以通过编写一个扩展或者替代品来弥补。Android 为开发人员提供了一个完全按照对样 式、观感和功能的设想来设计手机界面和应用程序的机会。 1.4 原生 Android 应用程序 Android 手机通常都带有一套预装的通用应用程序,它们是 Android 开源项目(Android Open Source Project,AOSP)的一部分,包括但不限于以下几种: ● 一个电子邮件客户端。 ● 一个 SMS 管理应用程序。 ● 一个完整的个人信息管理(personal information management,PIM)套件,包括日历和联系人 列表。 ● 一个基于 WebKit 的 Web 浏览器。 ● 一个音乐播放器和图片查看器。 ● 一个照相机和视频录制应用程序。 ● 一个计算器。 ● 一个主屏幕。 ● 一个闹钟。 许多 Android 设备还提供了以下的 Google 移动应用程序: ● 用来下载第三方 Android 应用程序的 Google Play Store。 ● 一个功能丰富的移动 Google 地图应用程序,包括街道浏览(StreetView)、驾驶导航(driving direction)、turn-by-turn 导航、卫星观察(satellite view)和交通路况(traffic conditions)。 ● Gmail 邮件客户端。 第 1 章 Android 简 介 5 ● Google Talk 即时消息客户端。 ● YouTube 视频播放器。 原生应用程序存储和使用的数据(如联系人详细信息)也可以被第三方应用程序使用。与之相似, 你所编写的应用程序也可以处理像来电这样的事件。 新的 Android 手机上的可用应用程序可能会根据硬件制造商和(或)手机运营商或发行商的不同 而有所不同。 Android 的开源本质意味着运营商和 OEM 可以定制用户界面和与每个 Android 设备捆绑在一起 的应用程序。一些 OEM 已经这么做了,比如 HTC 的 Sense、Motorola 的 MotoBlur 和 Samsung 的 TouchWiz。 需要注意,兼容设备的底层平台和 SDK 在各个 OEM 和运营商之间是一致的。用户界面的样式 和观感可能有所变化,但是应用程序在所有彼此兼容的 Android 设备中的功能是一样的。 1.5 Android SDK 的特征 作为一个开发环境,Android 最吸引人之处在于它提供的 API。 作为一个与应用程序无关的平台,Android 允许你创建一些类似于本地应用程序的应用程序。 下面的列表选取了一些最值得注意的 Android 特征: ● 用于电话或者数据传输的 GSM、EDGE、3G、4G 和 LTE 网络,允许接打电话或者收发 SMS 信息,还允许在移动网络中发送或者检索数据。 ● 为像 GPS 和基于网络的位置检测这样的基于位置的服务设计了详尽的 API。 ● 完全支持能够把地图控件集成到用户界面中的应用程序。 ● 可以访问 Wi-Fi 硬件和进行点对点连接。 ● 完全的多媒体硬件控制,包括使用摄像头和麦克风进行回放和录制。 ● 用于使用加速计、罗盘和气压表等传感器硬件的 API。 ● 用于使用蓝牙技术和 NFC 硬件进行点对点(P2P)数据传输的库。 ● IPC 消息传递。 ● 用于联系人、社交网络、日历和多媒体的共享数据存储和 API。 ● 后台服务、应用程序和进程。 ● 主屏幕 Widget 和 Live Wallpaper ● 把应用程序搜索结果集成到系统搜索中的功能。 ● 一个集成的基于 WebKit 的开源 HTML5 浏览器。 ● 专为移动设备进行优化的硬件加速图形,包括一个基于路径的 2D 图形库以及对使用 OpenGL ES 2.0 的 3D 图形的支持。 ● 通过动态资源框架实现本地化。 ● 支持重用应用程序组件和取代本地应用程序的应用程序框架。 1.5.1 访问硬件(包括摄像头、GPS 和传感器) Android 包含了用来简化那些涉及设备硬件开发的 API 库。这些 API 库可以保证不必为不同的 Android 4 高级编程(第 3 版) 6 设备创建软件的特殊实现,因此,创建的 Android 应用程序就可以像预料中的那样运行在所有支持 Android 软件栈的设备上。 Android SDK 包含了针对基于位置的服务硬件(如 GPS)、摄像头、音频、网络连接、Wi-Fi、蓝 牙、传感器(包括加速计)、NFC、触摸屏和电源管理的 API。第 12 章以及第 15 章~第 17 章将详细 讨论 Android 的一些硬件 API 的潜在用途。 1.5.2 使用 Wi-Fi、蓝牙技术和 NFC 进行数据传输 Android 为设备之间的数据传输提供了丰富的支持,其中包括蓝牙技术、Wi-Fi Direct 和 Android Beam。根据需要进行数据传输的设备,可以灵活选用这些技术,从而能够开发出具有创新性的协作 应用程序。 不止如此,Android 还为管理网络连接、蓝牙连接和 NFC 的标签读取提供了 API。 1.5.3 地图、地理编码和基于位置的服务 嵌入的地图支持使你可以开发出很多利用了 Android 设备的移动性的基于地图的应用程序。 Android 允许在设计的用户界面中包含交互式的 Google 地图,因此可以通过程序对地图进行控制, 还可以使用 Android 丰富的图形库对地图进行注释。 Android 的基于位置的服务通过管理如 GPS 和Google 的基于网络的定位技术来确定设备当前的 位置。这些服务对特定的位置检测技术进行了抽象,从而使你可以指定最低要求(例如,精度或者花 费),而不是选择特定技术。它也意味着,不管手机设备支持什么样的技术,基于位置的应用程序都 会正常运行。 为了把地图和位置联系起来,Android 包含了一个用于地理编码(geocoding)和逆地理编码(reverse geocoding)的 API,从而可以使你找到一个地址所对应的地图坐标和一个地图位置所对应的地址。 1.5.4 后台服务 Android 支持当应用程序不活动时,在后台运行应用程序和服务。 现代的手机和平板电脑本质上是多功能的设备,然而,它们有限的屏幕尺寸以及采用的交互模 式使得一般情况下只能有一个交互式应用程序是可见的。不支持后台执行的平台会限制那些不需要 你持续关注的应用程序的生存能力。 后台服务允许你构建一些不可见的应用程序组件,它们不需要与用户进行直接交互就能自动执 行处理操作。后台执行允许应用程序被事件驱动,并且能够支持定期更新,这就非常适用于监控游 戏的得分或者市场价格,生成基于位置的警告,或者划分来电和 SMS 消息的优先级并进行预先 筛选。 通知是以前移动设备提醒用户在后台应用程序中发生的事件的标准方式。使用通知管理器,可 以触发音频报警,引起震动,使设备的 LED 闪烁以及控制状态栏的通知图标。 第 16 章将详细介绍如何使用 Android 提供的通信 API。 第 13 章将详细讨论如何使用地图、地理编码器(geocoder)和基于位置的服务。 第 1 章 Android 简 介 7 1.5.5 使用 SQLite 数据库进行数据存储和检索 对于一个存储空间有限的小型设备而言,快速且高效的数据存储和检索功能是很重要的。 Android 通过 SQLite 为每一个应用程序提供了一个轻量级的关系数据库。应用程序可以利用这 个托管的关系数据库引擎来安全高效地存储数据。 默认情况下,每一个应用程序的数据库都放在一个沙盒(sandbox)中,即它的内容只对创建它的 应用程序可见。但是,Content Provider 提供了一种托管这些应用程序的数据库共享的机制,并为应 用程序抽象了底层数据源。 1.5.6 共享数据和应用程序间通信 Android 使用多种技术来实现应用程序间的数据共享,主要是 Intent 和 Content Provider。 Intent 提供了一种在应用程序内部和应用程序之间传递消息的机制。使用 Intent,可以在系统范 围内向其他应用程序广播一种期望的动作(例如,拨号或者编辑联系人),来让它们进行处理。使用 Intent 还可以将自己的应用程序注册为接收这些消息或者执行用户请求的动作。 Content Provider 是一种将安全的托管访问权限授予应用程序的私有数据库的方式。原生应用程 序(如联系人管理器)的数据存储都作为 Content Provider 提供,这样就可以在自己的应用程序中读取 或者修改这些存储的数据。 1.5.7 使用 Widget 和 Live Wallpaper 增强主屏幕 通过使用 Widget 和 Live Wallpaper,可以创建一些动态的应用程序组件,然后可以利用它们在 应用程序内提供一个窗口,或者在主屏幕上直接提供及时而有用的信息。 通过为用户提供一种在主屏幕上直接与应用程序交互的方法可以提高用户的参与度,使他们可 以立即访问感兴趣的信息,而不需要打开应用程序,而且上述功能在主屏幕上提供了一种访问应用 程序的快捷方式。 1.5.8 广泛的媒体支持和 2D/3D 图形 越来越大的屏幕,越来越清晰的显示和越来越高的分辨率,让手机变成了理所当然的多媒体设 在第 9 章和第 10 章中将学习如何使用通知以及如何高效地利用后台服务。 第 8 章将详细地讨论数据库和 Content Provider。 Intent 是 Android 中的一个基本组件,第 5 章将进行详细阐述。 第 8 章详细地讲解了 Content Provider,包括原生提供器, 并说明了如何创建和使 用自己的提供器。 第 14 章将讨论如何为主屏幕创建应用程序组件。 Android 4 高级编程(第 3 版) 8 备。为了能够充分利用硬件功能,Android 为使用 2D 画布绘图和使用 OpenGL 的 3D 图形渲染提供 了相应的图形库。 Android 也提供了处理静态图像、视频和音频文件的综合库,该库可处理 MPEG4、H.264、HTTP Live Streaming、VP8、WEBP、MP3、AAC、AMR、HLS、JPG、PNG 和 GIF 格式的图像、视频和 音频文件。 1.5.9 Cloud to Device Messaging Android Cloud to Device Messaging(C2DM)服务为开发人员提供了一种根据服务器端推送创建 事件驱动应用程序的有效机制。 通过使用 C2DM,可以在移动应用程序和服务器之间创建一个轻量级的、总是在线的连接,从 而能够实时地将少量的数据直接发送到设备上。 C2DM 服务通常用于向应用程序提醒服务器上可用的新数据,从而减少对轮询的需要,降低应 用程序更新对电池的影响,并改善这些更新的时间线。 1.5.10 优化的内存和进程管理 与 Java 和.NET 一样,Android 使用自己的运行时和虚拟机来管理应用程序内存。但与 Java 和.NET 不同的是,Android 运行时还管理着进程的生存期。Android 根据需要对进程进行暂停和结 束操作来为更高优先级的应用程序释放资源,从而保证高优先级应用程序的及时响应。 在此上下文环境中,正在与用户进行交互的应用程序将具有最高的优先级。既要保证能对你的 应用程序随时进行暂停或结束操作,又要保证应用程序能够保持实时响应与更新,以及必要时在后 台进行更新或重新启动——这在诸如手机平台等不允许应用程序控制其生存期的应用环境中是非常 重要的。 1.6 开放手机联盟简介 开放手机联盟(Open Handset Alliance,OHA)是由 80 多家技术公司组成的一个组织,这些公司 包括手机制造商、移动运营商、软件开发商、半导体公司和商业公司。值得注意的是,诸如 Samsung、 Motorola、HTC、T-Mobile、Vodafone、ARM 和 Qualcomm 等众多著名的移动技术公司也都纷纷加 入了开放手机联盟。用他们自己的话说,OHA 代表的是: 对开放原则的承诺、对未来的共同憧憬,以及把这种憧憬变为现实的具体实施计划。它可以有 力推动移动设备的创新,并且向消费者提供内容更丰富、资费更低廉且更美妙的移动体验。 www.openhandsetalliance.com 第 11 章将详细讲述 2D 和 3D 图形库,而第 15 章则涵盖了 Android 的媒体管理库 的相关内容。 第 3 章将介绍更多关于 Android 应用程序生命周期的相关内容。 第 1 章 Android 简 介 9 OHA 希望提供一个有利于移动开发创新的平台,该平台具有更快的速度和更高的品质,而且不 需要软件开发人员或者设备制造商支付费用,并希望以此向消费者提供更好的移动软件体验。 1.7 运行 Android 的环境 第一款 Android 手机——T-Mobile G1—— 于 2008 年 10 月在美国上市。到 2012 年年初,在超过 123 个国家的 231 个不同的运营商网络中已经有 3 亿多个 Android 兼容手机被售出,它们是由超过 39 个手机制造商生产的。 Android 手机操作系统并不是为某种特定的硬件实现而创建的,它的设计是面向各种各样的硬 件平台的,这些平台包括智能手机、平板电脑和电视等。 此外,由于得益于 Android 不收取许可费和其代码的完全开放性,手机制造商生产与 Android 兼容的手机产品或其他 Android 设备的成本得以大大降低。Android 平台在创建强大的应用程序方面 有很大的优势,因此,许多人期望这种优势可以鼓励手机制造商生产定制程度越来越高的硬件。 1.8 从事移动开发的原因 现代智能手机(包含手机通话功能的多功能设备,提供了功能丰富的 Web 浏览器、摄像头、多 媒体播放器、Wi-Fi 和基于位置的服务)的出现从根本上改变了人们使用移动设备以及访问 Internet 的方式。 在许多国家,拥有手机的人数远远超出了拥有计算机的人数。全球的手机用户数已经超过了 30 亿。在 2009 年,使用手机上网的人数第一次超过了使用 PC 上网的人数。很多人确信,在 5 年内使 用手机上网的人数将稳定地超过使用 PC 上网的人数。 现代智能手机日益流行,再加上高速手机数据和 Wi-Fi 热点也越来越多,这都使得市场对高级 移动应用程序的需求越来越大。 手机的普及以及我们使用手机的方式决定了它们是与 PC 完全不同的开发平台。在包含了麦克 风、摄像头、触摸屏、位置检测和环境传感器以后,手机实际上已经成为了人的感知能力的扩展。 智能手机应用程序改变了人们使用手机的方式。这就为应用程序开发人员提供了一个独特的机 会,使他们能够创建出动态而具有吸引力的新应用程序,并使这些应用程序变成人们的生活中不可 或缺的一部分。 1.9 从事 Android 开发的原因 Android 是移动开发技术发展历程上的一个里程碑,是现代移动设备开发技术的基础上的一个 移动应用程序框架。 使用简单、强大而且开放的 SDK,无须缴纳使用许可费,具有规范的文档和日益庞大的开发社 区,Android 的上述特点为移动开发人员提供了一个可以深刻改变人们移动应用生活的绝佳机会。 开发人员进入 Android 的门槛很低: ● 不要求获得 Android 开发人员认证。 Android 4 高级编程(第 3 版) 10 ● Google Play 提供了多种选项来帮助你发布应用程序并利用应用程序获利,你的应用程序可 以不收取费用、提前收取费用或者在应用程序内收取费用。 ● 没有针对应用程序发布的批准过程。 ● 允许你完全控制自己的品牌。 从商业角度看,每天都有 850 000 个新的 Android 设备被激活,而且众多研究显示,在售出的 智能手机中,Android 手机占的份额最大。截止到 2012 年 3 月,Google Play(即原来的 Android Market) 将支持销售应用程序的服务增加到了 131 个国家,用户从 Google Play 下载应用程序的次数超过 100 亿次,而且还在以每月 10 亿次的数量增长。 1.9.1 推动 Android 普及的因素 Android 主要是面向开发人员的,因为 Google 和 OHA 坚信,只有先让开发人员能更方便地开 发出移动应用软件,才能使这些软件在消费者中广泛推广。 作为一个开发平台,Android 功能非常强大,而且易于使用,它使那些没有任何移动开发经验 的人员也可以方便快速地创建有用的应用程序。显而易见,富有吸引力的 Android 应用程序将能够 带动对可以运行这些应用程序的设备的需求,特别是当开发人员无法为 Android 以外的平台编写某 些应用程序时更是如此。 随着支持 Android 的设备越来越多,硬件能力越来越强,并且还有高级的传感器和新的开发 API 可用,可供创新的空间只会越来越大。 对系统底层细节的开放访问通常有助于平台及基于该平台所开发的应用程序的流行。Internet 本身固有的开放性和平台无关性让它在短短十年之内变成了拥有数十亿美元产业的平台。在此之 前,像 Linux 这样的开源操作系统和 Windows 操作系统所提供的强大的 API 接口使个人计算机迅速 普及至寻常百姓家,同时也使神秘的计算机编程技术得以流行。 开放并且功能强大,这保证了任何有创意的想法都可以用最小的代价来实现。 1.9.2 Android 的独到之处 前面列出很多功能,如 3D 图形和本地数据库支持,在其他移动 SDK 上也有,并且移动浏览器 也开始支持它们。 移动平台(不只是 Android,也包括它的竞争对手)的创新步伐非常迅速,所以很难精确地比较不 同平台特有的功能。下面这个不算完善的列表列出了 Android 支持、而其他所有现代的移动开发平 台可能不支持的功能: ● Google Maps 应用程序 手机上的 Google 地图已经非常流行了,Android 把 Google Map 作 为一个原子的、可重用的控件提供给开发人员。MapView 小程序允许在用户 Activity 中显 示、操作和注释 Google Map,以使用熟悉的 Google 地图接口来构建基于地图的应用程序。 ● 后台服务和应用程序 对后台应用程序和服务的完善支持允许创建使用事件驱动模型的应 用程序,当其他应用程序正在被使用时,或者手机处于待机状态时,这些应用程序将在后 台自动运行。它可能是一个流式引用播放器,也可能是一个关注股市动态的应用程序,当 投资出现重大波动的时候进行通知。它还可能是一种服务,比如可以根据当前的位置、时 间和来电人的身份来改变铃声或者音量。Android 为所有的应用程序和开发人员提供了平等 的机会。 第 1 章 Android 简 介 11 ● 共享数据和进程间通信 通过使用 Intent 和 Content Provider,Android 使应用程序可以交换 信息、执行处理和共享数据。也可以使用这些机制来利用本地 Android 应用程序提供的数据 和功能。为了降低这种开放策略带来的风险,每个应用程序的进程、数据存储和文件都是 私有的,除非使用一种完全基于权限的安全机制显式地和其他应用程序进行共享。第 18 章 将详细介绍这种机制。 ● 平等地创建所有应用程序 Android 并不会区别对待本地应用程序和第三方开发的应用程 序。这就给了消费者改变他们设备的样式和感观的前所未有的权力,允许他们使用第三方 应用程序完全取代每个本地应用程序,并且这些第三方应用程序也能访问相同的底层数据 和硬件。 ● Wi-Fi Direct 和 Android Beam 通过使用这些创新性的设备间通信 API,可以在应用程序 中提供即时的媒体共享和流式传输等功能。Android Beam 是一个基于 NFC 的 API,允许支 持近距离交互,而 Wi-Fi Direct 则为设备间的高速可靠通信提供了更大范围的点对点连接。 ● 主屏幕 Widget、Live Wallpaper 和快速搜索框 使用 Widget 和 Live Wallpaper 可以从手机 的主屏幕上创建应用程序内的窗口。快速搜索框可以将应用程序的搜索结果直接整合到手 机的搜索功能中。 1.9.3 改变移动开发格局 目前存在的移动开发平台在移动开发过程中产生了一种排外的氛围。相比之下,Android 允许、 甚至鼓励革命性的颠覆。 作为一种消费设备,Android 手机在销售的时候都会应客户的要求而预装一套新手机所必须具 备的标准的核心应用程序,但是它真正的强大之处在于可以让用户完全拥有改变手机外观、感觉和 功能的能力,这给开发者提供了一个很好的机会。 所有的 Android 应用程序都是手机产品自身的一部分,而不仅仅是运行在手机之上的沙盒中的 软件。你不必开发那些运行在低功率设备上的小屏幕版本的软件,而是可以编写那些能够改变人 们使用手机的方式的移动应用程序。 作为一个开源的开发框架,虽然 Android 仍然必须和已有的以及将来可能会出现的各种移动开 发平台竞争,但是它仍然具有自己的优势。在移动开发过程中采用免费和开放的方法,并且可以不 受限制地访问手机的资源,这对于想要在移动开发中一展拳脚的任何开发人员来说都是一个机会。 1.10 开发框架简介 了解了为什么要在 Android 平台上进行开发之后,现在开始讨论如何开发 Android 应用程序。 Android 应用程序使用 Java 作为编程语言进行编写,但不是用传统的 Java 虚拟机执行,而是用 一个定制的称为 Dalvik 的虚拟机执行。 本章的后面部分将介绍 Android 的框架,先从对 Android 软件栈技术的解释开始 , 然后对 SDK 中包含的内容和 Android 库进行简单介绍,最后简单地了解一下 Dalvik 虚拟机。 Android 4 高级编程(第 3 版) 12 每个 Android 应用程序都运行在它自己的 Dalvik 实例的一个进程中,它把内存管理和进程管理 的所有工作都交给 Android 运行时进行处理,Android 运行时在必要的时候会暂停和结束进程,从而 更有效地管理资源。 Dalvik 和 Android 运行时位于一个 Linux 内核之上,由该 Linux 内核来处理低级的硬件交互, 包括驱动程序和内存管理,同时有一套 API 来提供所有对底层服务、功能和硬件的访问。 1.10.1 开发包中的资源 Android 软件开发包(software development kit,SDK)包含了开发、测试和调试 Android 应用程序 所需的所有东西: ● Android API SDK的核心是 Android API 库,它向开发人员提供了对 Android 栈进行访问 的方法。Google 也使用相同的库来开发原生 Android 应用程序。 ● 开发工具 为了让 Android 源代码变成可执行的 Android 应用程序,SDK 提供了多个开发工 具供编译和调试应用程序时使用。第 2 章将更加详细地讲述开发工具的相关内容。 ● Android 虚拟设备管理器和模拟器 Android 模拟器是一个完全交互式的移动设备模拟器, 并有多个皮肤可供选择。模拟器运行在模拟设备硬件配置的 Android 虚拟设备中。通过使用 模拟器,可以了解应用程序在实际的 Android 设备上的外观和运行情况。所有 Android 应用 程序都运行在 Dalvik VM 中,所以软件模拟器是一个非常好的开发环境。事实上,由于它 的硬件无关性,它提供了比任何单一的硬件实现都更好的独立测试环境。 ● 完整的文档 SDK中包含了大量代码级的参考信息,详细地说明了每个包和类中都包含什 么内容以及如何使用它们。除了代码文档之外,Android 的参考文档和开发指南还解释了如 何开始进行开发,并详细地解释了 Android 开发背后的基本原理,此外还强调了最佳开发实 践,并深入阐述了关于框架的主题。 ● 示例代码 Android SDK 包含了一些示例代码集,它们解释了使用 Android 的某些可能性, 以及一些用来强调如何使用每一个 API 功能的简单程序。 ● 在线支持 Android 迅速拥有了一个生机勃勃的开发社区。Google Groups(http://developer. android.com/resources/community-groups.html#ApplicationDeveloperLists)是一个活跃的 Android 开发论坛,也是 Google 的Android 开发人员经常去的论坛。Stack OverFlow (www.stackoverflow. com/questions/tagged/android)也是交流 Android 问题的一个热点区域,并且从那里可以找到 很多入门级问题的详细解答。 Android 针对那些习惯于使用流行的 Eclipse IDE 的移动开发人员发布了 Android Development Tools(ADT)插件来简化工程创建,并把 Android 模拟器以及构建和调试工具紧密地集成到了 Eclipse 中。第 2 章将详细介绍 ADT 插件的功能。 1.10.2 理解 Android 软件栈 简单地说,Android软件栈就是通过一个应用程序框架提供一个Linux内核和一个C/C++库集合, 而该应用程序框架为运行时和应用程序提供服务,并对它们进行管理。Android 软件栈由图 1-1 中的 元素组成。 第 1 章 Android 简 介 13 ● Linux 内核 核心服务(包括硬件驱动程序、进程和内存管理、安全、网络和电源管理)都由 一个 Linux 2.6 内核处理。内核还在硬件和软件栈的其他部分之间提供了一个抽象层。 应用层 原生应用程序 (联系人、地图、浏览器等) 第三方应用程序 开发应用程序 应用程序框架 基于位置的服务 内容提供器 窗口管理器 活动管理器 包管理器 电话服务 蓝牙技术/NFC/ Wi-Fi Direct 通知 视图 资源管理器 库 图形库 多媒体库 外观管理器 Android 运行时 Android 库 Dalvik 虚拟机 Linux 内核 硬件驱动程序 (USB、显 示 屏 、蓝 牙 等 ) 电源管理 进程管理 内存管理 图 1-1 ● 库 在内核之上,Android 包含了各种 C/C++核心库(例如 libc 和 SSL),以及: ● 用来回放音频和视频媒体的媒体库; ● 用来管理显示的外观管理器; ● 包含用于 2D 和 3D 图形的 SGL 和 OpenGL 的图形库; ● 用于本地数据库支持的 SQLite; ● 用于集成 Web 浏览器和 Internet 安全的 SSL 和 WebKit。 ● Android 运行时 Android 运行时可以让一个 Android 手机从本质上与一个移动 Linux 实现 区分开来。由于 Android 运行时包含了核心库和 Dalvik 虚拟机,因此,它是向应用程序提 供动力的引擎,它和库一起形成了应用程序框架的基础。 ● 核心库 虽然 Android 应用程序开发使用的是 Java 语言,但 Dalvik 并不是一个 Java 虚 拟机。Android 核心库提供了 Java 核心库以及 Android 特定库可用的大部分功能。 ● Dalvik 虚拟机 Dalvik 是一个基于寄存器的虚拟机,它已经被优化从而确保一个设备可 以高效地运行多个实例。它依赖 Linux 内核进行线程和底层内存管理。 ● 应用程序框架 应用程序框架提供了用来创建 Android 应用程序的类。它还对硬件访问提供 了一般抽象,并管理用户界面和应用程序资源。 ● 应用层 所有的应用程序,包括原生的和第三方的,都在应用层上使用相同的库进行构建。 应用层运行在 Android 运行时内,并且使用了应用程序框架中可用的类和服务。 Android 4 高级编程(第 3 版) 14 提示: 本书主要介绍的是如何使用 SDK 编写 Dalvik 支持的应用程序, NDK 开发不在本书 讨论范围内。如果你更倾向于 NDK 开发,想要探索 Android 的 Linux 内核和 C/C++底 层细节以及修改 Dalvik 或者底层的其他东西,那么建议你看一下 Android Internals Google Group,网址是 http://groups.google.com/group/android-internals。 虽然本书推荐在需要时尽量使用 NDK,但是并没有详细讨论如何使用 NDK。 1.10.3 Dalvik 虚拟机 Android 的一个关键元素就是 Dalvik 虚拟机。Android 使用定制的虚拟机来保证多个实例可以高 效地运行在一个设备上,而不是使用传统的 Java 虚拟机,比如 Java ME。 Dalvik 虚拟机使用设备的底层 Linux 内核来处理基本的功能,包括安全、线程以及进程和内存 管理。编写直接运行在底层 Linux OS 上的 C/C++应用程序也是可以的,但大部分情况下没有这个 必要。 如果你的应用程序需要利用 C/C++的速度和效率,则可以使用 Android 提供的 Native Development Kit(NDK)。设计 NDK 的目的是允许使用 libc 和 libm 库以及对 OpenGL 的本地访问创 建 C++库。 所有 Android 硬件和系统服务访问都是使用作为中间层的 Dalvik 来加以管理的。通过使用一个 VM 来承载应用程序的执行,开发人员就可以获得一个抽象层来保证他们永远都不需要考虑特定的 硬件实现。 Dalvik VM 执行 Dalvik 可执行文件,这种优化后的格式可以保证能最小限度地占用内存。使用 SDK 提供的工具,可以把 Java 语言编译的类转换为.dex 可执行文件。 1.10.4 Android 应用程序架构 Android 架构鼓励组件重用,允许在规定的安全限制的访问管理之下向其他的应用程序发布和 共享 Activity、Service 及数据。 使用可以替换联系人管理器或者电话拨号器的机制,同样可以公开自己的应用程序组件,让其 他开发人员在它们的基础上构建应用程序,例如创建新的 UI 前端和功能扩展。 下面的应用程序服务是所有 Android 应用程序的架构基础,它们提供了常用软件都会使用到的 框架: ● Activity Manager 和 Fragment Manager 分别控制 Activity 和 Fragment 的生命周期,包括 对第 3 章中描述的 Activity 栈进行管理。 ● 视图(View) 用来为 Activity 和 Fragment 构建用户界面,第 4 章将讲述相关内容。 ● Notification Manager(通知管理器) 如第 10 章所述,提供了一种一致的和非打断性的机制 来通知用户。 ● Content Provider(内容提供器) 如第 8 章所述,让应用程序可以共享数据。 第 2 章将介绍如何创建 Dalvik 可执行文件的更多相关内容。 第 1 章 Android 简 介 15 ● Resource Manager(资源管理器) 如第 3 章所述,支持像字符串和图形这样的非代码资源的 具体化。 ● Intent 如第 5 章所述,提供了一种在应用程序及其组件之间传输数据的机制。 1.10.5 Android 库 Android 提供了大量的 API 供开发使用。要想了解 Android SDK 中包含的包的完整列表,可以 参考 http://developer.android.com/reference/packages.html 上提供的文档。 Android 是针对大量的移动硬件设计的,所以要注意,一些高级或可选 API 的适用性及实现可 能因 Android 设备而异。 Intent 和 Broadcast Receiver 本章内容 ● Intent 简介 ● 使用隐式和显式 Intent 启动 Activity、子 Activity 和 Service ● 使用 Linkify ● 使用 Broadcast Intent 广播事件 ● 使用 Pending Intent ● Intent Filter 和 Broadcast Receiver 简介 ● 使用 Intent Filter 扩展应用程序的功能 ● 监听 Broadcast Intent ● 监视设备状态改变 ● 在运行时管理 manifest Receiver 在本章,将会了解 Intent。Intent 可能是 Android 开发中最独特、最重要的概念。你将学习如何 使用它们在应用程序内和应用程序之间广播数据,以及如何通过监听它们来检测到系统状态的变化。 你还将学习如何定义隐式和显式的 Intent,以便使用运行时迟绑定来启动 Activity 或 Service。 通过使用隐式的 Intent,将学会如何请求在一条数据上执行某个动作,让 Android 决定哪个应用程序 组件可以最好地服务该请求。 Broadcast Intent 用来在系统范围内公布应用程序事件。你将学到如何传递这些广播以及如何使 用 Broadcast Receiver 来接收它们。 5.1 Intent 简介 Intent 是一种消息传递机制,可以在应用程序内使用,也可以在应用程序间使用。Intent 可以 用于: ● 使用类名显式启动一个特定的 Service 或 Activity。 5 第 章 Android 4 高级编程(第 3 版) 146 提示:这一部分给出的说明是关于启动新的 Activity 的,但是这些相同的规则通 常也适用于 Service。第 9 章将会讲述关于启动(和创建)Service 的详细信息。 ● 启动 Activity 或 Service 来执行一个动作的 Intent,通常需要使用特定的数据,或者对特定的 数据执行动作。 ● 广播某个事件已经发生。 Intent 支持 Android 设备上安装的任意应用程序组件之间的交互,不管它们是哪个应用程序的一 部分都是如此。这就把设备从一个包含相互独立的组件集合的平台变成了一个互联的系统。 Intent 最常见的一个用法是显式地(通过指定要装载的类)或者隐式地(通过请求对一条数据执行 某个动作)启动新的 Activity。在后一种情况中,动作不一定由调用应用程序中的 Activity 执行。 Intent 也可以用来在系统范围内广播消息。应用程序可以注册一个 Broadcast Receiver 来监听和 响应这些广播的 Intent。这样就可以基于内部的、系统的或者第三方应用程序的事件创建事件驱动 的应用程序。 Android 通过广播 Intent 来公布系统事件,比如网络连接状态或者电池电量的改变。本地 Android 应用程序(如拨号程序和 SMS 管理器)简单地注册监听特定的广播 Intent(例如“来电”或者“收到 SMS 消息”)并作出相应的响应的组件。因此,可以通过注册监听相同 Intent 的 Broadcast Receiver 来替换 许多本地应用程序。 使用 Intent 来传播动作(甚至在同一个应用程序内传播动作),而不是显式地加载类,这是一条基 本的 Android 设计原则。它鼓励组件之间的分离,允许无缝地替换应用程序元素。它还提供了一个 简单的用于扩展应用程序功能的模型的基础。 5.1.1 使用 Intent 来启动 Activity Intent 最常见的用途是绑定应用程序组件,并在应用程序之间进行通信。Intent 用来启动 Activity, 允许创建不同屏幕的一个工作流。 要创建并显示一个 Activity,可以调用 startActivity,并传递给它一个 Intent,如下面的代码所示: startActivity(myIntent); startActivity 方法会查找并启动一个与 Intent 最匹配的 Activity。 可以构造 Intent 来显式地指定要打开的 Activity 类,或者包含一个目标 Activity 必须执行的动 作。在后面一种情况中,运行时将会使用一个称为“Intent 解析(intent resolution)”的过程来动态选 择 Activity。 如果使用 startActivity,则在新启动的 Activity 完成之后,应用程序不会接收到任何通知。要想 跟踪来自子 Activity 的反馈,可以使用本章后面详述的 startActivityForResult 方法。 1. 显式启动新 Activity 在第 3 章中已经知道了应用程序是由多个相互关联的屏幕——Activity——组成的,它们必须包 含在应用程序的 manifest 文件中。为在它们之间进行过渡,经常需要显式地指定要打开哪个 Activity。 要显式地选择要启动的 Activity 类,可以创建一个新的 Intent 来指定当前 Activity 的上下文以及 第 5 章 Intent 和 Broadcast Receiver 147 要启动的 Activity 的类。然后把这个 Intent 传递给 startActivity,如程序清单 5-1 所示。 程序清单 5-1 显式启动一个 Activity Intent intent = new Intent(MyActivity.this, MyOtherActivity.class); startActivity(intent); 代码片段 PA4AD_Ch05_Intents/src/MyActivity.java 在调用 startActivity 之后,新的 Activity(本例中是 MyOtherActivity)将会被创建、启动和恢复运 行,它会移动到 Activity 栈的顶部。 调用新 Activity 的 finish 或按下设备的返回按钮将关闭该 Activity,并把它从栈中移除。或者, 开发人员可以通过调用 startActivity 导航到其他 Activity。注意,每次调用 startActivity 时,会有一个 新的 Activity 添加到栈中,而按下后退按钮(或调用 finish)则依次删除每个 Activity。 2. 隐式的 Intent 和运行时迟绑定 隐式的 Intent 提供了一种机制,可以让匿名的应用程序组件响应动作请求。这意味着可以要求 系统启动一个可执行给定动作的 Activity,而不必知道需要启动哪个应用程序或 Activity。 例如,如果希望让用户从应用程序中打电话,那么可以实现一个新的拨号程序,也可以使用一 个隐式的 Intent 来请求一个在电话号码(表示为一个 URI)上执行动作(拨号)。 if (somethingWeird && itDontLookGood) { Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-2368")); startActivity(intent); } Android 会解析这个 Intent,并启动一个新的 Activity,该 Activity 会提供对这个电话号码进行拨 号的动作——在这种情况中,通常是 Phone Dialer。 当构建一个新的隐式的 Intent 时,需要指定一个要执行的动作,另外,也可以提供执行那个动 作需要的数据的 URI。还可以通过向 Intent 添加 extra 来向目标 Activity 发送额外的数据。 Extra 是一种向 Intent 附加基本类型值的机制。可以在任何 Intent 上使用重载后的 putExtra 方法 来附加一个新的名称/值对(NVP),以后在启动的 Activity中使用对应的get [type] Extra方法来检索它。 Extra 作为一个 Bundle 对象存储在 Intent 中,可以使用 getExtras 方法检索。 当使用这个 Intent 来启动一个 Activity 时,Android 将在运行时把它解析为最适合在指定的数据 类型上执行所需动作的类。这就意味着可以创建使用其他应用程序功能的项目,而不必提前确切知 道是哪个应用程序提供了这种功能。 如果多个 Activity 都能够执行指定的动作,则会向用户呈现各种选项。本章后面将详细介绍, Intent 解析过程是通过分析注册的 Broadcast Receiver 完成的。 许多本地应用程序都提供了能够对特定的数据执行动作的 Activity。第三方应用程序(包括你自 己的应用程序)也可以通过注册来支持新的动作,或者提供本地动作的替换提供器。本章后面的部分 将会介绍一些本地 Activity,以及如何注册自己的 Activity 来支持它们。 可从 wrox.com 下载源代码 Android 4 高级编程(第 3 版) 148 3. 确定 Intent 能否解析 在自己的应用程序中利用第三方应用程序的 Activity 和 Service 是十分方便的,但是,你无法保 证用户设备上安装了特定的某个应用程序,或者设备上有能够处理你的请求的应用程序。 因此,在调用 startActivity 之前,确定调用是否可以解析为一个 Activity 是一种很好的做法。 通过调用 Intent 的 resolveActivity 方法,并向该方法传入包管理器,可以对包管理器进行查询, 确定是否有 Activity 能够启动以响应该 Intent,如程序清单 5-2 中所示。 程序清单 5-2 隐式启动一个 Activity if (somethingWeird && itDontLookGood) { // Create the impliciy Intent to use to start a new Activity. Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-2368")); // Check if an Activity exists to perform this action. PackageManager pm = getPackageManager(); ComponentName cn = intent.resolveActivity(pm); if (cn == null) { // If there is no Activity available to perform the action // Check to see if the Google Play Store is available. Uri marketUri = Uri.parse("market://search?q=pname:com.myapp.packagename"); Intent marketIntent = new Intent(Intent.ACTION_VIEW).setData(marketUri); // If the Google Play Store is available, use it to download an application // capable of performing the required action. Otherwise log an // error. if (marketIntent.resolveActivity(pm) != null) startActivity(marketIntent); else Log.d(TAG, "Market client not available."); } else startActivity(intent); } 代码片段 PA4AD_Ch05_Intents/src/MyActivity.java 如果没有找到 Activity,可以选择禁用相关的功能(和相关的用户界面控件),也可以引导用户找 到 Google Play Store 中合适的应用程序。要注意的是,Google Play 并不是在所有的设备和模拟器上 都可用的,所以最好也对此进行检查。 4. 从 Activity 返回结果 通过 startActivity 启动的 Activity 独立于其父 Activity,并且在关闭时不会提供任何反馈。 当需要反馈时,可以启动一个 Activity 作为另一个 Activity 的子 Activity,用它向父 Activity 传递 结果。子 Activity 只是以一种不同的方式启动的 Activity。因此,必须在应用程序的 manifest 文件中注 册它们,就像其他任何 Activity 一样。在 manifest 文件中注册的任何 Activity 都可以作为子 Activity 可从 wrox.com 下载源代码 第 5 章 Intent 和 Broadcast Receiver 149 打开,包括系统 Activity 或第三方应用程序的 Activity。 当子 Activity 结束时,它会触发调用 Activity 内的事件处理程序 onActivityResult。对于一个 Activity 为另一个 Activity 提供数据输入(比如用户从一个列表中选择某一项)的情况,子 Activity 特 别适用。 启动子 Activity startActivityForResult 的工作方式和 startActivity 相似,但是有一个重要的区别。除了传入显式 或隐式 Intent 来决定启动哪个 Activity 以外,还需要传入一个请求码。这个值将在后面用于唯一标 识返回了结果的子 Activity。 程序清单 5-3 显示了用于显式启动一个子 Activity 的代码。 程序清单 5-3 显式启动一个子 Activity 以返回结果 private static final int SHOW_SUBACTIVITY = 1; private void startSubActivity() { Intent intent = new Intent(this, MyOtherActivity.class); startActivityForResult(intent, SHOW_SUBACTIVITY); } 代码片段 PA4AD_Ch05_Intents/src/MyActivity.java 类似于常规 Activity,可以隐式或显式地启动子 Activity。程序清单 5-4 通过使用隐式 Intent 启 动一个新的子 Activity 来选取联系人。 程序清单 5-4 隐式启动一个子 Activity 以返回结果 private static final int PICK_CONTACT_SUBACTIVITY = 2; private void startSubActivityImplicitly() { Uri uri = Uri.parse("content://contacts/people"); Intent intent = new Intent(Intent.ACTION_PICK, uri); startActivityForResult(intent, PICK_CONTACT_SUBACTIVITY); } 代码片段 PA4AD_Ch05_Intents/src/MyActivity.java 返回结果 当准备好返回子 Activity 时,可以在调用 finish 以前调用 setResult,以便向调用 Activity 返回一 个结果。 setResult 方法有两个参数:结果码和表示为 Intent 的结果数据本身。 结果码是运行子 Activity 的结果——通常是 Activity.RESULT_OK 或者 Activity.RESULT_ CANCELED。在某些环境下,当 OK 和 CANCELED 不足以精确描述可用的返回结果时,可能希望 使用自己的响应码(response code)来处理应用程序特定的选择;setResult 支持任意的整数值。 作为结果返回的 Intent 通常包含某段内容(比如选择的联系人、电话号码或媒体文件)的 URI 和 用于返回附加信息的一组 extra。 可从 wrox.com 下载源代码 可从 wrox.com 下载源代码 Android 4 高级编程(第 3 版) 150 提示:如果子 Activity 非正常地关闭或者在关闭之前没有指定结果码,那么结果 码就是 Activity.RESULT_CANCELED。 程序清单 5-5 来自于一个子 Activity 的 onCreate 方法,展示了一个 OK 按钮和一个 Cancel 按钮 如何向调用它的 Activity 返回不同的结果。 程序清单 5-5 从子 Activity 返回结果 Button okButton = (Button) findViewById(R.id.ok_button); okButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { long selected_horse_id = listView.getSelectedItemId(); Uri selectedHorse = Uri.parse("content://horses/" + selected_horse_id); Intent result = new Intent(Intent.ACTION_PICK, selectedHorse); setResult(RESULT_OK, result); finish(); } }); Button cancelButton = (Button) findViewById(R.id.cancel_button); cancelButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { setResult(RESULT_CANCELED); finish(); } }); 代码片段 PA4AD_Ch05_Intents/src/SelectHorseActivity.java 如果用户通过按下硬件返回键关闭 Activity,或者在调用 finish 以前没有调用 setResult,那么结 果码将被设为 RESULT_CANCELED,结果 Intent 将被设为 null。 处理子 Activity 结果 当一个子 Activity 关闭的时候,它会触发其调用 Activity 的 onActivityResult 事件处理程序。可 以通过重写这个方法来处理从子 Activity 返回的结果。 onActivityResult 接收多个参数: ● 请求码 在启动正在返回的子 Activity 时使用的请求码。 ● 结果码 子 Activity 设置的结果码,用来说明其结果。它可以是任何整数值,但是一般情况 下都是 Activity.RESULT_OK 或者 Activity.RESULT_CANCELED。 ● 数据 Intent 用来包装所有返回的数据。根据子 Activity 目标的不同,它可能会包含代表选 定内容的 URI。另外,子 Activity 可以在返回的数据 Intent 内以 extra 的形式返回信息。 程序清单 5-6 实现了一个 Activity 的 onActivityResult 事件处理程序。 可从 wrox.com 下载源代码 第 5 章 Intent 和 Broadcast Receiver 151 提示:稍后将会介绍 Intent Filter,以及如何把自己的 Activity 注册为这些动作的 处理程序。 程序清单 5-6 实现一个 onActivityResult 事件处理程序 private static final int SELECT_HORSE = 1; private static final int SELECT_GUN = 2; Uri selectedHorse = null; Uri selectedGun = null; @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch(requestCode) { case (SELECT_HORSE): if (resultCode == Activity.RESULT_OK) selectedHorse = data.getData(); break; case (SELECT_GUN): if (resultCode == Activity.RESULT_OK) selectedGun = data.getData(); break; default: break; } } 代码片段 PA4AD_Ch05_Intents/src/MyActivity.java 5. 原生 Android 动作 原生 Android 应用程序也可以使用 Intent 来启动 Activity 和子 Activity。 下面的不完整列表列出了某些原生动作,它们都是 Intent 类中的静态字符串常量。在创建隐式 的Intent来启动应用程序内的Activity或者子Activity的时候,可以使用这些动作(称为ActivityIntent)。 ● ACTION_ALL_APPS 打开一个列出所有已安装应用程序的 Activity。通常,此操作由启 动器处理。 ● ACTION_ANSWER 打开一个处理来电的 Activity,通常这个动作是由本地电话拨号程序 进行处理的。 ● ACTION_BUG_REPORT 显示一个可以报告 bug 的 Activity,通常由本地 bug 报告机制 处理。 可从 wrox.com 下载源代码 Android 4 高级编程(第 3 版) 152 提示:除了这些 Activity 动作之外,Android 还包含了很多广播动作, 它们用来创 建广播 Intent 以公布系统事件。本章后面将对这些广播动作进行详细讲述。 ● ACTION_CALL 打开一个电话拨号程序,并立即使用 Intent 的数据 URI 所提供的号码拨 打一个电话。此动作只应用于代替本地拨号程序的 Activity。大多数情况下,使用 ACTION_DIAL 是一种更好的方式。 ● ACTION_CALL_BUTTON 当用户按下硬件的“拨打按钮”时触发,通常会调用拨号 Activity。 ● ACTION_DELETE 启动一个 Activity,允许删除 Intent 的数据 URI 中指定的数据。 ● ACTION_DIAL 打开一个拨号程序,要拨打的号码由 Intent 的数据 URI 预先提供。默认 情况下,这是由本地 Android 电话拨号程序进行处理的。拨号程序可以规范化大部分号码样 式,例如,tel:555-1234 和 tel:(212)555 1212 都是有效的号码。 ● ACTION_EDIT 请求一个 Activity,要求该 Activity 可以编辑 Intent 的数据 URI 中的数据。 ● ACTION_INSERT 打开一个能够在 Intent 的数据 URI 指定的游标处插入新项的 Activity。 当作为子 Activity 调用的时候,它应该返回一个指向新插入项的 URI。 ● ACTION_PICK 启动一个子 Activity,它可以让你从 Intent 的数据 URI 指定的 Content Provider 中选择一个项。当关闭的时候,它应该返回所选择的项的 URI。启动的 Activity 与 选择的数据有关,例如,传递 content://contacts/people 将会调用本地联系人列表。 ● ACTION_SEARCH 通常用于启动特定的搜索 Activity。如果没有在特定的 Activity 上触发 它,就会提示用户从所有支持搜索的应用程序中做出选择。可以使用 SearchManager.QUERY 键把搜索词作为一个 Intent 的 extra 中的字符串来提供。 ● ACTION_SEARCH_LONG_PRESS 允许截获对硬件搜索键的长按操作。通常由系统处 理,以提供语音搜索的快捷方式。 ● ACTION_SENDTO 启动一个 Activity 来向 Intent 的数据 URI 所指定的联系人发送一条 消息。 ● ACTION_SEND 启动一个 Activity,该 Activity 会发送 Intent 中指定的数据。接收人需要 由解析的 Activity 来选择。使用 setType 可以设置要传输的数据的 MIME 类型。数据本身应 该根据它的类型,使用 EXTRA_TEXT 或者 EXTRA_STREAM 存储为 extra。对于 E-mail, 本地 Android 应用程序也可以使用 EXTRA_EMAIL、EXTRA_CC、EXTRA_BCC 和 EXTRA_SUBJECT 键来接收 extra。应该只使用 ACTION_SEND 动作向远程接收人(而不是 设备上的另外一个应用程序)发送数据。 ● ACTION_VIEW 这是最常见的通用动作。视图要求以最合理的方式查看 Intent的数据 URI 中提供的数据。不同的应用程序将会根据所提供的数据的 URI 模式来处理视图请求。一般 情况下,http:地址将会打开浏览器,tel:地址将会打开拨号程序以拨打该号码,geo:地址会在 Google 地图应用程序中显示出来,而联系人信息将会在联系人管理器中显示出来。 ● ACTION_WEB_SEARCH 打开一个浏览器,根据 SearchManager.QUERY 键提供的查询 执行 Web 搜索。 第 5 章 Intent 和 Broadcast Receiver 153 提示:大多数 Android 设备有至少两个电子邮件应用程序: Gmail 和 Email。当多 个 Activity 被解析为可能的动作使用者时,将会要求用户做出选择。在模拟器中,必 须先配置电子邮件客户端,然后它才能够响应建立了链接的电子邮件地址。 5.1.2 Linkify 简介 Linkify 是一个辅助类,它会自动地在 TextView 类(或者 TextView 的派生类)中通过 RegEx 模式 匹配来创建超链接。 那些匹配一个指定的 RegEx 模式的文本都将会被转换为一个可以单击的超链接,这些超链接可 以隐式使用匹配的文本作为目标 URI 来触发 startActivity(new Intent(Intent.ACTION_VIEW,uri))。 可以指定任何字符串模式来作为可单击链接处理;为了方便使用,Linkify 类提供了常见的内容 类型的预设值。 1. 原生 Linkify 链接类型 Linkify 类有一些预设值可以检测到 Web URL、电子邮件地址和电话号码,并把它们转换为链 接。要应用一个预设值,需要使用静态的 Linkify.addLinks 方法,并传入要建立链接的视图,以及以 下的一个或更多个自描述的 Linkify 类常量的位掩码(bitmask):WEB_URLS、EMAIL_ADDRESSES、 PHONE_NUMBERS 和 ALL。 TextView textView = (TextView)findViewById(R.id.myTextView); Linkify.addLinks(textView, Linkify.WEB_URLS|Linkify.EMAIL_ADDRESSES); 也可以使用 android:autoLink 属性来在一个布局内部链接视图。它支持下列一个或者多个值: none、web、email、phone 或者 all。 2. 创建定制的链接字符串 要为自己的数据建立链接,需要定义自己的 linkify 字符串,可以通过创建一个新的 RegEx 模式 来匹配希望显示为超链接的文本。 和本地类型一样,可以通过调用 Linkify.addLinks 来为目标 Text View 建立链接,只不过这次传 入的是 RegEx 模式,而不是预设的常量。也可以给它传递一个前缀,当单击链接时,该前缀将会被 添加到目标 URI 前面。 程序清单 5-7 为一个视图建立了链接,以此来支持由一个 Android Content Provider(将会在第 8 章中创建它)所提供的地震数据。注意,指定的 RegEx 模式并不是包含整个模式,而是匹配所有以 quake 开头,后接一个数字的文本,文本中可以包含空格。之后,在触发 Intent 之前把完整的模式添 加到 URI 的前面。 Android 4 高级编程(第 3 版) 154 程序清单 5-7 使用 Linkify 创建定制链接字符串 // Define the base URI. String baseUri = "content://com.paad.earthquake/earthquakes/"; // Contruct an Intent to test if there is an Activity capable of // viewing the content you are Linkifying. Use the Package Manager // to perform the test. PackageManager pm = getPackageManager(); Intent testIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(baseUri)); boolean activityExists = testIntent.resolveActivity(pm) != null; // If there is an Activity capable of viewing the content // Linkify the text. if (activityExists) { int flags = Pattern.CASE_INSENSITIVE; Pattern p = Pattern.compile("\\bquake[\\s]?[0-9]+\\b", flags); Linkify.addLinks(myTextView, p, baseUri); } 代码片段 PA4AD_Ch05_Linkify/src/MyActivity.java 注意,在这个示例中,在“quake”和一个数字之间包含空格可以返回匹配的结果,但是得到的 URI 不是有效的。通过实现和指定 Transform Filter 和 Match Filter 接口中的一个或两个,可以解决这 个问题。这两个接口将在接下来的小节中详细介绍,它们提供了对目标 URI 的结构以及匹配字符串 的定义的额外控制,具体用法如下面的框架代码所示: Linkify.addLinks(myTextView, p, baseUri, new MyMatchFilter(), new MyTransformFilter()); 3. 使用 Match Filter 通过实现 Match Filter 中的 acceptMatch 方法来向 RegEx 模式匹配添加额外的条件。当发现一个 可能的匹配时,acceptMatch 就会被触发,匹配的起始索引和结束索引(以及要搜索的全部文本)就会 作为参数传入。 程序清单 5-8 展示了一个 Match Filter 实现,它会取消那些前面紧接着一个感叹号(!)的所有 匹配。 程序清单 5-8 使用 Linkify Match Filter class MyMatchFilter implements MatchFilter { public boolean acceptMatch(CharSequence s, int start, int end) { return (start == 0 || s.charAt(start-1) != '!'); } } 代码片段 PA4AD_Ch05_Linkify/src/MyActivity.java 4. 使用 Transform Filter Transform Filter 允许修改匹配的链接文本生成的隐式 URI。把链接文本和目标 URI 分离开,你 可从 wrox.com 下载源代码 可从 wrox.com 下载源代码 第 5 章 Intent 和 Broadcast Receiver 155 能够更自由地决定如何把数据字符串显示给用户。 要使用 Transform Filter,需要在 Transform Filter 中实现 transformUrl 方法。当 Linkify 找到一个 成功的匹配之后,它会调用 transformUrl,并传入需要使用的 RegEx 模式以及匹配文本字符串(还没 有添加基础 URI 前缀)。可以修改匹配的字符串,使它可以作为一个 View Intent 的数据追加到基础 字符串的后面,然后返回结果。 程序清单 5-9 中的 TransformFilter 实现用来把匹配的文本转换为小写的 URI,并删除了所有的 空格字符。 程序清单 5-9 使用 Linkify Transform Filter class MyTransformFilter implements TransformFilter { public String transformUrl(Matcher match, String url) { return url.toLowerCase().replace(" ", ""); } } 代码片段 PA4AD_Ch05_Linkify/src/MyActivity.java 5.1.3 使用 Intent 广播事件 到目前为止,已经看到了如何使用 Intent 来启动新的应用程序组件,但是实际上它们也可以使 用 sendBroadcast 方法来在组件之间匿名地广播消息。 作为一个系统级的消息传递机制,Intent 可以在进程之间发送结构化的消息。因此,可以通过 实现 Broadcast Receiver 来监听和响应应用程序内的这些 Broadcast Intent。 Broadcast Intent 用于向监听器通知系统的应用程序或应用程序事件,从而可以扩展应用程序间 的事件驱动的编程模型。 Broadcast Intent 可以使应用程序更加开放;通过使用 Intent 来广播一个事件,可以在不用修改 原始的应用程序的情况下,让你和第三方开发人员对事件作出反应。在应用程序中,可以通过监听 Broadcast Intent 来对设备状态变化和第三方应用程序事件作出反应。 Android 大量使用了 Broadcast Intent 来广播系统事件,如网络连接、扩展 dock 状态和来电的 变化。 1. 使用 Intent 来广播事件 在应用程序组件中,可以构建希望广播的 Intent,然后使用 sendBroadcast 方法来发送它。 可以对 Intent 的动作、数据和分类进行设置,从而使 Broadcast Receiver 能够精确地确定它们的 需求。在这种方案中,Intent 动作字符串可以用来标识要广播的事件,所以它应该是能够标识事件 的唯一的字符串。习惯上,动作字符串使用与 Java 包名相同的构建方式,如下面的代码段所示: public static final String NEW_LIFEFORM_DETECTED = "com.paad.action.NEW_LIFEFORM"; 如果希望在 Intent 中包含数据,那么可以使用 Intent 的 data 属性指定一个 URI。也可以包含 extras 来添加额外的基本值。按照事件驱动的范型来考虑,extras 相当于传递给事件处理程序的可选参数。 程序清单 5-10 展示了使用前面定义的动作所创建的基本的 Broadcast Intent,附加的事件信息存 可从 wrox.com 下载源代码 Android 4 高级编程(第 3 版) 156 储在 extras 中。 程序清单 5-10 广播 Intent Intent intent = new Intent(LifeformDetectedReceiver.NEW_LIFEFORM); intent.putExtra(LifeformDetectedReceiver.EXTRA_LIFEFORM_NAME, detectedLifeform); intent.putExtra(LifeformDetectedReceiver.EXTRA_LONGITUDE, currentLongitude); intent.putExtra(LifeformDetectedReceiver.EXTRA_LATITUDE, currentLatitude); sendBroadcast(intent); 代码片段 PA4AD_Ch05_BroadcastIntents/src/MyActivity.java 2. 使用 Broadcast Receiver 来监听广播 Broadcast Receiver(通常简单地称为接收器)可以用来监听 Broadcast Intent。要使 Broadcast Receiver 能够接收广播,就需要对其进行注册,既可以使用代码,也可以在应用程序的 manifest 文件中注册(此 时称为 manifest 接收器)。无论怎么注册,都需要使用一个 Intent Filter 来指定它要监听哪些 Intent 和 数据。 对于包含 manifest 接收器的应用程序,在 Intent 被广播出去的时候,应用程序不一定非要处于 运行状态才能执行接收。当匹配的 Intent 被广播出去的时候,它们会被自动地启动。对于资源管理 来说,这一点是非常优秀的,因为它可以让你创建出事件驱动的应用程序,即使它们被关闭或者销 毁了,也仍然能够对广播事件作出响应。 要创建一个新的 Broadcast Receiver,需要扩展 BroadcastReceiver 类并重写 onReceive 事件处理 程序。 import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //TODO: 响应接收到的 Intent. } } 当接收到一个与在注册接收器时使用的 Intent Filter 相匹配的 Broadcast Intent 的时候,就会执行 onReceive 方法。onReceive 处理程序必须在 5 秒钟之内完成,否则就会显示 Force Close 对话框。 一般情况下,Broadcast Receiver 将会更新内容、启动 Service、更新 Activity UI,或者使用 Notification Manager 来通知用户。5 秒钟的执行限制保证了主要的处理工作不能够、也不应该由 Broadcast Receiver 直接完成。 程序清单 5-11 展示了如何实现一个 Broadcast Receiver,它 从 Broadcast Intent 中提取数据和几个 extra,并使用它们来启动一个新 Activity。在下面的部分中,将会学习如何在代码和在应用程序的 manifest 中注册它。 可从 wrox.com 下载源代码 第 5 章 Intent 和 Broadcast Receiver 157 程序清单 5-11 实现一个 Broadcast Receiver public class LifeformDetectedReceiver extends BroadcastReceiver { public final static String EXTRA_LIFEFORM_NAME = "EXTRA_LIFEFORM_NAME"; public final static String EXTRA_LATITUDE = "EXTRA_LATITUDE"; public final static String EXTRA_LONGITUDE = "EXTRA_LONGITUDE"; public static final String ACTION_BURN = "com.paad.alien.action.BURN_IT_WITH_FIRE"; public static final String NEW_LIFEFORM = "com.paad.alien.action.NEW_LIFEFORM"; @Override public void onReceive(Context context, Intent intent) { //从 Intent 获得 lifeform 的细节 Uri data = intent.getData(); String type = intent.getStringExtra(EXTRA_LIFEFORM_NAME); double lat = intent.getDoubleExtra(EXTRA_LATITUDE, 0); double lng = intent.getDoubleExtra(EXTRA_LONGITUDE, 0); Location loc = new Location("gps"); loc.setLatitude(lat); loc.setLongitude(lng); if (type.equals("facehugger")) { Intent startIntent = new Intent(ACTION_BURN, data); startIntent.putExtra(EXTRA_LATITUDE, lat); startIntent.putExtra(EXTRA_LONGITUDE, lng); context.startService(startIntent); } } } 代码片段 PA4AD_Ch05_BroadcastIntents/src/LifeformDetectedReceiver.java 在代码中注册 Broadcast Receiver 影响特定 Activity 的 UI 的 Broadcast Receiver 通常在代码中注册。在代码中注册的接收器只会 在包含它的应用程序组件运行时响应 Broadcast Intent。 在接收器用来更新一个 Activity 中的 UI 元素时,这样做很有帮助。在这种情况下,在 onResume 处理程序中注册接收器,并在 onPause()中注销它是一种很好的做法。 程序清单 5-12 显示了如何使用一个 IntentFilter 类来注册和注销一个 Broadcast Receiver。 程序清单 5-12 在代码中注册和注销一个 Broadcast Receiver private IntentFilter filter = new IntentFilter(LifeformDetectedReceiver.NEW_LIFEFORM); private LifeformDetectedReceiver receiver = new LifeformDetectedReceiver(); 可从 wrox.com 下载源代码 可从 wrox.com 下载源代码 Android 4 高级编程(第 3 版) 158 @Override public void onResume() { super.onResume(); //注册 Broadcast Receiver. registerReceiver(receiver, filter); } @Override public void onPause() { //注销 Broadcast Receiver unregisterReceiver(receiver); super.onPause(); } 代码片段PA4AD_Ch05_BroadcastIntents/src/MyActivity.java 在应用程序的 manifest 中注册 Broadcast Receiver 要在应用程序的 manifest 中包含一个 Broadcast Receiver,可以在 application 节点中添加一个 receiver 标签,以指定要注册的 Broadcast Receiver 的类名。接收器节点需要包含一个 intent-filter 标 签来指定要监听的动作字符串。 通过这种方式注册的Broadcast Receiver总是活动的,并且即使当应用程序被终止或者未启动时, 也可以接收 Broadcast Intent。 3. 广播有序的 Intent 当 Broadcast Receiver 接收 Intent 的顺序十分重要时,特别是当需要接收器能够影响将来的接收 器收到的 Broadcast Intent 时,可以使用 sendOrderedBroadcast 方法,如下所示。 String requiredPermission = "com.paad.MY_BROADCAST_PERMISSION"; sendOrderedBroadcast(intent, requiredPermission); 使用这个方法时,Intent 将会按照优先级顺序被传递给所有具有合适权限(当指定了权限时)的已 注册的接收器。可以在 Broadcast Receiver 的 Intent Filter manifest 节点中使用 android:priority 属性指 定其权限,值越大,代表优先级越高。 第 5 章 Intent 和 Broadcast Receiver 159 只对应用程序需要强制特定接收顺序的接收器发送有序的广播并指定接收器的优先级,这是一 种很好的做法。 发送有序广播的一种常见的例子是广播想要收到其结果数据的 Intent。使 用 sendOrderedBroadcast 方 法时,可以指定一个将放到接收器队列末尾的接收器,从而确保当 Broadcast Intent 已被已注册的有 序 Broadcast Receiver 处理(和修改)后,它能够收到该 Broadcast Intent。 在这种情况中,对于那些在返回给最后一个接收器之前可能被任何收到广播的接收器修改的 Intent 结果、数据和 extra,为它们指定默认值通常很有帮助。 // Specify the default result, data, and extras. // The may be modified by any of the Receivers who handle the broadcast // before being received by the final Receiver. int initialResult = Activity.RESULT_OK; String initialData = null; String initialExtras = null; // A special Handler instance on which to receive the final result. // Specify null to use the Context on which the Intent was broadcast. Handler scheduler = null; sendOrderedBroadcast(intent, requiredPermission, finalResultReceiver, scheduler, initialResult, initialData, initialExtras); 4. 广播 Sticky Intent Sticky Intent 是 Broadcast Intent 的有用变体,可以保存它们最后一次广播的值,并且当有一个新 的接收器被注册为接收该广播时,它们会把这些值作为 Intent 返回。 当调用 registerReceiver 来指定一个匹配 Sticky Broadcast Intent 的 Intent Filter 时,返回值将是最 后一次 Intent 广播,例如电池电量变化的广播: IntentFilter battery = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent currentBatteryCharge = registerReceiver(null, battery); 从这段代码可以看出,不是必须指定一个接收器来获得 Sticky Intent 的当前值。因此,许多系 统设备状态广播(例如电池和 dock 状态)使用 Intent 来提高效率。本章中将会详细讨论这方面的内容。 要广播自己的 Sticky Intent,应用程序必须具有 BROADCAST_STICKY 用户权限,然后需要调 用 sendStickyBroadcast 并传入相关的 Intent: sendStickyBroadcast(intent); 要删除一个 Sticky Intent,可以调用 removeStickyBroadcast,并传入要删除的 Sticky Intent。 removeStickyBroadcast(intent); 5.1.4 Local Broadcast Manager Local Broadcast Manager(局部广播管理器)包含在 Android Support Library 中,用于简化注册 Broadcast Intent,以及在应用程序内的组件之间发送 Broadcast Intent 的工作。 Android 4 高级编程(第 3 版) 160 提示:在使用时,Pending Intent 会执行那些包装好的 Intent,同时拥有与你在自 己的应用程序中执行它们时相同的权限和身份。 因为局部广播的作用域要小一些,所以使用 Local Broadcast Manager 比发送全局广播更加高效。 而且使用 Local Broadcast Manager 也确保了应用程序外部的任何组件都收不到你广播的 Intent,所以 不会有私人数据或敏感数据(如位置信息)泄露出去的风险。 类似地,其他应用程序也不能向你的接收器发送广播,避免了这些接收器成为安全漏洞。 要使用 Local Broadcast Manager,首先必须在应用程序内包含 Android Support Library,如第 2 章所述。 使用 LocalBroadcastManager.getInstance 方法来返回 Local Broadcast Manager 的一个实例: LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); 要注册一个局部 Broadcast Receiver,与注册全局接收器时类似,需要使用 Local Broadcast Manager 的 registerReceiver 方法,并传入一个 Broadcast Receiver 和一个 Intent Filter: lbm.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Handle the received local broadcast } }, new IntentFilter(LOCAL_ACTION)); 注意,指定的 Broadcast Receiver 也可以用来处理全局 Intent 广播。 要发送一个局部 Broadcast Intent,可以使用 Local Broadcast Manager 的 sendBroadcast 方法,并 传入要广播的 Intent: lbm.sendBroadcast(new Intent(LOCAL_ACTION)); Local Broadcast Manager 还包含一个用于同步的 sendBroadcastSync 方法,直到每个已注册的接 收器都收到广播后才接触阻塞。 5.1.5 Pending Intent 简介 PendingIntent 类提供了一种创建可由其他应用程序在稍晚的时间触发的 Intent 的机制。 Pending Intent 通常用于包装在响应将来的事件时触发的 Intent,例如单击 Widget 或 Notification。 PendingIntent 类提供了构建 Pending Intent 的静态方法,以便启动 Activity、启动 Service 或者广 播 Intent。 int requestCode = 0; int flags = 0; //启动一个 Activity Intent startActivityIntent = new Intent(this, MyOtherActivity.class); PendingIntent.getActivity(this, requestCode, startActivityIntent, flags); 第 5 章 Intent 和 Broadcast Receiver 161 //启动一个 Service Intent startServiceIntent = new Intent(this, MyService.class); PendingIntent.getService(this, requestCode, startServiceIntent , flags); //广播一个 Intent Intent broadcastIntent = new Intent(NEW_LIFEFORM_DETECTED); PendingIntent.getBroadcast(this, requestCode, broadcastIntent, flags); PendingIntent 类包含了一些静态的常量,它们可以用于指定标志,以更新或者取消与指定动作 匹配的现有 Pending Intent,也可以用于指定该 Intent 是否只触发一次。在第 10 章和第 14 章介绍 Notification 和 Widget 时,将深入讨论各个选项。 5.2 创建 Intent Filter 和 Broadcast Receiver 学习了使用 Intent 来启动 Activity/Service 和广播事件后,了解如何创建 Broadcast Receiver 和 Intent Filter 也是很重要的,它们能够监听 Broadcast Intent 从而让应用程序响应这些 Intent。 在 Activity 和 Service 中,Intent 代表了对在某个数据集上执行的动作的请求,Intent Filter 则声明了一个特定的应用程序组件能够对一个类型的数据执行操作。 Intent Filter 还用来指定一个 Broadcast Receiver 感兴趣接收的动作。 5.2.1 使用 Intent Filter 为隐式 Intent 提供服务 如果 Intent 是对在某个数据集上执行的动作的请求,那么 Android 是如何知道使用哪个应用 程序(和组件)来响应这个请求的呢?使用 Intent Filter,应用程序组件可以声明它们支持的动作 和数据。 要把一个 Activity 或者 Service 注册为一个可能的 Intent 处理程序,可以在它的 manifest 节 点中添加一个 intent-filter 标签并使用下面的标签(以及相关的属性): ● action 使用 android:name 属性指定要为之服务的动作的名称。每个 Intent Filter 必须要 有至少一个 action(动作)标签。Action 应该是一个描述性的唯一的字符串。所以最好的 做法使用基于 Java 的包命名约定的命名系统。 ● category 使用 android:name 属性来指定应该在哪种情况下为 action 提供服务。每个 Intent Filter 标签都可以包含多个 category 标签。既可以指定自己的 category 也可以使用 以下 Android 提供的标准值: ● ALTERNATIVE 可以把这个动作指定为在特定数据类型上执行的默认动作的可 选项。例如,一个联系人的默认动作是查看其信息,而可选的动作则是对其进行编辑。 ● SELECTED_ALTERNATIVE 与 ALTERNATIVE 相似,但是 ALTERNATIVE 总 是使用后面将描述的 intent resolution 解析为一个动作,而当要求有很多种可能性的 时候,则可以使用 SELECTED_ALTERNATIVE。在本章后面可以看到 Intent Filter 的一种用法就是使用 action 帮助动态构建上下文菜单。 Android 4 高级编程(第 3 版) 162 ● BROWSABLE 指定一个在浏览器内部可用的动作。当一个 Intent 在浏览器内部触 发时,它总是会包含 BROWSABLE 类别。如果想让应用程序响应浏览器内触发的动 作(例如,截获指向特定网站的链接),那么必须包含 BROWSABLE 类别。 ● DEFAULT 通过设置这个类型可以使一个组件成为 Intent Filter 内指定的数据类型 的默认动作。对于那些使用一个显式的 Intent 启动的 Activity,这个类型是很有必要的。 ● HOME 通过将一个 Intent Filter 的类别设置为 HOME,而不指定一个 action,就 可 以把它作为本地屏幕的可选项。 ● LAUNCHER 使用这个类别会让一个 Activity 出现在应用程序的启动器中。 ● data data标签允许指定组件可以执行的数据类型;根据情况,也可以包含多个数据标 签。可以使用以下属性的任意组合来指定你的组件所支持的数据: ● android:host 指定一个有效的主机名(如 google.com)。 ● android:mimetype 指定组件可以执行的数据类型。例如,将匹配所有的 Android cursor。 ● android:path 指定 URI 的有效路径值(如/transport/boats/)。 ● android:port 指定主机的有效端口。 ● android:scheme 要求一种特定的模式(如 content or http)。 下面的代码展示了一个Activity的Intent Filter,它将基于它的mime类型执行SHOW_DAMAGE 动作,这个动作既可以是主要的,也可以是可选的。 你也许注意到了当在 Android 设备上点击 YouTube 视频或者 Google Map 位置的链接时,会 分别提示你使用 YouTube 或者 Google Map。这是通过在 Intent Filter 的 data 标签下指定 scheme、 host 和 path 属性来实现的,如程序清单 5-13 所示。在这个例子中,以 http://blog.radioactiveyak.com 形式开头的链接都将由这个 Activity 来处理。 程序清单 5-13 将一个 Activity 注册为一个 Intent Receiver,并使用一个 Intent Filter 来浏览一个 特定网站的内容 可从 wrox.com 下载源代码 第 5 章 Intent 和 Broadcast Receiver 163 代码片段 PA4AD_Ch05_Intents/AndroidManifest.xml 注意,必须包含有 browsable 类别,以便在浏览器中点击链接能够触发这种行为。 1. Android 如何解析 Intent Filter 当在 startActivity 中传入一个隐式 Intent 时,决定启动哪一个 Activity 的过程叫做 intent 解 析。intent 解析的目的是使用以下步骤来找出最匹配的 Intent Filter: (1) Android 将已安装包的可用的 Intent Filter 放到一个列表中。 (2) 那些与解析 Intent 时相关联的动作或者类别不匹配的 Intent Filter 将会从列表中移除。 ● 如果 Intent Filter 包含了指定的动作,那么就认为动作匹配了。如果检查到没有任 何一个动作和 Intent 指定的动作相匹配时,就认为动作匹配失败了。 ● 对于 category 匹配来说,Intent Filter 必须包含待解析的 Intent 中的所有 category, 但可以包含 Intent 中所不包含的其他的 category。一个没有指定 category 的 Intent Filter 只能和没有任何 category 的 Intent 相匹配。 (3) 最后,Intent 的数据 URI 的每一个部分都和 Intent Filter 的 data 标签进行比较。如果 Intent Filter 指定了 scheme、host/authority、path 或者 MIME 类型,那么这些值都要和 Intent 的 URI 比较。任意一个不匹配都会把 Intent Filter 从列表中移除。没有指定数据值的 Intent Filter 将会和 所有的 Intent 数据值匹配。 ● MIME类型是指要匹配的数据的数据类型。当匹配数据类型时,可以使用通配符来 匹配子类型(例如,earthquakes/*)。如果 Intent Filter 指定了一种数据类型,那么它必 须匹配该 Intent;如果不指定数据类型,则它会和所有的 Intent 匹配。 ● scheme是 URI 的“协议”部分(例如,http:,mailto:或者 tel:)。 ● hostname 或者 data authority 是 URI 位于 scheme 和 path 之间的部分(例如,developer. android.com)。hostname 要想匹配,Intent Filter 的 scheme 必须也要匹配。 ● 数据 path 是 authority 之后的内容(例如,/training)。只有数据的 scheme 和 hostname 都匹配的时候,path 才能匹配。 (4) 当隐式启动一个 Activity 时,如果这个进程解析出多个组件,那么所有可能匹配的组件 都会呈现给用户。对于 Broadcast Receiver,每个匹配的接收器将接收 Broadcast Intent。 原生 Android 应用程序组件被解析的方式与第三方应用程序被解析的方式是完全相同的。 它们并没有更高的优先级,并且可以完全被新的 Activity 取代,而这些新的 Activity 声明了可以 为相同的动作请求提供服务的 Intent Filter。 2. 在 Activity 中找到和使用接收到的 Intent 当通过隐式 Intent 启动应用程序组件时,它需要找出要执行的动作和对哪些数据执行动作。 可以调用 getIntent 得到启动一个 Activity 的 Intent,如程序清单 5-14 所示。 Android 4 高级编程(第 3 版) 164 程序清单 5-14 在一个 Activity 中找到启动它的 Intent @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Intent intent = getIntent(); String action = intent.getAction(); Uri data = intent.getData(); } 代码片段 PA4AD_Ch05_Intents/src/MyOtherActivity.java 使用 getData 和 getAction 方法来找到与 Intent 相关联的数据和动作。可以使用类型安全的 getExtra 方法来得到存储在其 extras bundle 中的额外信息。 getIntent 方法总是返回最初用来创建 Activity 的 Intent。在一些情况下,Activity 在启动后可 能继续接收 Intent。可以使用 widget 和 Notification 提供一些快捷方式使之能在一个可能依然在 运行的 Activity 中显示数据,虽然该 Activity 是不可见的。 可以在Activity中重写onNewIntent处理程序来接收和处理在Activity创建后得到的新的Intent。 @Override public void onNewIntent(Intent newIntent) { // TODO:响应新的 Intent super.onNewIntent(newIntent); } 3. 传递责任 可以使用 startNextMatchingActivity 方法将处理动作的责任传递给下一个最佳匹配的 Activity。 Intent intent = getIntent(); if (isDuringBreak) startNextMatchingActivity(intent); 这允许为组件添加额外的条件,用来限制它们在基于 Intent Filter 的 intent 解析进程之外的 使用。 4. 选择联系人示例 在这个例子中,将创建一个新的 Activity 来为联系人数据的 ACTION_PICK 动作提供服务。 它显示了联系人数据库中的每一个联系人,而且在关闭并且将其 URI 返回给调用它的 Activity 之前,用户可以选择其中的一个联系人。 可从 wrox.com 下载源代码 第 5 章 Intent 和 Broadcast Receiver 165 提示:这个例子的实用价值有待考虑。因为 Android 已经提供了一个用来从一个 列表中选择一个联系人的 Intent Filter,它可以在一个隐式的 Intent 中使用 content: //contacts/people/的 URI 来进行调用。因此,这个练习的目的只是为了说明这种形式 , 即使这个特定的实现没有任何用处。 (1) 创建一个新的 ContactPicker 项目,其中包含一个 ContactPicker Activity。 package com.paad.contactpicker; import android.app.Activity; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract.Contacts; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import android.widget.SimpleCursorAdapter; public class ContactPicker extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } } (2) 通过修改 main.xml 布局资源来包含一个 ListView 控件。后面将使用这个控件显示联 系人。 (3) 创建一个新的包含一个单独的 TextView 控件的 listitemlayout.xml 布局资源。它将用来 在 List View 中显示每个联系人。 parent, View view, int pos, lv.setOnItemClickListener(new ListView.OnItemClickListener() { 路径返回给调用 Activity。 b. 为 List View 添加一个 onItemClickListener。当从列表中选择一个联系人时应该将该项的 lv.setAdapter(adapter); ListView lv = (ListView)findViewById(R.id.contactListView); to); from, c, ٛ R.layout.listitemlayout, SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, int[] to = new int[] { R.id.itemTextView }; String[] from = new String[] { Contacts.DISPLAY_NAME_PRIMARY }; ContactsContract.Contacts.CONTENT_URI, null, null, null, null); final Cursor c = getContentResolver().query( 章介绍的 Cursor Loader。 把它绑定到 List View 上。注意,在本例中,查询是在主 UI 线程上执行的。更好的方法是使用第 8 a. 创建一个新的 Cursor 来遍历存储在联系人列表中的联系人,并使用 SimpleCursorArrayAdapter setContentView(R.layout.main); super.onCreate(savedInstanceState); public void onCreate(Bundle savedInstanceState) { @Override (4) 返回到 ContactPicker Activity。重写 onCreate 方法。 /> android:textColor="#FFF" android:textSize="16dp" android:padding="10dp" android:layout_height="wrap_content" android:layout_width="match_parent" android:id="@+id/itemTextView" android:layout_height="match_parent" 166 Android 4 高级编程(第 3 版) 第 5 章 Intent 和 Broadcast Receiver 167 finish(); } }); c. 关闭 onCreate 方法。 } (5) 修改应用程序的 manifest 文件,并更新 Activity 的 intent-filter 标签以添加在联系人数据 上对 ACTION_PICK 动作的支持。 (6) 至此就完成了子 Activity。要对其测试,可以创建一个新的测试工具 ContactPickerTester Activity。创建一个新的布局资源—— contactpickertester.xml,它包含了一个用来显示选中的联系 人的 TextView 和一个用来启动子 Activity 的 Button。
还剩124页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 4 人已下载

下载pdf

pdf贡献者

eene

贡献于2014-02-18

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf