安卓 x86 教程(一)

随笔3个月前发布 混沌小
52 0 0

原文:Android TV apps development

协议:CC BY-NC-SA 4.0

零、简介

我们撰写了 Android on x86:英特尔架构优化简介,为该主题的最佳实践和程序提供了一站式的详细资源。这本书涵盖了安装问题、硬件优化问题、软件需求、编程任务,以及在考虑为基于 x86 的 Android 设备编程时出现的性能优化。我们自己参与过相关的项目,我们致力于将我们的经验和信息收集到一本书中,这本书可以作为任何项目特定需求的指南。我们进行了微调优化、本机代码调整、硬件加速和多媒体应用的高级配置。

这本书不仅仅致力于代码,尽管你会在里面找到大量的代码样本和案例研究。相反,我们已经为 x86 平台上的 Android 提供了您需要的信息,以便充分利用 x86 架构。我们将指导您安装面向英特尔架构的 Android 软件开发套件,帮助您了解商用 Android 设备可用处理器之间的异同,教您创建和移植应用,调试现有 x86 应用,提供 NDK 和 C++ 优化解决方案,并介绍英特尔硬件加速执行管理器。我们收集的信息为您快速、出色地完成开发工作提供了最有用的帮助。

为什么在 x86 上使用 Android?

2011 年,我们在沟通方式上经历了一次范式转变。智能设备销量首次超过个人电脑销量。卫兵换岗有三个原因:

  • 我们对开放、持续交流的日益增长的专业和社会需求
  • 智能手机和平板电脑的低成本和引人注目的新功能
  • 移动应用的易用性和可用性提高

在接下来的几年里,手机上网很可能会超过笔记本电脑和台式机;我们用来交流的硬件可能会改变,但我们对随时随地连接的热情肯定会继续。

谷歌的 Android 操作系统占据了全球智能手机出货量 80%以上的市场份额,已经被证明是这场移动革命的领导者。Android 成功的关键原因是其开放的平台和灵活的合作伙伴关系。Android 开发者可以获得的大量开源资源刺激了更多应用的开发,给了消费者更多的选择。此外,开放平台支持竞争性和多样化的硬件环境。

随着高性能移动设备市场的扩大,谷歌已经与英特尔合作,展望 Android 的下一个前沿:让操作系统在内置英特尔架构的设备上运行。英特尔架构上的 Android 之旅非正式地始于 2009 年,当时一群开发人员启动了开源 Android-x86 计划,以便将 Android 移植到运行英特尔 x86 处理器的设备上。不久之后,随着官方 Android on Intel 架构项目的推出,英特尔开始为 Android 开源项目(AOSP)贡献代码和资源。2012 年,首款搭载英特尔处理器的安卓智能手机在全球范围内发布上市;到 2013 年底,具有前所未有处理能力的 Android 智能手机和平板电脑正在进入美国市场。最近,这两个组织承诺让 Android 在 64 位设备上运行,包括上网本、笔记本电脑和传统的台式电脑,这意味着在 2014 年,Android 将打入一个历史上由微软 Windows 和苹果 OSX 主导的市场。Android 将把其庞大的、蓬勃发展的应用开发人员社区带到广泛的设备和硬件架构上。

这种合作给双方都带来了很多好处。英特尔的 x86 架构拥有 35 年的优秀处理能力、成熟的开发者生态系统和一套成熟的开发工具。在性能方面,英特尔的最新芯片在高性能和低功耗之间取得了平衡,非常适合智能手机、平板电脑和上网本。原生 x86 仿真器支持是最新 Android SDK 版本的一个关键特性,英特尔致力于为开发人员提供大量工具,用于优化他们芯片的 Android 应用性能。

通过扩展到 32 位和 64 位架构,Android 的前景正在变得广阔。每天,越来越多搭载安卓系统和英特尔处理器的移动设备出现在货架上和我们的指尖上,而即将到来的基于英特尔技术的上网本和笔记本电脑将会把环境塑造成令人惊叹的东西。一种新的 Android 体验将会形成,它将保持多样性,并针对更大的屏幕、强大的多窗口和更快的处理器速度进行优化。这是一个令人兴奋的时刻,我们希望开发者能够抓住这个新的机会,拓展 Android 的视野。

这本书是给谁的?

这本书主要针对两类人群:开发者和那些有兴趣选择 Android x86 作为应用平台的人。考虑到这一点,开始的章节关注更高层次的非技术问题,这样来自所有技术背景的人都可以做出明智的选择。后面的章节重点关注开发人员方面,从微处理器架构和 Android 开发环境的基础开始,然后构建非常高级的、注重性能的内容。我们的目标是接触到对 x86 平台上的 Android 感兴趣的所有人,并尽最大努力为您提供您需要的答案。

我们真的希望你喜欢这本书。我们当然很喜欢探索这个话题,并期待着在未来几年看到这个快速发展的领域会发生什么。我们还想指出的是,虽然我们可能对 Android 略知一二,但我们认识到我们肯定不是对所有事情都最了解的。请随意质疑您在本书中找到的任何信息——我们鼓励您使用外部资源,并真正参与到围绕该技术的社区中来!

一、Android 操作系统的历史和演变

我要毁掉安卓,因为它是偷来的产品。我愿意为此进行热核战争。

—史蒂夫·乔布斯,苹果公司

Android,Inc. 的创始人有着明确的使命。根据 Android 创始人之一安迪·鲁宾的说法,Android Inc. 是为了开发“更智能的移动设备,更了解其所有者的位置和偏好。”鲁宾进一步指出,“如果人们聪明的话,这些信息开始被整合到消费产品中。”那是 2003 年,地点是加州的帕洛阿尔托。这是 Android 诞生的一年。

虽然安卓公司开始秘密运作,但今天全世界都知道了安卓。众所周知,Android 是现代智能手机、平板电脑和即将问世的笔记本电脑的操作系统,但这到底意味着什么呢?Android 以前是什么样子的?它是如何走到今天这一步的?所有这些问题以及更多的问题都将在这简短的一章中得到解答。

起源

2005 年,当价值数十亿美元的技术公司谷歌收购安卓公司时,安卓首次出现在技术雷达上。当时,人们对安卓以及谷歌打算用它做什么知之甚少。直到 2007 年,当谷歌宣布世界上第一个真正的移动设备开放平台时,信息才变得稀少。

Android 第一次发行

2007 年 11 月 5 日,开放手机联盟发布了一份新闻稿,为 Android 平台的未来奠定了基础。该联盟将 Android 的一些目标表述为:“促进移动设备的创新,并为消费者提供比当今移动平台上的大部分产品更好的用户体验。”

当时,全球使用的手机数量超过 20 亿部,而截至 2010 年使用的手机数量为 46 亿部。然而,提供移动设备的各个公司之间没有平台的协调。随着 Android 的引入,单一操作系统消除了重新实现手机应用和中间件的需要。开发新设备的公司现在可以更专注于硬件和底层组件。

但是这些公司并不是唯一从安卓发布中受益的公司;软件开发人员现在可以将应用发布到多种设备上,只需对底层代码做很少的修改。这使得开发人员可以花更多的时间在这些手机运行的应用上,并创建我们都习惯的丰富而令人印象深刻的应用。这部分是由于 Android 背后的开源哲学和 Apache 许可证,这是大多数 Android 源代码使用的许可证。

开源 Apache 许可

Apache 许可证只是开源社区中存在的许多不同许可证之一。尽管所有这些许可证都有所不同,但它们都促进了相同的开源思维,这可以总结如下:

“自由软件”是自由的问题,而不是价格的问题。为了理解这个概念,你应该把“免费”理解为“言论自由”,而不是“免费啤酒”。

理查德·史泰曼

Apache 许可证特别授予出于任何目的使用软件的自由,以及分发、修改或分发修改版本的能力。Apache 许可证也是许可的,这意味着修改后的版本不必屈从于 Apache 许可证。有关 Apache 许可的更多信息,请访问http://www.apache.org/licenses/LICENSE-2.0

安卓是什么?

那么 Android 到底是什么?Android 操作系统是运行在全球超过 4 亿台设备上的开源技术栈。这个技术堆栈由各种组件组成,允许开发人员和设备制造商独立工作。这可以分为五个主要部分——应用、应用框架、本地库、Android 运行时和 Linux 内核——如图图 1-1 所示。

安卓 x86 教程(一)

Android OS(维基百科)http://en.wikipedia.org/wiki/File:Android-System-Architecture.svg

图 1-1 。Android 系统架构

应用

应用存在于最高层。这些都是每个使用 Android 的人最熟悉的工具。Android 配备了各种支持日常电话需求的强大应用,如消息传递、电子邮件、互联网浏览和各种第三方应用。这些应用主要用 Java 编程语言编写。在最近一次与甲骨文的法律诉讼中,谷歌的安卓主管安迪·鲁宾解释了他为什么选择 Java 作为开发者的使用语言。Rubin 的主要观点是 Java 有一个众所周知的品牌名称,而且全世界几乎所有的大学都教授 Java。

这些应用通过各种方式分发,最常见的是来自谷歌 Play 商店(以前的 Android market place);然而,Android 操作系统也支持通过 USB 连接和 SD 卡安装应用。

应用框架

Android 为开发人员提供了为用户创建广泛的、交互式的、丰富的图形应用的能力和工具,并且旨在将这些应用部署到谷歌 Play 商店。开发人员可以访问核心应用内部使用的相同 API,也可以访问几乎所有现有的 Java 库。Android 应用的开发流程请参考 第六章:安装 Android SDK 进行英特尔应用开发。

本机库

下一层是岔路口。原生库和 Android 运行时存在于大致相同的空间。本机库是 Android 系统所依赖的编译和预装的 C/C++ 二进制文件。这些包括图 1-1 绿色部分的所有库。接下来的章节描述了 Android 中一些比较突出的本地库和它们的功能。

地面管理器

这通常被称为 Android 的窗口管理器。Surface Manager 用于合成任何单个屏幕的外观。它还做一些更微妙的事情来帮助 Android 顺利运行,如屏幕外缓冲和过渡。

SQLite〔??〕〔??〕

这是一个用于在 Android 设备的会话间保存信息的数据库。在 Android 上,SQLite 数据库存储在设备的内部存储器中,因此 SD 卡可以互换,而不会丢失设备特定的信息。

WebKit

WebKit 允许快速有效地将 HTML 渲染并显示到 Android 上。这是 Android 系统中的默认浏览器引擎,可供系统和第三方应用使用。

OpenGL/ES

OpenGL 引擎处理 Android 中的图形。OpenGL 可以在 Android 上渲染 2D 和 3D 对象。这也支持带有专用图形芯片的设备上的硬件加速。

Android 运行时

Android 运行时内部有两个主要组件:Android 提供的核心 Java 库和 Dalvik 虚拟机。Dalvik 虚拟机是 Google 对 Java 的实现,它被优化用于移动设备。Dalvik 内部更具体的差异是非常技术性的,不在本书中讨论。

Linux 内核

最后一层是 Linux 内核。Android 最初基于 Linux 2.6 内核,并针对移动使用进行了一些优化。当前版本的 Android 基于 Linux 3.1 内核。Linux 内核提供了尽可能接近硬件的访问。因此,驱动程序是在内核空间中编写的,以尽可能快速高效地运行。这些包括控制内部无线电、打开立体声和摄像头、处理电源和电池充电,以及操作设备上的物理键盘或按钮。与 Android 一样,Linux 内核也是一个开源项目,并被广泛使用,尤其是在企业环境中的服务器上。

开放手机联盟

2007 年 11 月,开放手机联盟(OHA) 由致力于开发开放移动标准的 34 个创始成员成立,包括谷歌、移动设备制造商、应用开发商、嵌入式系统开发商和商业化公司。网站中描述的该联盟的目标如下:

Open Handset Alliance 是一个由 84 家技术和移动公司组成的团体,旨在加速移动领域的创新,为消费者提供更丰富、更便宜、更好的移动体验。

目前,OHA 有 84 家公司正在开发和从事该联盟迄今为止唯一的主要项目——Android。由于 OHA 成员提供的服务和产品,设备和相关服务以更低的价格生产出更高的质量。

Android 开源项目

收购安卓公司后,安卓开源项目(AOSP)成立,并由谷歌领导。AOSP 负责 Android 软件栈的开发和维护。如 Google 所述,该项目的目标如下:

Android 开源项目的目标是创造一个成功的现实产品,改善最终用户的移动体验。

Android 的设计和维护考虑到了向后功能。这意味着新设备可以运行早在 Android 的 Cupcake (1.5)上开发的应用。Android SDK 的官方支持只追溯到 Cupcake (1.5),所以为 Cupcake 之前的设备编写的应用不能保证在最新的 Android 设备上运行。

在 AOSP 的发展过程中,已经有许多不同版本的 Android 发布在移动设备上。当新的 Android 版本发布时,移动设备所有者可以选择是否升级他们的操作系统。随着 Android 的每一次迭代,新的 SDK 都可供开发人员使用,各种新功能也添加到受支持的设备中。软件开发人员在开发新的应用时,需要注意以前版本的遗留特性。

天文 (1.0)

Astro 是 Android 的起点,于 2007 年 11 月作为测试版发布,并于 2008 年 9 月在 HTC Dream 上向公众发布。Astro 展示了 Android 操作系统的各种核心功能,包括许多 Android 用户现在知道和喜欢的应用。这些应用包括安卓市场、网络浏览器、电子邮件/Gmail、谷歌地图、信息服务、媒体播放器、YouTube 以及其他各种应用。

纸杯蛋糕(1.5)

2009 年 4 月 30 日发布的 Cupcake 是 Android 下一个登陆商业市场的主要版本。Cupcake 基于 Linux 内核 2.6.27,并为用户和开发人员提供了许多新特性。主要的变化是支持虚拟键盘,支持主屏幕上的小部件,在不同地方添加动画,以及支持蓝牙设备的自动配对和立体声。有趣的是,从纸杯蛋糕开始,迄今为止所有的安卓版本都以甜点命名。

甜甜圈(1.6)

2009 年 9 月 15 日,谷歌发布 Android 版本,命名为 Donut。Donut 带来了从 2.6.27 到 2.6.29 的 Linux 内核更新,以及一些新特性和支持的设备。主要功能包括联系人/网页/书签的语音和文本搜索,支持 WVGA 屏幕,以及相机功能和速度的改进。Donut 是 1.x 系列中最后一个发布的 Android 版本。

闪电(2.0/2.1)

clair 于 2009 年 10 月 26 日发布,它继续构建在 Linux 内核版本 2.6.29 上。SDK 2.0 版为开发者和消费者带来了许多新的特性和功能。Android 在有能力的设备上的外观和感觉发生了巨大的变化,包括许多不同应用的速度显著提高。Android 2.0 的首要设备是威瑞森无线的摩托罗拉的 droid。

2009 年 12 月 3 日,谷歌将 Android 更新到 2.0.1 版本,努力修复一些小错误,并为开发者更新 API。直到 2010 年 1 月 12 日,Android 才搬到 2.1 版本。与 12 月的更新类似,2.1 版本主要包括对底层 API 的更新和错误修复。

弗罗约(2.2.x)

2010 年 5 月 20 日,Android SDK 版(Froyo)发布,搭载 Linux 内核 2.6.32。谷歌的 Nexus One 是市场上第一款展示 Froyo 及其新功能的设备。Froyo 增加了非常重要的功能,包括 Adobe Flash 支持、Android 云到设备的消息传递、Wi-Fi 热点功能和显著的性能优化。需要注意的是,Android SDK 建议选择 Froyo 作为您的基础开发版本,以达到 Android 用户的最大当前用户群。

Android 2.2 SDK 随后发布了三个更新:2011 年 1 月 18 日的 2.2.1、1 月 22 日的 2.2.2 和 11 月 21 日的 2.2.3。这些更新主要是对 Android 的错误修复和安全更新。

姜饼(2.3.x)

Gingerbread 于 2010 年 12 月 6 日发布,基于 Linux 内核 2.6.35。与 Froyo 的发布类似,谷歌的 Nexus S 也是为了展示姜饼而推出的。Gingerbread 的功能包括支持 WXGA 和其他超大屏幕尺寸,改进虚拟键盘,支持更多内部传感器(即陀螺仪和气压计),支持多个和前置摄像头,以及读取近场通信 (NFC)标签的能力。

从 2011 年 2 月到 9 月,Gingerbread 2 . 3 . 3–7 发布了五个更新。这些更新带来了各种功能、安全更新和错误修复。引入的最重要的功能之一是开放附件支持,它允许兼容设备作为兼容软件平台的 USB 外设。

蜂巢(3.x)

2011 年 2 月,摩托罗拉 Xoom 上发布了第一个仅支持平板电脑的 Android 版本 Honeycomb。因为 Honeycomb 是专门为平板设备设计的,所以 Android 被调整以允许更大的屏幕空间带来更愉快的体验。这包括对屏幕键盘的重新设计,允许快速访问通知和导航的系统栏,允许更轻松地使用网络的多个浏览器选项卡,以及对多核处理器的支持。

在当前的生命周期中,Honeycomb 已经更新了六次,其中两次是主要的。第一次更新是 2011 年 5 月 10 日的 Android SDK 版本 3.1,即增加了对 USB 附件的支持,如键盘、操纵杆和其他人机界面设备(hid)。SDK 的第二次重大更新是 2011 年 7 月 15 日的 3.2。3.2 最显著的特点是兼容不是为平板电脑设计的 Android 应用的显示模式。Honeycomb 的最近四次更新是小的改进、错误修复和安全更新。

冰淇淋三明治(4.0.x)

冰淇淋三明治(ICS) 发布于 2011 年 10 月 19 日,基于 Linux 内核 3.0.1。三星的 Galaxy Nexus 是上市时与 ICS 一起发布的设备。ICS 对 Android 用户界面(UI)进行了大量的功能和改进。一些功能包括可定制的启动器、选项卡式网络浏览器、解锁设备的面部识别、内置照片编辑器、UI 的硬件加速以及最初在 3.x (Honeycomb)中引入的软件按钮。值得注意的是,ICS 将 3.x 版(蜂巢)和 2.3.x 版(姜饼)合并为一个支持手机和平板电脑的单一操作系统。

从 2011 年 11 月到 2012 年 3 月,已经发布了四个针对 ICS 设备的小更新。这些更新集中在稳定性改进、相机性能和错误修复上。

糖豆(4.1.x)

果冻豆于 2012 年 7 月 9 日发布,基于 Linux 内核 3.1.10。华硕的 Nexus 7 平板设备是果冻豆的旗舰用户。Jelly Bean 发布了许多对 Android 用户界面和音频的改进和性能升级。2012 年 11 月 13 日发布的基于 Linux 内核 3.4.0 的 4.2 版增加了可访问性改进。4.3 版本于 2013 年 7 月 24 日发布,添加了 OpenGL ES 3.0 支持,以获得更好的游戏图形、安全性增强和升级的数字版权管理 API。Jelly Bean 版本的其他功能包括可定制的键盘布局、可扩展的通知、特定于应用的通知过滤和多声道音频。

KitKat (4.4.x)

撰写本文时,Android 的最新版本 KitKat 发布于 2013 年 9 月 3 日。它的功能包括内存更少的设备的性能优化,扩展的可访问性 API,无线打印能力,以及一个新的实验性运行时虚拟机,称为 ART,它可能会取代 Dalvik。KitKat 于 10 月 31 日在谷歌自己的 Nexus 5 智能手机上首次亮相。2013.

概观

通过所有这些版本的 Android,操作系统的功能和变化带来了丰富的以用户为中心的体验。对该设备的技术方面知之甚少甚至一无所知的普通用户可以像第二天性一样操作该设备。现在,您已经对使这成为可能的底层系统和架构有了更多的了解,剩下唯一要问的是,下一步是什么?

二、移动设备和操作系统的前景

到 2020 年,联网设备将达到 500 亿台。

—爱立信,2010 年

联网计算机。连接的设备。移动设备。机器对机器(M2M)。移动中(OTG)。便携式计算。智能服务。这份名单越列越多。用来描述向其他机器发送数据的机器的术语似乎每天都在变化。所有这些不同类型的设备看似相似,很容易混淆。

Android 肯定不是当今市场上设备上使用的唯一操作系统。各种手机操作系统已经存在,很多还在和安卓竞争。虽然这本书的重点是 Android 市场,但对竞争的理解——iOS、Windows Phone 等等——对任何成功的商业冒险都很重要,因此也是本章的重点。中国著名的将军和战略家孙子在他的《孙子兵法》中最好地表达了这一点:“如果你了解敌人和了解自己,你就不必害怕一百场战斗的结果。”

移动领域的竞争

Android 操作系统是当前商业市场中非常受消费者欢迎的选择。如今有超过 2.5 亿台安卓设备在使用。但 Android 不是唯一的选择,也不是第一个实现的移动操作系统。还有几个额外的移动操作系统,包括苹果的 iOS,英特尔和诺基亚的 MeeGo,微软的 Windows Phone 等等。这些操作系统的实现有许多不同之处,使用这些平台也有许多不同的原因。本节通过讨论它们的优缺点,提供了这些其他移动操作系统的简要概述。

iOS

由苹果公司开发和发行的 iOS 最初于 2007 年在最初的 iPhone 和 iPod Touch 上发布。仅次于 Android,iOS 目前是移动操作系统领域市场份额最接近的竞争对手。截至 2012 年 5 月,Android 拥有 50.9%的用户,而 iOS 平台为 31.9%。值得注意的是,iOS 最初被称为 iPhone OS,是基于它的主要启动设备 iPhone。

概观

与 Android 不同,iOS 从一开始就是一个封闭的源代码,只在有限的几个平台上发布。iOS 的每个新版本都包括主要由苹果公司开发和制造的新 iOS 设备。iOS 大致基于苹果的桌面操作系统 OS X,而后者又部分基于 UNIX 操作系统。(OSX 和 Linux,以及 iOS 和 Android,在 BSD Unix 中共享一个共同的开发祖先,BSD Unix 是由加州大学伯克利分校开发和发布的开源 Unix 操作系统变体)。

应用

iOS 附带了各种系统应用,如基本的电话操作、网络浏览器(Safari)、媒体播放器和电子邮件客户端。iOS 还能够运行开发者使用苹果 iOS 软件开发工具包(SDK ) 开发的各种第三方应用。应用可以访问所有设备外设,通常包括各种摄像头、用于检测设备移动的加速度计、麦克风、用于硬件加速的板载图形芯片和触摸屏。

Android 操作系统的第三方应用供应存在一些显著差异。为了开发 iOS 设备的应用,您需要购买 SDK 的开发者许可证。iOS 设备的应用通常用 Objective-C 编写,通常用 Xcode 开发,Xcode 是 OS X 平台的开发环境。此外,使用 SDK 创建的应用在 iOS 市场上销售之前,会经过苹果公司的筛选和验证。这使得苹果能够阻止开发者发布可能伤害其用户群的应用,如恶意软件或信息窃取者。

曾经有过苹果停止发布有效应用的情况。在一个案例中,一个第三方应用使用设备的音量按钮作为按下屏幕按钮的替代方法来用相机拍照。意识到这一点后,苹果从 App Store 中移除了该应用,声称这一功能“违反了苹果的政策”。第三方公司删除了这一功能,苹果最终发布了包含这一功能的机载摄像头更新。

平台

运行 iOS 的设备是通过苹果开发和销售的,因此差异很小。尽管这看起来有局限性,但是硬件多样性的缺乏使得应用能够被标准化。例如,由于只有几种可能的屏幕尺寸和图形硬件,应用开发人员只需处理几种不同的情况。iOS 主要出现在三个主要平台上——iPhone、iPad 和 iPod Touch。

  • iPhone——iPhone 是苹果的智能手机版本,最初于 2007 年 1 月发布。iPhone 的每一个新版本都包含了 iOS 的增量更新和新的主要功能。iPhone 的特点是一个口袋大小的设备,一个多点触摸屏,背面有一个摄像头(在新版本中前面有),还有一个用于音频的麦克风。
  • iPad——发布于 2010 年 4 月,iPad 是苹果公司创造和销售的平板电脑。它大约是一本标准杂志的大小。iPad 的屏幕比 iPhone 大得多,硬件也有所升级。每一代 iPad 都增加了重要的硬件升级和新功能。iPad 运行与 iPhone 和 iPod Touch 相同的应用;但是,如果需要,可以专门为 iPad 创建应用。
  • iPod Touch——几乎在所有方面都与 iPhone 相似,第一代 iPod Touch 于 2007 年 9 月上市。iPod Touch 和 iPhone 的主要区别在于 iPod Touch 缺少蜂窝通信功能。大多数为 iPhone 开发的应用都可以在 iPod Touch 上运行,开发者只需修改很少的代码,甚至不需要修改。iPod Touch 提供了一个在 iOS 上玩游戏的选项,无需支付 iPhone 手机套餐的订阅费或 iPad 的额外费用。

黑莓

黑莓手机有时被称为原始智能手机,于 2003 年推出。截至 2013 年第三季度,黑莓占据了 3%的移动智能手机市场份额。最初的黑莓手机有一个小的彩色屏幕,一个完整的 QWERTY 键盘,一个轨迹球和一个摄像头。与苹果的做法类似,黑莓设备由 Research in Motion (RIM) 在内部开发制造,该公司于 2013 年初更名为黑莓。黑莓最初的营销目标是为普通商务人士创造设备。这一重点包括检查电子邮件、访问互联网以及轻松高效地安排会议的能力。

Windows Phone

Windows Phone 由微软开发,作为 Windows Mobile 的继任者,是移动操作系统领域的第四大竞争对手。Windows Phones 于 2010 年 11 月进入消费者市场,与 Windows Mobile 不同,它的目标是远离企业市场。截至 2013 年第三季度,Windows Phone 和 Windows Mobile 占据了 2%的移动市场份额。Windows Phone 的布局与传统的智能手机用户界面有很大不同。微软非常注重易用性,以及与现有 Windows 服务(如 Windows Live)的连接。

Symbian

塞班操作系统是由埃森哲为诺基亚开发的,埃森哲是世界上最大的咨询和技术服务公司之一。截至 2012 年 5 月,塞班已经从 2009 年 2 月的 47%下降到 1.1%。Symbian 的功能包括各种应用、多点触摸屏、Wi-Fi、蓝牙和多任务处理能力。

MeeGo

2010 年 2 月,英特尔和诺基亚在世界移动通信大会上宣布了他们最新的冒险产品 MeeGo。MeeGo 是一个基于 Linux 的开源操作系统,面向各种移动设备。MeeGo 旨在运行在低性能设备上,如上网本、平板电脑、车载信息娱乐设备、智能电视和各种其他嵌入式系统。MeeGo 的用户界面非常类似于 Android,有各种各样的应用。2011 年 9 月,MeeGo 项目被取消,英特尔团队将他们的经验和技能带到了 Tizen,这是英特尔和三星之间的一个新的联合项目。

安卓之前

自从支持 Android 的设备出现在世界上和我们的口袋里以来,这似乎是一个漫长的过程。然而,大量的脚步和几位前辈导致了 Android 的创造和创新。尽管在 Android 出现之前,没有什么东西与它非常相似,但它的常见和受欢迎的功能显然是有启发的。

智能手机历史

在开放手机联盟移动设备出现之前,运行在这些设备上的软件是专门为每部新手机开发的。为 Android 操作系统做出的一些决定可以追溯到 21 世纪初的手机。

西蒙个人通讯器

许多人认为 IBM 和 BellSouth 的 Simon Personal Communicator (1994)是第一部智能手机。Simon 将个人数字助理(PDA)的许多功能与现有蜂窝设备的功能相结合。除了能够进行蜂窝通信,Simon 还有一个触摸屏和各种应用,如日历、游戏、记事本、计算器和触摸屏键盘。西蒙在拉斯维加斯的一次电脑交易会上启动了智能手机市场,西蒙的样机引起了人们的极大兴趣。西蒙的原型如此受欢迎,以至于在贸易展的第二天就出现在了今日美国的财经版的头版。

诺基亚 9000(诺基亚通信器)

1996 年推出的诺基亚 9000 在许多方面与西蒙相似,延续了智能手机的愿景和方向。诺基亚 9000 采用了双重方式——合上时看起来像一部笨重的手机,打开时显示出完整的 QWERTY 键盘和更大的水平屏幕。像西蒙一样,诺基亚 9000 的特色是各种应用,允许超出常规蜂窝设备的功能。

京瓷 6035

五年后于 2001 年发布的京瓷 6035 看起来更像现代智能手机。当关闭时,京瓷有物理按钮用作拨号键盘。当打开时,它有一个更大的垂直屏幕,包含各种应用和工具。京瓷的特色是 Palm OS,可以收发电子邮件和浏览网页。

黑莓 5810

RIM 发布的第一款黑莓手机是黑莓 5810 (2002 年)。它的特色外观一直伴随着黑莓直到今天。黑莓 5810 针对电子邮件和商务用途进行了优化,面向商务专业人士销售。功能包括一个大触摸屏,一个完整的 QWERTY 键盘和一个内置天线。

移动市场:成功与失败

在任何一个发展中市场,新的想法和创新可能会引起消费者的极大兴趣,也可能会让他们完全失去兴趣。移动领域也不例外。尽管许多移动设备销量很好,但也有许多设备损失惨重。本节重点介绍了一些最近在商业市场上成功和失败的移动设备。

摩托罗拉 i1

2010 年 6 月发布的摩托罗拉 i1 就是一个不太成功的移动设备的例子。虽然它是在 Boost Mobile 上发布的,没有必要签订合同,但 i1 只能运行 Android OS v1.5 (Cupcake),只能处理 2G 数据速度。与摩托罗拉 2010 年的其他设备 Droid X 相比,i1 卖得很差。

Droid X

Droid X 于 2010 年 7 月发布,远非一次失败。Droid X 采用 Android OS v 2.1–2.3,4.3 英寸多点触摸屏,8GB 内部闪存。随着 iPhone 4 的即将发布,摩托罗拉用 Droid X 进行了一场积极的营销活动,在 iPhone 4 上市的前一天宣布了这款设备。这似乎得到了回报,因为 Droid X 在网上和许多零售点销售一空。

黑莓火炬

黑莓 Torch 搭载了黑莓 OS 6 操作系统,RIM 首席执行官吉姆·巴尔西利(Jim Balsillie)称之为“超越任何现有产品的巨大飞跃”。然而,火炬在发布的前三天只卖出了 15 万台。相比之下,苹果 iPhone 3G 和 3GS 的销量都超过了 100 万部。在一个非常好的产品市场中,火炬是一个好产品的典型例子。虽然 150,000 台是一个相当大的数字,但这意味着黑莓无法维持其市场份额。

iPhone

作为有史以来最成功的设备之一,苹果的 iPhone 设备几乎每一次发布都能卖出数百万台,是市场成功的一个典型例子。专注于易用性和演示,iPhone 提供了不同的移动体验。苹果对 iPhone 的营销重点可以用简单来形容,用苹果的 logo 作为俘获过往 iPod 粉丝的手段。

移动市场:趋势

虽然不能保证未来,但明显的趋势有助于预测市场下一步的走向。移动领域也不例外;事实上,在某些情况下,它们甚至更容易辨认。移动设备及其用户之间的联系是一种不断发展的情况,创造了一个充满新可能性的世界。这种“网络纤维”可以让用户随时与周围的世界保持联系。

位置

大多数现代设备都有某种 GPS 或其他方法来定位你在世界上的位置。许多更受欢迎的应用已经利用这一特性来鼓励用户在旅途中使用他们的设备。无论您是在查看当前温度,在脸书更新中标记您的位置,还是试图找到回家的路,定位服务的使用越来越频繁。

随着 iOS 5 的发布,苹果在其核心操作系统中引入了位置功能。例如,当你路过某个地点时,你的移动设备可以提醒你需要去取杂货。这个功能的吸引力是显而易见的——不用考虑具体的时间,下次你经过药店时,系统会提醒你需要买更多的阿司匹林。在智能手机开发的所有领域,将定位功能嵌入应用是当前的热门话题。

当前手机用途

有了 Android 和所有其他操作系统,用户对于如何使用他们的移动设备有了无限的选择。但是消费者是如何使用他们的设备的,他们在设备上花了多少时间?

根据 2013 年 5 月皮尤互联网与美国生活研究,56%的美国成年人现在是智能手机用户。手机最常见的两种用途是浏览网页和搜索特定信息,这两种用途都占据了手机使用时间的绝大部分。脸书和 YouTube 拥有非常大的流量,截至 2013 年,脸书拥有超过 8 亿移动用户。

在所有智能手机用户中,约 59%的人每天在智能手机上使用网络应用和工具的时间超过 30 分钟。然而,每天 30 分钟内真正通过电话和短信交流的人的比例要低得多,只有 32%。随着我们的手机应用变得越来越丰富,电话等旧的交流形式已经转变为更新的社交信息形式,如脸书、Twitter 和 MySpace。

商业

由于移动设备几乎可以做笔记本电脑或家用电脑能做的任何事情,移动设备直接用于商业只是时间问题。无论是从亚马逊购买新产品,从应用商店购买应用,还是购买周日比赛的门票,移动设备都已经成为在旅途中购买商品和服务的一种方式。

移动商务市场正处于起步阶段。专家认为,到 2015 年,我们在手机上的花费将从不到 10 亿美元增加到超过 990 亿美元。

概观

连接现代社会的“网络纤维”非常明显。我们的移动设备让我们随时随地与世界联系。让我们如此轻松地访问这个丰富环境的应用和操作系统就像胶水一样将我们的世界粘在一起。下一章将详细讨论 Android 设备如何与现有技术交互,以及开发者可以使用什么样的界面。

三、超越移动应用——技术基础

在这一行,当你意识到自己陷入困境时,已经来不及自救了。除非你一直害怕,否则你就完了。

—比尔·盖茨

移动设备与我们的生活息息相关,不仅仅是简单的通信。联网设备就在我们身边:Wi-Fi 相框、汽车上的蓝牙接收器,甚至无线耳机。这些设备让我们能够以一种前所未有的方式与时俱进,与周围的一切保持联系。手机让我们以一种全新的方式与世界互动。

连接的设备

据英特尔公司副总裁 Kirk Skaugen 预测,未来几年将有超过 150 亿台设备连接到互联网。互联网目前支持超过 40 亿台联网设备,允许几乎不间断的通信。

支持这些连接的计算机芯片价格低廉,催生了一波联网设备的浪潮。以前没有无线功能的设备现在有了,这改变了我们与它们交互的方式。例如,现代电视包括无线芯片,允许它们直接通过互联网接收流媒体电视和电影。

家庭计算

这可能感觉像是旧闻,但家庭计算仍然是一个非常合法和有利可图的业务。在美国能源信息管理局进行的一项研究中,只有不到 3000 万美国人没有某种类型的家用电脑。与拥有一台或多台个人电脑的 8000 万美国人相比,美国比以往任何时候都更加互联互通。

自从智能手机问世并进入市场以来,个人电脑上的应用已经走过了漫长的道路。从智能手机使用和控制家用电脑的能力已经起飞,反之亦然。您可以在旅途中查看桌面,将文件和联系人从手机同步到笔记本电脑,将手机用作遥控器,或将手机中的视频直接传输到电视机。

汽车

现代汽车装载了大量的新技术。你可以坐在后座上看电视,通过蓝牙传输音乐,或者用免提与世界上的任何人通话。

智能手机为我们日常依赖的交通工具增加了更多的功能。通过蓝牙进行免提通话的功能通常包含在现代智能手机中。这种功能允许您在旅途中与他人进行合法交流。GPS 是智能手机上使用的另一种车内应用。你不再需要一个单独的 GPS 设备来绘制你的路线,这大大损害了独立的 GPS 设备市场。将这些设备的所有功能直接移植到智能手机上增加了便利性和易用性。此外,您还可以通过辅助端口将音乐传输到汽车的扬声器,找到您所经过的城市中最便宜的汽油,并找到正在发生的事情。

与汽车直接互动的应用才刚刚起步。随着手机变得越来越强大,汽车推出了更多的技术接口,新的智能手机应用将为此而创建。

数字娱乐

一个新的数字娱乐时代即将到来。现代世界的互联本质为娱乐设备和服务创造了新的可能性和用途。现在有多种方式可以随时通过互联网传输电视、电影和音乐。娱乐业接受并鼓励这些服务,通常提供付费点播内容。

随着这些功能的增加,智能手机应用开发人员为手机创造了新的用途。您可以将手机用作电视遥控器、从特定设备的 HDMI 端口播放电影的媒体中心,或者从收音机播放音乐的便携式媒体播放器。

特殊要求

市场的公共部分并不是唯一使用智能手机的地方。现代通信设备有许多军事和私人用途。无论是专门构建的加固设备、超远程功能还是绝密级通信,这些类型的设备都有许多特殊要求。

加固

有时也被称为硬化,加固 涉及改变设备,以便它可以在比普通用户可能遇到的更激烈的条件下使用。这些设备经常用于军事应用,可能需要在水下、沙尘暴中或雨中使用,或者可能需要在从很远的地方坠落后经受住冲击。技术规范准确地记录了这些设备必须能够处理什么,才能获得军事应用许可。

入口防护等级

入口防护(IP)等级代码 是一种针对外力防护等级进行分类的标准。IP 编码标准规定每个分类包含两个代表其物理和液体保护程度的字符。如果没有保护措施,则在该位置放置一个 X。

第一个数字是它对固体颗粒和物体的防护。你对第一个角色的最高评价是 6 分。得分为 6 意味着该设备具有完全的接触保护,以及没有灰尘进入。第二个字符描述了对液体,特别是水的保护。最高的水等级是 8 级,这表明该设备可以完全浸没在水下超过一米,任何时间都不会受到内部损坏。

IP 代码只是各种保护分类标准中的一种。在美国国内,也有国家电气制造商协会(NEMA) ,发布保护等级。在美国以外,有 IP 代码规范的扩展,以及全新的系统。例如,称为 IP69K 的德国标准适用于高压和高温环境。

医疗

医学界有自己的要求清单。医疗数据的信息和使用必须保密。在美国,医患保密协议非常重要,这些设备必须受到保护。在全国范围内选定的医院中,Android 平板电脑运行并包含敏感信息。患者的电子病历(EMR ) 包含了患者的整个病史,属于敏感信息。

为了保护这些设备,信息被加密和锁定。使用该设备需要密码认证,以及可能的二级认证形式以确保身份。这些设备还具有屏幕和设计,使医疗专业人员的生活更容易。这通常意味着大屏幕,易于阅读,应用专门迎合医疗情况。

虚拟化

硬件价格昂贵,在单个硬件设备上创建虚拟平台已经成为一种常见的做法。这节省了资金和资源,因为您可以随时替换虚拟实例,而无需接触硬件。虚拟实例还允许您与系统交互,而不需要任何特定的硬件。例如,运行在 Android 模拟器上的虚拟 Android 设备允许用户与它进行交互,就像应用部署在实际设备上一样。

安全通信

保持军事通信安全一直是一个挑战。在野外发送和接收安全通信是一个非常困难的问题。安全对话的能力是对抗力量的强大优势。安全通信的困难是一个多部分问题,很少有答案。您必须建立一种加密、解密和传输通信的方法,尽可能减少延迟。此外,您必须提供一些方法来确定谁该相信谁不该相信。

由于所有这些复杂因素,军方使用的硬件和软件往往比消费级产品落后好几年。在 Android 设备上,各公司一直在努力开发将用于战场和政府建筑的安全通信。

类型 1

美国国家安全局(NSA ) 已经认证了一种类型 1 的设备,或系统,用于安全信息。类型 1 认证是一个严格的过程,涉及广泛的测试和形式分析。一些分析领域包括密码安全、防篡改、制造和辐射测试。

联邦信息处理标准

联邦信息处理标准(FIPS )认证授予软件和计算机系统使用敏感和高度机密信息的权限。FIPS 有不同的分类,处理不同的标准。例如,FIPS 46-3 是美国数据加密标准(DES ) 的代码,而 FIPS 197 则处理高级加密标准(AES ) 。

我们互联世界的网络纤维

现代世界已经变得非常依赖于持续的连接,人们很容易忘记存在多少种连接形式。这些连接方法都有不同的好处和限制。为了找到符合您业务需求的正确匹配,对这些方法如何操作有一个坚实的技术理解是很重要的。

蜂窝网络

蜂窝网络是难以置信的技术和复杂的系统;即使是小型网络也需要专门的硬件和软件。蜂窝网络通过使用蜂窝塔从一点到另一点传输数据。蜂窝网络有两种主要的通信协议——全球移动通信系统(GSM ) 和码分多址(CDMA ) 。这些协议允许多种类型的数据从一个设备流向另一个设备。他们的数据包括语音通话、短消息服务(SMS ) 、多媒体消息服务(MMS ) 。开放移动联盟(OMA)对蜂窝网络上提供的许多协议和服务的技术细节和规范进行了标准化。

开放手机联盟

开放移动联盟 成立于 2002 年 6 月,是移动电话行业的标准组织。OMA 负责维护和创建移动领域的许多标准,并与各种标准团体合作。其中比较著名的有第三代合作伙伴计划(3GPP ) 、第三代合作伙伴计划 2 (3GPP2 ) 、互联网工程任务组(IETF ) 和万维网联盟(W3C ) 。

无线通信

无线通信在两个或更多没有物理连接的端点之间传输数据。这种连接的距离可以短至几英寸,也可以远至几十万英里。存在许多形式的无线通信,其中最流行的是 Wi-Fi、蓝牙和射频(RF)。每种方法都有不同的优点和问题。

Wi-Fi

如今生产的几乎每一个电子通讯设备上都有 wi-Fi;它允许从无线接入点(WAP)进行数据连接。根据环境的不同,Wi-Fi 网络的覆盖范围在 120 英尺(36 米)到 300 英尺(91 米)之间。常用的两种主要类型的 Wi-Fi 是 802.11b 和 802.11g。大多数 Android 设备都支持连接到互联网和本地网络的 Wi-Fi。在 Android 设备上使用 Wi-Fi 时,任何蜂窝数据连接都将暂停,直到连接丢失或 Wi-Fi 被禁用。在 Android 设备上使用 Wi-Fi 通常意味着更快的连接速度和更少的延迟。

蓝牙

蓝牙类似于 Wi-Fi,但它是一种更新的无线技术,由电信供应商爱立信于 1994 年创建。它适用于短距离传输数据,安全性高。蓝牙设备主要通过配对来工作,侧重于一对一的通信。通过在配对过程中要求物理干预以确保用户可以访问设备的两侧,安全性得到了提高。蓝牙支持直接数据传输,并且由于短距离和更高的数据速度,典型的蓝牙设备充当现有系统的接口或控制器。这些系统包括耳机、键盘、鼠标和移动设备。

大多数现代 Android 设备都支持蓝牙,支持蓝牙 2.0 或更高版本。这些 Android 设备可以播放音乐,在手机之间聊天,甚至使用蓝牙创建互联网热点。使用蓝牙时,Android 允许您的启用设备充当服务器或客户端。

手机界面

随着新技术被集成到智能手机中,我们与移动设备通信的方式已经发生了变化。现代手机不只是一个简单的带有某种显示器的物理拨号盘,而是包含多点触摸屏、振动马达、LED 通知灯、复杂的噪声检测、扬声器、加速度计和物理按钮。Android 开发者使用 Android SDK 提供这些技术。

触摸屏

全彩色触摸屏是一种更新、流行的技术,已经占领了智能手机市场。移动设备中使用的最好的触摸屏支持高分辨率、多点触摸和无刮盖。目前在移动领域使用的触摸屏主要有两种类型——电容式和电阻式。

电容性的

电容式触摸屏由绝缘体组成,通常是玻璃,涂有导电材质。当手指触摸表面时,电场会发生扭曲,测量电容可以找到按压的位置。由于这项技术,屏幕相当准确,只需要最轻微的触摸。电容屏最明显的缺点是需要某种电子传导材质来操作屏幕。这意味着触控笔必须导电才能工作(您的手套也必须导电)!

电阻的

电阻式触摸屏,顾名思义,有两个覆盖着电阻材质的柔性薄片。其中一张纸上有水平运行的传感器,另一张纸上有垂直运行的传感器。当屏幕接触时,精确的点是基于哪些线交叉来定位的。任何物体都可以操作电阻式触摸屏;然而,与电容式触摸屏相比,所需的力量要大得多,精度也不太准确。

苹果公司选择在其所有采用触摸屏的产品中仅使用电容式触摸屏。在 Android 设备市场领域,设备制造商在电容性和电阻性之间的选择上一直存在分歧。

振动电机

触摸屏只是与移动设备交互的众多方法之一。几乎所有现代智能手机都有某种振动马达。振动马达由一个较小的电动马达组成,该电动马达带有一个特意以不平衡方式附加的重物。当马达试图旋转时,重量的不平衡性质会导致设备摇动和振动。在安卓系统中,开发者可以完全使用这个马达,并且可以随时打开和关闭它。

LED 灯

现代智能手机包含一个或多个灯,可用作辅助通知手段。例如,在 Droid 2 上,收到未读文本信息后,指示灯会亮起绿色,如果更改了无线电状态,指示灯会亮起蓝色。灯光颜色和状态可以由任何 Android 开发者随意修改。

加速度计

许多现代智能手机都内置加速度计。加速度计是一种测量一个或多个方向加速度的电子芯片。Android 操作系统使用内置加速度计作为检测屏幕方向的手段。第三方开发人员通过开发将测量结果作为输入形式的应用,扩展了这项技术。

倾斜传感器

在许多方面与加速度计相似,倾斜传感器是一些现代智能手机中包含的嵌入式设备。Android 操作系统中使用倾斜传感器作为加速度计的替代,以确定设备的当前方向。

硬件按钮

除了触摸屏,许多手机都有物理按钮,可以更快地完成任务,如调节音量、让手机进入睡眠状态、返回主屏幕和使用摄像头。当 Android 应用获得设备的焦点时,这些按钮通常可以被重新编程,作为指定应用的输入。例如,在许多媒体应用中,音量按钮会改变以调节媒体控制器的音量,而不是电话铃声的音量。

概观

Android 设备不仅仅是通信和娱乐设备。它们被用于许多不同的方面,例如与标准个人电脑的交互、控制我们的交通工具、与数字娱乐系统交互等等。潜力超出了普通消费者市场。为了适应所有可能使用这些设备的情况,您必须满足一些特殊要求。设备可以被强化以在水下和极端任务环境中工作,可以被加密和保护以用于敏感区域,可以被虚拟化以用于测试和开发,甚至可以被安全地用于军事部队。这一切之所以成为可能,是因为网络光纤可以让设备随时保持连接。正是这种网络纤维创造了现代丰富多变的数字世界。我们可以通过各种媒介与我们的设备互动。高分辨率触摸屏提供了灵敏而精确的控制;振动马达可以通知我们新的事件;内置传感器可以检测设备方向的实时变化。所有这些界面,甚至更多,都可供 Android 开发者以任何可能的方式使用。

四、Android 开发——业务概述和考虑因素

数据是一种珍贵的东西,将比系统本身持续更久。

—蒂姆·伯纳斯·李

Android 行业有自己的准入要求。当你决定是否进行一项商业冒险时,充分理解这些要求和规范是很有帮助的。如前几章所述,Android 操作系统已经是移动设备的行业领导者,其市场份额逐年增长。随着 x86 在 Android 软件堆栈中的引入,现在支持为 x86 系统编写的应用,这进一步扩大了市场。

安卓市场份额

根据国际数据公司(IDC) 的数据,Android 2013 年第二季度的出货量已经达到 1.87 亿台。之前的纪录——1 亿台的销量——在 2012 年被安卓打破。这些数字使 Android 在智能手机操作系统中的市场份额高达 79%。与去年的 1.36 亿台相比,Android 增长了 74%以上。Android 的增长很大程度上可以归功于三星在上一季度的成功,三星占据了智能手机制造市场 39%以上的份额。IDC 的高级分析师 Kevin Restivo 这样评价 Android 的成功:

自从安卓推出以来,不叫 iOS 的智能手机操作系统的份额下降并不是巧合。智能手机操作系统不是一个孤立的产品,它是一个更大的技术生态系统的重要组成部分。谷歌拥有欣欣向荣、多元化的产品组合。它的许多竞争对手与移动操作系统的联系较弱,不具备这种能力。这一因素和其他因素导致了竞争对手的份额损失,很少有例外。

Android 市场份额的未来是一个更有争议的话题。一些行业专家声称,这是 Android 统治地位的巅峰。专家认为,随着新 iPhone 型号的发布,以及新的 iOS 更新,苹果可能会在未来几年从谷歌手中夺回市场份额。其他人声称这仅仅是开始,约翰·科特西耶对此做了最好的阐述:

安卓是一列已经离站的火车,它不为任何人而停。仅这一季度售出的 Android 手机数量就超过了 2007 年全年售出的各类智能手机的总数。

很难说智能手机操作系统市场的未来会是什么样子。没有人能肯定地说,但是市场份额的成功如何转化为利润呢?Android 是一个免费的开源平台,谷歌是如何从中赚钱的?

Android 如何赚钱

谷歌是一家巨大成功的公司;不可否认。但是谷歌如何从 Android 项目中赚钱呢?苹果不仅对他们的操作系统收费,还对升级和应用收费。谷歌的战略符合他们公司的主要收入来源,广告。

谷歌有各种途径从 Android 和它的软件栈中赚钱。首先,谷歌通过在浏览器和 Google Play 商店中做广告向安卓收取费用。这条广告渠道可能看起来很小;然而,这是谷歌大部分收入的来源。谷歌还从 Google Play 上的应用中收取版税,并对向 Google Play 商店添加内容收费。

这个行业领导者每年从谷歌网站上的广告以及通过 AdSense 在联网的“合作伙伴”网站上的广告中获得数十亿美元的收入。2012 年第三季度,谷歌的总广告收入超过 140 亿美元。谷歌首席执行官拉里·佩奇(Larry Page)将谷歌的移动业务估值为 80 亿美元。自去年的 25 亿美元以来,这一数字几乎增长了三倍。佩奇没有进一步细分这笔收入来自哪些部门;他只是对增长发表评论说,“用户在 Google Play 中为内容和应用付费。”谷歌的 SVP 和首席财务官帕特里克·皮切特对这一增长发表评论说:“显然我们没有细分类别。广告仍然是 80 亿美元中的大部分,是其中的绝大部分。”

Android 成功的原因

到底是什么让 Android 如此成功并具有商业可行性?一些专家声称,它的产品背后是强大的金融公司;其他人声称这是开源社区和免费共享思想的后盾;还有一些人声称是因为它的特性和广泛的开发能力。很难找到一个单一的原因,但指出成功的领域要容易得多。

免费

Android 一直是,也将永远是免费的。这使得所有相关方都能获得巨额奖金。制造商可以放心地构建硬件,因为 Android 堆栈将是免费的,开发人员可以编写应用而无需担心复杂的平台成本,研究人员可以发现缺陷并改进底层系统,而无需购买许可证和协议。

开源

开源社区是一个庞大的开发者和志同道合的个人的集合,他们相信共享信息可以带来更强大更好的产品。Android 是这个社区的孩子,多亏了 Google,它才得以开源。这允许来自世界各地的程序员贡献和扩展他们每天使用的软件。

定制

Android 软件栈允许你将你的设备个性化到你觉得舒服的程度。您可以更改颜色、手机响应通知的方式、删除和添加应用,甚至更改保护设备的方式。这种级别的定制允许用户享受和利用设备的最大潜力。

应用基础

Google Play 商店有超过 100 万个不同价格的 Android 应用。这些应用可以以开发人员可以想象的任何方式使用设备。与 iOS 的应用商店相比,Google Play 商店主要由免费应用组成。这是苹果应用商店产生更高收入的主要原因。考虑到这一点,2013 年第二季度,Google Play 商店的应用下载量超过了苹果应用商店。开发者可以从应用内部的广告中获得收入,这使得消费者可以免费使用应用。

硬件选择

与其他移动操作系统不同,Android 有最多的设备可供选择。目前市场上有超过 3900 款 Android 设备。消费者可以找到一款拥有他们需要的所有功能的设备。这些选择还覆盖了美国和欧洲国家的所有主要蜂窝服务提供商,提供了更大的覆盖范围。

设备价格

Android 设备为用户提供了各种价位。消费者可以根据对他们来说重要的功能来选择他们想要为 Android 设备支付多少钱。一些第三方移动服务提供商以现收现付的方式提供 Android 设备。对于更高的价格和服务合同包,最新和最棒的 Android 设备是可用的。你花的钱越多,你的设备包含的硬件和功能就越多。

传统和未来平台支持

Android 已经上市五年了,并且已经产生了大量的软件版本和硬件平台。由于平台的健壮性,Android 必须就如何处理过去的版本以及未来的修订做出选择。向后兼容是所有大型平台都面临的问题。

传统支持

Android 从 1.5 版本开始,被打包到硬件平台上,并存在于商业市场中。Android 软件完全向后兼容。这意味着为 1.5 版构建的应用可以在 4.2 版设备上充分使用。这缓解了开发者对失去新设备市场份额的担忧。然而,需要注意的是,这种兼容性没有利用 Android 的新功能。为了获得最好的质量和功能,应该为新版本重写应用。

Android 也有一些有趣的硬件要求。为了获得谷歌的 Android 认证,制造商必须满足某些条件,如拥有蜂窝无线电、GPS 功能和 Wi-Fi 芯片。然而,用户认为理所当然的许多事情并没有被强制或监管。这个列表包括屏幕尺寸,屏幕分辨率,内部存储大小,GPU 速度,甚至处理器规格。这为制造商提供了制造高性能设备、廉价的消费者友好型设备以及介于两者之间的任何设备的自由。

未来支持

谷歌对 Android 未来版本的策略是迎合向后兼容性,争取最大的市场份额。新版 Android 将拥有运行多年前开发的应用的机制。需要注意的是,新的应用不能在旧的系统上运行。如果您使用 Android 软件开发工具包 4.1 版开发应用,该应用无法在 2.3 版本的 Android 设备上运行。然而,为 2.3 Android 设备编写的应用无需修改就可以在 4.1 设备上运行。

为什么 x86 和 Android 适合您

Android 和 x86 家族 是一个进入门槛低,成功概率大的行业。这些低准入要求甚至给了最小的公司成功的机会,主要是因为启动成本是最低的。问题不是 Android 和 x86 是否适合你,而是你需要采取什么步骤才能成功。

交叉兼容性

x86 体系结构提供了在许多行业中部署的多样化系统网络。从收银机到电视,再到移动设备,甚至主要的公用事业控制系统,x86 平台无处不在。世界上很少有行业没有以某种方式整合 x86 体系结构。结合 Android 平台,拓展范围更大。

为 Android 编写的应用将在所有版本相同的 Android 设备上运行,而不管底层处理器是什么。这意味着,在 ARM Android 设备上编写和测试的应用几乎不需要任何努力就可以在英特尔 x86 Android 设备上发挥其全部功能。使用安卓 NDK 的应用是个例外。但是,通过简单的重新编译,应用应该已经启动并运行了。有关软件迁移的更多信息,请参见第七章:创建和移植基于 NDK 的 Android 应用。

进入壁垒

从经济学角度来说,用乔·S·贝恩的经典定义来说,“进入壁垒是一个行业中的现有卖家相对于潜在进入卖家的优势,这反映在现有卖家可以持续地将价格提高到竞争水平以上,而不会吸引新公司进入该行业。”

进入壁垒是许多公司能够积极主导某些行业的原因。例如,石油行业的准入门槛非常高。创办一家成功的石油公司的成本高得惊人,因为你需要拥有如此多的资源和工具来与现有的领导者竞争。

Android 行业的进入门槛非常低。在 Android 领域创建成功企业的成本和要求远低于大多数其他技术行业。最大的挑战包括找到一个产品创意和建立一个开发团队。有了强有力的想法和基础团队,成功只是开发和营销的问题。事实上,成本非常低,甚至个人也能在这个市场上取得成功。

Android 的安全性

就软件系统而言,安全性通常是提供商比客户更关心的问题。一个好的安全系统,可以深入讲解,不打折扣。Android 就是这些系统中的一个。围绕平台及其组件的安全性已被很好地记录和研究。有关该系统的更多信息和技术细节,请参考x86 上的 Android 安全指南。

应用安全

随着应用市场的引入,Android 的安全模型变得异常复杂。Android 必须保护自己的应用,并为第三方应用提供一定程度的安全性。它的安全系统必须足够简单,普通用户能够理解应用,并且它必须允许用户决定是否使用它们。

Android 对该问题的解决方案是使用权限。为了访问设备的某些功能,您必须注册相关权限。例如,要在应用中使用数据或 Wi-Fi 服务,您必须注册以使用互联网权限。当向用户显示应用时,也会显示不同的权限。如果一个应用试图在没有注册许可的情况下使用某个特性,这个应用就会崩溃。

应用安全性的另一个主要部分是应用之间的信息分离。如果一个应用可以自由地与手机上的其他应用进行交互,恶意的事情就可能发生。用户需要应用能够相互发送消息的情况有很多。Android 的内部应用消息系统使用 intents 的概念在整个操作系统中传递信息。

意图只是由应用产生并传递给 Android 的自由格式的消息。这些消息可以有各种类型的数据,主要有两种类型。一个隐含意图是一个为任何可以访问它的应用而存在的消息。例如,在许多 Android 应用上,当你点击一个链接时,会出现一个窗口,让你从所有几个可以查看该网站的应用中进行选择。在后台,应用触发一个隐式意图,然后传递给所有可用的应用。另一方面,明确的意图是针对一个非常具体的应用。这种意图只有为其设计的应用才能看到和处理。

第三方应用也作为底层操作系统上的独立用户运行。这意味着第三方应用不能访问另一个应用拥有的文件和资源。例外情况是系统应用。系统应用可以访问设备运行所需的所有部分。

平台安全性

围绕手机及其机载功能的安全性是一个广泛讨论的话题。Android 提供了许多不同的安全功能来帮助保护用户及其数据。其中一些功能包括屏幕锁定、文本和电子邮件加密、多种类型的密码,以及当您访问设备的某些部分时的额外密码提示。这些功能验证经过身份验证的用户。

还创建了第三方应用来帮助这项工作。如果您的设备被盗,现有的应用可以帮助您找到设备,从您的手机中删除数据,远程锁定设备,并为您通过设备进行身份验证的方式添加更多定制。

许可

在软件开发行业,许可费非常普遍。从库许可成本,到平台许可成本,甚至设备许可成本,在很多情况下,你需要花一点钱来开发和销售产品。由于 Android 是一个开源和开发者友好的社区,它的目标是保持低成本。

安卓授权费用

没有。Android 是在 Apache 2.0 许可下开源的,允许完全免费的商业使用、修改和分发。这意味着任何人都可以玩 Android 操作系统的源代码,并从中创造出一个全新的产品。

应用许可成本

为 Android 创建应用是一个稍微复杂一些的过程。开发套件和软件开发工具包对任何想下载的人都是免费的。使用这些工具,您可以从源代码构建、测试和部署任何 Android 应用到开发设备。发布你的 Android 应用是一个不同的故事。

Android 设备可以通过 USB 和 SD 卡安装第三方应用,前提是该选项已启用,但这些媒介对消费者来说不是很方便。进入谷歌 Play 商店,这是一个 Android 应用市场,开发者可以快速轻松地上传和更新应用,供所有 Android 用户查看和购买。

在谷歌 Play 商店销售应用的过程非常简单。第一步是建立一个谷歌账户。这使您可以访问 Android 开发网站,在那里您可以管理您的应用。一旦你创建了你的账户,谷歌需要一次性支付 25 美元的费用来在 Google Play 市场内分发应用。然后,您可以上传并配置您的应用,以在市场上进行分发。

现在你有了一个想要分发的应用,你如何赚钱,那看起来像什么?要从您的 Android 应用销售中收款,您需要一个 Google Checkout 商家帐户。在 Google Play 商店购买您的应用将会存入您的 Google Wallet,该钱包可以通过各种方式转换为美元。谷歌确实会从你的所有利润中抽取一定比例,这是基于很多因素,比如应用销售额和销售率。

物理开发成本

当你开始从事软件行业时,重要的是要记住你要创建的虚拟产品是有物理需求的。与传统工程不同,被出售的东西不一定有物理组件。要创建和全面测试 Android 应用,需要几个物理平台。

软件开发系统

开发者使用软件开发系统来编写将在 Android 设备上运行的代码。Android 软件开发工具包(SDK)可以在 Windows、Linux 和 Mac 上运行,因此选择操作系统取决于开发人员。说到硬件选择,有几件事需要考虑。

Android SDK 是一个相对较大的应用,尤其是在它下载了运行不同版本 Android 所需的文件之后。获得一个足够大的硬盘来容纳所有需要的工具和文件是必须的,但是硬盘速度也是一个严重的问题。随着固态硬盘价格的下降,值得考虑开发一个。固态硬盘的读写速度通常是传统硬盘的两倍以上。这相当于节省了大量时间——系统启动速度更快,集成开发环境(ide)运行速度更快,应用运行速度也更快。

如果您计划在笔记本电脑上运行 Android 模拟器,那么为笔记本电脑投资大量 RAM 也是值得的。4GB 或更高应该足够了。如果小于 2GB,就会有问题。随着系统内存的增加,可以同时运行更多的应用。

最后,您需要一个足够强大的处理器来运行 SDK 和所需的开发工具。选择取决于您的开发团队;但是,越新越好。多核处理器总是一个优势。

安卓测试系统

Android 模拟器能够实现物理 Android 设备提供的几乎所有功能。即便如此,在真正的硬件上进行测试也是至关重要的。真实的硬件系统将完全按照客户的预期做出响应,因此在真实的硬件上进行测试可以获得更加自然的体验。

单个 Android 设备是不够的。在与您的目标版本相同的 Android 版本的多个设备上进行测试非常重要。如果应用是为以前版本的 Android 开发的,那么在有新版本的设备上进行测试也是必不可少的。你用来测试软件和确认可用性的设备种类越多,你在发布前发现的错误和问题就越多。在客户处理问题之前发现并解决问题将有助于建立你的产品。

概观

Android 和 x86 是一个强大的组合。凭借较低的准入门槛、强大的安全后盾、较低的许可成本以及部署应用的简单机制,Android 是一个容易涉足且有利可图的行业。得益于 Android 的成功和谷歌的利润,Android 堆栈得到了越来越多的支持。Android 和 x86 是开发的正确选择。通过对开发团队和产品的仔细考虑,可行的策略和成功的管理,Android 生态系统可以赚很多钱。

五、英特尔移动式处理器

如果通用汽车能像计算机行业一样跟上技术的发展,我们都会开着 25 美元、每加仑能跑 1000 英里的汽车。

—比尔·盖茨

英特尔是最初的微处理器设计者:第一个商业化的微处理器是 1971 年的英特尔 4004。英特尔处理器目前主导着高性能市场,占领了几乎所有的现代高端服务器。英特尔的奔腾系列处理器在 20 世纪 90 年代的个人计算中无处不在,其 Core i 系列是当今笔记本电脑和超极本最受欢迎的中央处理器。此外,英特尔凭借移动专用微处理器产品进入移动市场,与移动领域的市场领导者 ARM Ltd .展开竞争。结合 Android 操作系统的健壮性和灵活性,x86 系列处理器的强大功能和兼容性为移动市场带来了极具竞争力的新设备家族。

英特尔的 x86 产品线

x86 构成了庞大的英特尔处理器家族的基础架构,从最早的英特尔 8086 到奔腾系列、I 系列、最新的配备虚拟化管理程序的服务器处理器、专为移动和嵌入式应用设计的低功耗 Atom 和 Haswell 微处理器 ,以及面向可穿戴计算的微型 Quark 片上系统。最初,8086 架构是为嵌入式系统设计的。但是,英特尔 8086 架构的早期实现非常成功,导致了一长串的修改和升级,增加了功能和丰富的特性。

x86 架构是一个复杂指令集计算(CISC)系统 ,由更复杂的指令构建而成,便于使用和简化实现。英特尔移动处理器的主要竞争对手 ARM 是精简指令集计算(RISC)系统 ,没有这些特性。例如,在 RISC 系统中,将一个给定的值加载到内存中可能需要三到四条指令,而在 CISC 系统中,只有一条专门编写的指令来完成这个任务。x86 架构也是基于寄存器到内存的,这意味着指令可以影响寄存器和内存。

历史

英特尔是世界上历史最悠久的半导体制造公司之一,以在计算机硬件和相关行业开发创新和功能技术而闻名。该公司由鲍勃·诺伊斯和戈登·摩尔于 1968 年创办。风险投资家亚瑟·洛克(Arthur Rock)用 1 万美元的初始投资和后来的 250 万美元的出资巩固了这家公司,从而获得了董事长的职位。

英特尔在 1969 年发布了他们的前两款产品:3101 肖特基双极随机存取存储器,以及 1101,世界上第一款金属氧化物半导体(MOS) 。如前所述,第一款英特尔处理器发布于 1971 年,它被称为 4004。

1978 年,英特尔首次发布了将改变世界的 8086 系列处理器架构。仅仅五年后,英特尔可以正式称自己为十亿美元公司。英特尔是全球最大的半导体公司,根据 2012 年年终报告,其市场份额为 15.7%,收入为 475 亿美元。最初的 x86 体系结构已经拆分、多样化、添加了新的规范,并被重新塑造成更小的外形,继续在世界各地的产品中使用。对英特尔来说,在 x86 平台上整合 Android 只是又向前迈进了一步。

因为 x86 体系结构已经在很多技术中使用,从服务器到个人电脑、移动电话、笔记本电脑和平板电脑,所以汇编其设备的完整列表将非常困难。它的广泛使用导致开发人员创建了专用于 x86 平台的工具、应用、框架和库。

这一切都始于 1978 年的英特尔 8086,最初是作为英特尔 8080 8 位微处理器的实验性 16 位扩展而构建的。8086 是驱动“IBM 个人电脑”及其所有复制品的处理器。x86 一词源自 8086 的后继产品,所有产品都以“86”结尾 1985 年,英特尔继续采用 x86 架构,推出首款 32 位处理器英特尔 80386。直到 2005 年,随着奔腾 4 的发布,x86 64 位处理器才进入市场。

英特尔基于 x86 架构的最新家庭计算处理器系列被戏称为英特尔酷睿 I 系列。该系列支持 64 位操作,专注于性能和速度。所有处理器都支持超线程,并拥有多个内核,支持并发处理。与个人计算核心 I 系列微处理器并行运行的是面向移动设备的基于 x86 的凌动系列。

优势和劣势

作为半导体市场的行业领导者,英特尔处理器拥有独特的优势。首先也是最重要的一点,英特尔处理器拥有其他处理器中最高的性能。该性能包括处理器速度以及内核和虚拟内核的数量。x86 体系结构还允许开发人员访问最大的可用软件集合。最后一个主要优势是使用英特尔 CPU 的高端系统的可扩展性;处理器的增加直接提高了性能。

表 5-1 强调了英特尔凌动处理器家族的一些差异,这些处理器家族采用 x86 指令集。英特尔凌动处理器家族针对每种平台类型包含许多不同的品种,包括平板电脑、智能手机、上网本和其他移动消费电子产品,而表 5-1 代表了每种平台的可比高端型号。

表 5-1 。英特尔凌动处理器家族对比

安卓 x86 教程(一)

有些情况下,x86 系列不是微处理器的正确选择。英特尔家族在物理上比其他品牌的 CPU 大得多,在 Core 系列中占据了超过一英寸的空间。直到英特尔凌动系列,英特尔处理器的功耗对嵌入式设备的要求太高;然而,领先的凌动处理器与 ARM 竞争电池寿命。最后,英特尔处理器的成本非常高,而且在有些系统中,4 核 3GHz 处理器是多余的。在这些情况下,可能需要使用 ARM 或其他低性能 CPU。

商业模式

在家庭计算方面,英特尔一直在为笔记本电脑、超极本和台式机平台生产功能强大、高能效的处理器。最接近的竞争对手是半导体公司 Advanced Micro Devices,Inc .在 2006 年,台式机市场曾一度接近被 AMD 和 Intel 瓜分,但现在不再是这样了。截至 2012 年 11 月,英特尔 CPU 的市场份额约为 71%,而 AMD 为 28%。

随着笔记本电脑和平板电脑越来越受欢迎,英特尔发布了凌动系列处理器。凌动处理器平衡了热量和功率,其性能专门针对需要长时间使用电池的产品。Atom 系列可用于超过 1 亿台设备,目前正在扩展到移动市场。

移动巨头的冲突:ARM 对 Intel

ARM 于 1983 年进入微处理器市场,并在某些领域成为强有力的竞争对手。英特尔及其基于 x86 的处理器已经成功占领了大部分桌面和家庭计算市场。另一方面,ARM 是目前移动和嵌入式设备市场的领导者。下一节详细讨论每个公司的处理器的特性,包括它的优点和缺点。

手臂

如果你看看单位销售额,ARM 是目前移动领域的赢家。目前市场上有超过 300 亿台设备,每天售出 1600 万台,ARM 每年的收入超过 9 亿美元。ARM 的历史、业务战略和未来计划都与 ARM 的成功有关。

历史

ARM 的故事始于英国个人电脑公司 Acorn。最初的 Acorn RISC 机器是在 1984 年到 1985 年间开发的。1982 年,在 ARM 之前,英国广播公司(BBC)与 Acorn 签约开发一种家用电脑,后来被称为 BBC 微型计算机。BBC micro 取得了巨大的成功,并使 Acorn 从一个只有几名员工的小公司成长为拥有数百名员工的中型企业。

在 BBC 微型时代结束时,Acorn 开始寻找下一个处理器来推进他们的新个人电脑。Acorn 尝试了各种 16 位和 32 位处理器,包括苹果 IIGS 中使用的 65C816,但没有找到一款具有 Acorn 所需性能的处理器。Acorn 解决这个问题的方法很简单,就是开发一种新的处理器,ARM1。

尽管 ARM1 拥有令人难以置信的能力和性能,但直到第一个真正基于 ARM 的平台 Archimedes 的发布,ARM 1 才得到广泛使用。阿基米德是一台台式电脑,于 1987 年中期发布,主要用于学校和其他教育环境。尽管取得了不太大的成功,并得到了消费者的响应,但 ARM 团队继续努力,开发了 ARMv3,专注于提高性能,以与英特尔和摩托罗拉工作站竞争。

1990 年,Acorn RISC 机器成为高级 RISC 机器,高级 RISC 机器有限公司成立。在创始合作伙伴苹果、Acorn 和 VLSI Technology 的帮助下,该公司成立的唯一目的是继续开发 ARM 处理器。在此基础上,ARMv6 诞生了,并于 2002 年 10 月发布给授权厂商。ARMv6 架构及其最近的亲戚 ARMv7 和 ARMv8 如今广泛用于嵌入式和移动设备。

优势和劣势

ARM 处理器有一些非常吸引人的品质。首先,它们非常小。事实上,最现代的 ARM11 系列处理器还不到 2 毫米 2 。由于外形小巧,使用时产生的热量通常很低,足以避免任何类型的散热器或冷却系统。即使很小,ARM 芯片也可以在一片硅片中包含许多核心系统组件。这些组件包括 CPU、GPU 和 DSP。最后一个主要优势是相对于竞争对手而言功耗极低;一些报告声称节省了高达 66%的成本。用电量越少,电池寿命越长,电费就越便宜。

表 5-2 展示了目前在移动市场上使用的一些较为流行的 ARM 处理器。该表只是 ARM 为移动设备提供的众多选项中的一个示例,但是与表 5-1 的比较表明了与英特尔处理器家族的显著差异。可比的 ARM 移动处理器提供的处理器速度要低得多,即使在 A15 的高端也是如此。

表 5-2 。ARM Cortex-A 系列对比

安卓 x86 教程(一)

尽管 ARM 芯片有很多优点,但也有很多缺点。首先,ARM 芯片缺乏任何繁重处理情况所需的严肃性能。ARM 处理器本身的可扩展性也较差,尤其是与现代英特尔 CPU 相比。ARM 的软件需要专门为架构创建;幸运的是,ARM 已经有了一些更常用的工具和工具。

商业模式

对 ARM 公司决策的分析有助于揭示他们在处理器市场的关注点。显而易见,人们看重的是 RISC 架构、高性能、低功耗和低价位。这些差异使 ARM 非常适合移动市场,也是 ARM 处理器几乎只用于智能手机的关键原因。

然而,ARM 处理器不是由 ARM 有限公司销售或制造的。相反,处理器架构被授权给相关方。ARM 有限公司提供各种条款和不同的费用。对于所有的被许可方,ARM 提供了深入的文档、完整的软件开发工具集,以及带许可 CPU 的制造芯片的销售权。

这种商业模式为公司带来了好处;在 2013 年第二季度,ARM 报告将 51%的收入归类为版税,39%来自许可。该报告继续详细说明了版税和许可证的数量。每单位版税的平均成本约为 0.07 美元,超过 26 亿单位。另一方面,该季度签署了 25 份新许可证,平均每份许可证价值约 184 万美元。

将来的

ARM 公开发布的最新处理器是 ARM7,有各种修改的实现。ARM7 广泛应用于现代智能手机市场。公司内部有传言称,ARM 将在处理器方面寻求更多发展方向。

随着面向 x86 的 Windows 8 的发布,微软为 ARM 处理器创建了一个名为 Windows RT 的 Windows 版本。Windows RT 几乎完全是从零开始编写的,并且已经设法消除了许多(但不是全部)现代向后兼容 Windows 版本的瓶颈。测试表明,RT 应用的运行速度比竞争对手英特尔芯片上的相同应用快 20%。

专家还预测 ARM 将进入服务器和数据中心市场。在基于 ARM 的 Linux 服务器操作系统的支持下,这越来越成为现实。可以想象,在 ARM 上运行高性能系统意味着更低的功耗。这在高端 ARM 系统的当前性能与高端 Intel 系统的性能之间还有待观察。

英特尔的凌动系列微处理器

凌动处理器是移动设备的特色。典型设备包括小型笔记本电脑、上网本、平板电脑、电视和新型智能手机。Atom 平衡了性能和功耗,使设备的电池续航时间更长。

随着超过 1 亿个 Atom CPUs 的出货,Atom 的影响力显而易见。与所有英特尔处理器一样,凌动是英特尔架构(IA)家族 的一员。IA 系列独特的交叉可移植性允许处理器之间快速轻松的转换。

英特尔凌动进化

英特尔凌动处理器是主要用于笔记本电脑的低功耗处理器英特尔 A100 和 A110 的继任者。A100 和 A110 的代号为 Stealey,最初的尺寸为 90 纳米。表 5-3 和 5-4 强调了 Atom 在平板电脑和智能手机上的一些迭代,从 2008 年 4 月处理器家族的婴儿期到其现代版本。

表 5-3 。英特尔凌动智能手机处理器

安卓 x86 教程(一)

表 5-4 。英特尔凌动平板电脑处理器

安卓 x86 教程(一)

乍一看,表 5-3 中列出的处理器似乎只是稍微好一点,但是为了真正理解发生了什么,您需要考虑所有的变量。Penwell 是英特尔今天为智能手机生产的处理器的先驱,尺寸仅为 32 纳米,支持多核,具有嵌入式 GPU 支持的顶级操作频率。对于现代设备制造商来说,这显然是英特尔的选择。

与表 5-3 中的现有处理器相比,表 5-4 中列出的平板电脑处理器能力更强。这些平板电脑处理器支持更多内核,具有更快的 GPU 速度,有助于容纳更大且通常高分辨率的显示组件。

英特尔凌动安全技术

在当今时代,随着技术的发展,安全性始终是一个问题。英特尔凌动处理器支持许多安全特性。其中包括安全启动、英特尔平台信任技术、硬件增强加密和操作系统级密钥存储。安全启动是当前统一可扩展固件接口(UEFI)规范的一部分,用英特尔自己的话说就是最好的描述:

启用并完全配置后,安全引导有助于计算机抵御恶意软件的攻击和感染。安全引导通过验证数字签名来检测对引导加载程序、关键操作系统文件和未授权选项 rom 的篡改。检测在攻击或感染系统之前被阻止运行。

英特尔平台信任技术(简称 PTT)是平板电脑上的虚拟智能卡读卡器,允许通过 CPU 进行基于证书的认证。

英特尔凌动特性

英特尔凌动处理器支持其他英特尔处理器中存在的大量特性。能效是英特尔世界的一个新概念,而凌动将这一概念推向了前沿。凌动处理器可以定制,以实现超低功耗与不同性能可扩展性选项之间的正确平衡。在性能方面,凌动支持英特尔超线程和英特尔突发技术,以帮助处理所需的性能和能效。英特尔推出的凌动处理器的最后一个主要特性是移动性概念,支持 NFC、高级相机成像、3G 和 4G LTE。

Android 和 Atom

Atom 处理器是当前 Android 平台首选的 x86 处理器。Atom Android 团队带来了一个装满顶级功能的衣柜。这包括支持多种格式的 1080p 高清 3D 图形、屏幕共享和设备配对、优化的网页渲染和简单的交叉计算能力。Atom Android 平台支持 Android SDK 应用的开箱即用。在大多数情况下,Android NDK 应用只需要重新编译就能得到完全支持。关于兼容性和转换过程的更多信息可以在以下标题为应用兼容性的章节和 第七章:创建和移植基于 NDK 的 Android 应用中找到。

Medfield 片上系统内部

英特尔的 Medfield 平台旨在用于运行 Android 操作系统的智能手机和平板电脑。一款名为英特尔凌动 Z2610 片上系统(SOC) 的 Medfield 模型将在稍后详细讨论(见图 5-1 )。如前所述,英特尔最近开始生产独立的移动处理器,包括一个代号为 Penwell 的处理器。虽然 Penwell 处理器包含一些与 Medfield SoC 相同的部分,即 Saltwell 系列微处理器架构,但 Penwell 是一款主要针对智能手机的独立处理器,而不是 Medfield 针对智能手机和平板电脑的多部分高性能系统。

安卓 x86 教程(一)

图 5-1 。梅菲尔德框图

这个 Medfield 模型,Z2610,在物理上分为两个复合体,北复合体和南复合体。North Complex 包括一个 Saltwell 系列单核处理器、一个 32 位双通道 LPDDR2 内存控制器、一个 3D 图形内核、视频解码和编码引擎、一个能够支持多达三台显示器的 2D 显示控制器,以及一个用于摄像机输入的图像处理器。South Complex 包括完成智能手机设计所需的所有 I/O 接口,如安全引擎、支持 SD/eMMC 存储卡的存储控制器、USB OTG 控制器、3G 调制解调器、免费无线解决方案(CWS)接口、SPI 和 UART。参见图 5-1 。

放大 Saltwell CPU 架构

Saltwell CPU 架构相当简单。该设计的理念是创建一种在优化性能和高效功耗之间取得平衡的处理器。该处理器使用有序架构,这不同于市场上的大多数其他处理器,它们使用无序执行。该处理器有一个 64kb 的 L1 缓存和一个 512k b 的 L2 缓存。该处理器支持英特尔突发性能技术,可让处理器动态提高 CPU 速度。Saltwell 有三种频率模式:低频模式(LFM) 运行在 600MHz,高频模式(HFM) 运行在 900MHz,突发频率模式(BFM) 运行在 1.6GHz。在电源优化特性中,Saltwell 有一个超低功耗智能 L2 高速缓存,在 CPU 处于 C6 状态时保存数据,以降低 C 状态恢复期间的延迟。此外,Saltwell 为内核和 SoC 的其余部分提供了单独的电源层和时钟输入,这使得电源和时钟门控可以通过英特尔智能闲置技术(英特尔 SIT)轻松配置。该技术可使 CPU 在 SoC 仍处于开启状态(S0 状态)时完全关闭。

英特尔的 Saltwell 和 ARM 的 Cortex A15 的架构差异

如书中所列,脱离英特尔凌动处理器:架构迁移指南, 1 英特尔凌动架构与 ARM 架构在各方面都有很大不同。表 5-5 显示了 Saltwell 和 ARM Cortex 架构之间的高级差异列表。

表 5-5 。Saltwell 和 ARM 之间的高级别差异(Cortex A15)

|

特征

|

索尔特韦尔

|

手臂皮层

|
| — | — | — |
| 技术 | 32 纳米 | 28 纳米 |
| 体系结构 | 按顺序 | 无序的 |
| 整数流水线 | Sixteen | Fifteen |
| L1 高速缓存 | 64KB | 可配置高达 64KB |
| L2 高速缓存 | 512KB | 最大 4MB |
| 指令组 | IA32,英特尔流 SIMD 扩展,英特尔补充流 SIMD 扩展 3 | 手臂,拇指 |
| 多核/线程支持 | 采用英特尔超线程技术的单核 | 多核 |
| 安全技术 | 英特尔智能安全技术(英特尔 S&ST) | TrustZone*技术 |

架构

如前所述,Saltwell 的架构与英特尔凌动系列的其他处理器相似。它使用有序执行设计。对于有序处理器,所有指令都按照它们被提取的顺序来执行,而无序处理器能够同时执行多条指令,并且稍后在流水线中对它们进行重新排序。ARM 处理器使用乱序架构,这种架构的优势是以最小的延迟执行指令。然而,这增加了核心设计的复杂性。取消重新排序逻辑是英特尔凌动处理器的节能举措之一。

整数流水线

英特尔凌动管道分为六个阶段:详情列于表 5-6 中。

表 5-6 。英特尔凌动指令阶段和流水线阶段

|

阶段

|

流水线阶段

|
| — | — |
| 取指令 | three |
| 指令解码 | three |
| 指令发布 | three |
| 数据存取 | three |
| 执行 | one |
| 回复 | three |

这种指令架构导致英特尔凌动处理器中共有 16 条整数流水线,并且需要三个额外的阶段来执行浮点指令。最新的 ARM 处理器有 15 个整数流水线。ARM 处理器中漫长的流水线牺牲了能量和性能。Saltwell 每个时钟周期可以解码多达两条指令,而最新的 ARM 处理器是三重超标量体系结构。

指令集

ARM 指令集始终是 32 位的,并在一个 4 字节的边界上对齐,而 IA32 指令集大小不同,不需要任何对齐。ARM 指令和 IA32 指令的另一个区别是指令的执行方式。对于 ARM,所有指令都有条件地执行,以减少分支开销和分支期间的误预测。每个指令都需要满足条件标志才能生效,否则该指令将作为 NOP 并被丢弃。英特尔架构中也有条件指令;这些被称为条件 MOV 指令。IA32 中的其他指令不是有条件执行的。

多核/线程支持

如前所述,Saltwell 支持英特尔超线程技术(英特尔 HT 技术),通过使用共享资源来完成任务。该技术的细节将在下一节中进一步讨论。ARM 多核架构拥有独特的资源,可在每个内核上执行任务。内核的一致性由 AMBA 4 AXI 处理,这是一个兼容的从接口,直接与内核接口。

安全技术

Medfield 中有一个名为英特尔智能安全技术(英特尔 S&ST)的安全子系统。它是一个完整的硬件和软件安全架构。该子系统符合行业标准,支持 AES、DES、3DES、RSA、ECC、SHA-1/2 和 DRM。它还支持 1,000 位 OTP 并支持 SecureBoot。安全系统在 ARM 处理器中的实现是不同的。英特尔实施的安全子系统没有单独的控制器。ARM 处理器使用 TrustZone 技术,其中系统中的资源(如处理器和内存)被分为两个世界:正常世界(??)和安全世界(??)。这种信任区体系结构有三个动机:

  • 提供一个安全框架,允许设计人员根据用例定制所需的功能。
  • 在不需要为安全任务配备专用处理器的情况下,节省芯片面积和功耗。
  • 通过提供单个调试组件,防止在调试期间对安全世界中的安全敏感任务或正常世界中的非安全敏感任务的入侵。

英特尔超线程技术

英特尔超线程技术(英特尔 HT 技术)支持软件查看物理处理器封装中的多个逻辑处理器。Saltwell CPU 架构使用英特尔超线程技术来提升其性能。在单个有序架构处理器中拥有第二个线程,可以使 Saltwell 在一个时钟周期内执行多个指令,并在两个线程之间共享执行资源,与单线程处理器相比,性能提高了 50%,如图图 5-2 所示。

安卓 x86 教程(一)

图 5-2 。英特尔超线程技术的优势

在英特尔超线程技术中,处理器拥有由通用寄存器、控制寄存器、高级可编程中断控制器(APIC)寄存器和一些机器状态寄存器组成的架构状态副本。架构状态的复制是软件可以将一个内核处理器视为两个逻辑处理器的原因。两个线程共享缓存、执行单元、分支预测器、控制逻辑和总线。这就产生了一个问题,即线程之间可能存在资源争用和工作负载不平衡。然而,大多数当前的开发套件(如 Dalvik 和 JavaScript)已经具备支持多线程环境的能力,为开发人员提供了一种简单的方法来生成利用英特尔超线程技术优势的应用。Android 上的应用开发人员还可以利用英特尔 VTune 性能工具来分析工作负载,并对他们的应用执行资源调整。

应用兼容性:原生开发套件和二进制翻译器

Android 已经移植到 x86,所有进一步的版本都将在 x86 和 ARM 架构中提供。在英特尔凌动平台上运行操作系统不成问题。然而,在某些情况下,现有的 Android 应用可能需要在修改或不修改源代码的情况下重新编译。

据信,谷歌 Play 商店大约 75–80%(通常引用的数字)的 Android 应用运行在 Dalvik VM 之上,并使用 Android 框架(见图 5-3 )。使用 Android 软件开发工具包(SDK)用 Java 语言编写的绝大多数 Dalvik VM 应用都是与处理器无关的。它们在英特尔凌动平台上透明运行,无需移植。对于包含 C 和 C++ 代码的应用子集,开发人员需要使用最新的 Android 原生开发工具包 (NDK)重新编译他们的代码。

安卓 x86 教程(一)

图 5-3 。Android 框架

在大多数情况下,NDK 应用开发人员只需重新编译项目,该项目支持 x86 (x86)、ARMv5 (armeabi)和 ARMv7 (armeabi-v7a)。针对 x86 的编译(通过带有编译器标志-march=i686 –msse3 –mstackrealign –mfpmath=sse的 GCC)将生成完全符合英特尔凌动 CPU 特性集的代码。只有使用 ARM 供应商特定功能的应用才需要重写源代码,然后重新编译。

所得到的 APK 应用包可以包括用于 x86、ARMv5 和 ARMv7 的三个版本的机器代码。在安装时,只有适当版本的代码被解包并安装到目标平台上。

其余的应用要么是使用为 ARM 构建的 Java 本地接口(JNI)库的 Dalvik VM 应用,要么是未针对 x86 编译的本地开发工具包(NDK)应用。由于调用了本地库(尤其是特定于 ARM 的本地库),这些应用无法在英特尔凌动平台上运行。

英特尔和谷歌携手合作,确保在英特尔凌动平台上“按原样”执行原生应用,无需进行移植。英特尔提供二进制翻译(BT),在执行过程中将 ARM 代码动态翻译为 x86 代码,如图 5-4 中的所示。这种转换减轻了尚未移植到 x86 的 JNI 库和 NDK 应用的不便。它允许设备公开自己支持两个应用二进制接口(ABI):x86 和 ARMv5。这可以从build.prop观察到,如图图 5-4 所示。

ro.product.cpu.abi=x86
...
ro.product.cpu.abi2=armeabi

  • 1
  • 2
  • 3

安卓 x86 教程(一)

图 5-4 。二进制翻译

如果 NDK 应用没有为 x86 平台重新构建,二进制翻译器会在本地将 armeabi 版本翻译成 x86。这同样适用于请求基于 ARM 的 JNI 库的 Dalvik VM 应用。翻译流程经过优化,对最终用户完全透明。

所有这些努力的结合应该会导致 Google Play 中大约 90%的应用立即工作。另外 10%的应用可能需要一些额外的配置和设置才能完全正常工作。在 第七章:创建和移植基于 NDK 的 Android 应用中,我们将涉及更多关于使用 x86 进行本地代码开发的细节,这将提供一些通用的建议来帮助任何适合这一类别的应用。

概观

本章从公司和处理器的角度简要介绍了英特尔和 ARM 的历史。您了解了英特尔和 Arm 的一些特定处理器,并了解了它们各自带来的优势。在简要介绍了每家公司之后,我们花了一些时间讨论了英特尔凌动处理器以及其现代版本的特性和特点。最后,我们进入了关于英特尔凌动 Medfield 架构的技术讨论,这是最新 x86 手机和平板电脑的特色。我们已经讨论了整数流水线如何流动,采用了哪些安全系统,甚至英特尔超线程技术如何优化性能。我们详细讨论了二进制翻译,并解释了基于 NDK 的应用必须如何准备才能移植到英特尔平台。

1 马塔萨、洛里和马克斯·多梅卡、脱离英特尔凌动处理器:架构迁移指南。英特尔出版社,2010 年。

六、为英特尔应用开发安装 Android SDK

在一个有英特尔和微软的国家,2000 年问题怎么会成为问题呢?

—阿尔·戈尔

本章涵盖在英特尔架构处理器上开发 Android 应用的必要信息。第一步是安装软件开发套件(SDK ),并为在基于英特尔架构的 Android 设备上运行的应用开发设置合适的环境。SDK 包括工具和平台组件,供开发人员开发、构建、测试、调试和优化他们的 Android 应用,以及管理 Android 平台组件安装。SDK 还提供了与构建和开发环境集成的简单方法,例如与 Eclipse 或 Apache Ant 集成。

准备安装 SDK

下一节将致力于建立一个可用的开发环境,以便您可以开始在英特尔平台上开发 Android 应用。如果您已经有了 Android 开发环境设置,您可以跳过这一部分。

支持的操作系统

支持以下操作系统:

  • Windows XP (32 位)、Vista (32 位或 64 位)、Windows 7 (32 位或 64 位)和 Windows 8 (32 位或 64 位)
  • Mac OS X (32 位或 64 位)
  • Linux (Ubuntu,Fedora);需要 GNU C 库(glibc ) 2.7 或更高版本
  • 在 Ubuntu Linux 上,需要 8.04 或更高版本
  • 在 Fedora 上,目标版本是 F-12 和更高版本
  • 64 位发行版必须能够运行 32 位应用

硬件要求

Android SDK 要求为您选择安装的所有组件提供磁盘存储。运行模拟器需要额外的磁盘空间,例如,为 Android 虚拟设备(avd)创建 SD 卡。

安装 JDK

SDK 至少需要 Java JDK 5 或 JDK 6。JDK 7 也受支持。仅有 JRE (Java 运行时环境)是不够的。如果您的系统没有安装 JDK 5、6 或 7,您可以从http://www.oracle.com/technetwork/java/javase/downloads/index.html下载 JDK SE 7 并安装在您的系统上。

安装 Eclipse

强烈推荐使用 Eclipse 的 SDK 来开发 Android 应用。可以去http://www.eclipse.org/downloads/下载或者更新 Eclipse。我们建议使用以下 Eclipse 设置来开发面向英特尔架构的 Android 应用:

  • Eclipse 3.5(伽利略)或更高版本
  • Eclipse Classic(版本 3.5.1 和更高版本)
  • Android 开发工具插件(推荐)

安装 Apache Ant(可选)

强烈推荐使用 Eclipse 等集成开发环境开发 Android 应用。但是作为替代,您可以使用 Apache Ant 与 SDK 一起构建 Android 应用。您可以访问http://ant.apache.org/下载二进制发行版并安装 Ant。要使用 SDK,需要 Ant 1.8 或更高版本。

下载 SDK 启动包并添加 SDK 组件

您可以在http://developer.android.com/sdk/index.html下载 SDK starter 包。SDK starter 包不包含开发 Android 应用所需的特定于平台的组件。它只提供核心 SDK 工具供您下载其余的平台组件。

安装 SDK 启动包后,运行 Android SDK 和 AVD 管理器。

  • 在 Windows 上,选择开始安卓 x86 教程(一)所有程序安卓 x86 教程(一) Android SDK 工具安卓 x86 教程(一) SDK 管理器
  • 在 Linux 上,运行your-android-sdk-directory/tools/android

在 Android SDK 和 AVD 管理器对话框的左侧面板中,选择可用的包,在右侧面板中,点击并展开 Android Repository 节点,选择要安装的包,如图图 6-1 所示。

安卓 x86 教程(一)

图 6-1 。在 Linux 中安装 Android SDK 和 AVD 管理器

安卓 x86 教程(一) 注意如果你试图从防火墙后下载,你可能会看到一条错误信息。如果出现这种情况,请在防火墙外重试。如果您仍然看到错误消息,请关闭 SDK 管理器,然后在开始菜单中右键单击它,并选择以管理员身份运行。这两个步骤将解决您在尝试下载时看到的大多数错误信息。

设置 Eclipse 以使用 SDK

如果您使用 Eclipse IDE 开发软件,我们强烈建议您安装并设置 Android 开发工具(ADT)插件。

为 Eclipse 安装 ADT 插件

要安装 Eclipse 的 ADT 插件,请遵循以下步骤:

  1. 启动 Eclipse,在安装对话框中选择 Help 安卓 x86 教程(一)安装新软件,点击 Add 按钮。

  2. In the Add Repository dialog box, enter ADT Plugin in the Name field and enter https://dl-ssl.google.com/android/eclipse/ in the Location fields, as shown in Figure 6-2. Then click OK.

    安卓 x86 教程(一)

    图 6-2 。存储库对话框

  3. It will go back to the Install dialog box, connect to the Google repository server, and display the available ADT packages, as shown in Figure 6-3.

    安卓 x86 教程(一)

    图 6-3 。ADT 软件包列表

  4. 选择下一步,接受许可协议,然后选择完成。

  5. 重启 Eclipse。

配置 ADT 插件

要配置 ADT 插件,请执行以下步骤:

  1. 启动 Eclipse 并选择 Windows 安卓 x86 教程(一)首选项。
  2. 在首选项对话框中,从左侧面板中选择 Android。在右侧面板中,使用浏览按钮导航到您的 Android SDK 安装目录,然后单击应用。您已经安装的 SDK 目标列表将会出现,如图 6-4 所示。此时,单击确定。

安卓 x86 教程(一)

图 6-4 。ADT SDK 目标列表

完成这些步骤后,您将拥有开始 Android 开发的必要工具。现在,您已经具备了编写第一个应用所需的一切,但最好还是在此时安装英特尔架构(x86)仿真器 ,这样您就可以在应用准备就绪后立即对其进行测试。下一节将带您完成英特尔架构(x86)仿真器的安装。我们讨论用 AOSP 源代码构建一个仿真器映像,并仿真 x86 的最终系统映像。

Android 开发者工具 会定期更新以包含最新的 API。构建仿真器映像时,构建最新 Android 版本的工具唾手可得。如果您选择下载预构建的仿真器系统映像,您的选择将会过时几个月。在本章中,我们使用模拟 Android 2.3(代号为姜饼,涵盖 API 级别 9 和 10)和 Android 4.0(代号为冰激凌三明治,涵盖 API 级别 14 和 15)的案例研究。姜饼是第一个在 ARM 以外的平台上发布的 Android 版本,它的新功能直接来自 Android 开源项目的努力,这在 第一章:Android OS 的历史和演变中讨论过。

Android 虚拟设备仿真概述

Android 可以在各种不同屏幕尺寸、硬件功能和特性的设备上运行。典型的设备具有多种软件(Android API)和硬件功能,如传感器、GPS、摄像头、SD 卡和特定尺寸的多点触摸屏。

该仿真器非常灵活,可配置不同的软件和硬件配置选项。开发者可以使用名为 Android 虚拟设备(AVD) 的仿真器配置定制仿真器。AVD 基本上是一组指定不同 Android 软件和设备硬件功能的配置文件。Android 模拟器使用这些 AVD 配置在模拟器上配置和启动适当的 Android 虚拟映像。

正如 Android 网站上记录的那样(参见http://developer.android.com/guide/developing/devices/index.html),典型的 AVD 配置具有:

  • 指定所有设备功能(如照相机和传感器)的硬件配置文件。
  • 一个系统映像,这个 AVD 的模拟器使用它(指定目标 API 级别,比如 10 代表 Gingerbread,19 代表 KitKat)。
  • 一个数据映像,作为用户数据、设置和 SD 卡的专用存储空间。
  • 其他选项包括模拟器皮肤、屏幕尺寸和 SD 卡大小。

开发人员被鼓励针对不同的 API 级别、屏幕尺寸和硬件功能(比如摄像头、传感器和多点触控)。AVD 配置可用于根据需要定制仿真器。开发人员可以根据需要创建任意数量的 avd,每个 avd 都针对不同的基于英特尔架构的 Android 设备。例如,开发人员可以创建基于英特尔架构的姜饼 AVD,内置类似 WVGA800 的皮肤,或者手动指定屏幕分辨率的自定义皮肤。

Android SDK 从版本 r12 开始就支持基于英特尔架构的 Android 仿真。SDK 将这种支持集成到所有开发人员工具中,包括 eclipse ADT 插件。图 6-5 是运行 Gingerbread 的 x86 Android 模拟器的示例截图。突出显示型号,并显示 x86 模拟器上的完整 Android。

安卓 x86 教程(一)

图 6-5 。安卓模拟器

关于如何使用模拟器的详细说明,请参考以下 Android 文档:http://developer.android.com/tools/devices/emulator.html

你应该使用哪个模拟器

在撰写本文时,仿真器映像可用于 Android 2.3.7(姜饼)、Android 4.0.4(冰淇淋三明治)和 Android 4.3(果冻豆)的英特尔架构(x86)。您可以在http://software.intel.com/en-us/articles/android-43-jelly-bean-x86-emulator-system-image找到最近的图像。

尽管为最新的 Android 操作系统版本开发有许多优势,但许多开发人员更喜欢以 Android 2.x 或 Android 4.x 为目标,因为大多数 Android 手机都运行 Android 4.x 或更高版本。这个百分比会随着时间的推移而变化,因此强烈建议您在确定目标操作系统时牢记市场条件。

要了解更多特定于 Gingerbread 的操作系统信息,下面的文章可能会很有用:http://blogs.computerworld.com/17479/android_gingerbread_faq

冰淇淋三明治资讯,用这篇文章:http://www.computerworld.com/s/article/9230152/Android_4.0_The_ultimate_guide_plus_cheat_sheet_

为什么要使用模拟器

首先,它是免费的。Android SDK 及其第三方插件完全不需要任何成本,并且允许开发人员模拟他们不拥有并且可能无法访问的设备。这一点很重要,因为并非所有手机都通过空中下载(OTA)更新获得最新的 Android 操作系统版本,而且开发者购买每一款预计支持其软件包的设备也不可行。

  • 开发和测试。 开发者可以使用 SDK 创建几个 Android 虚拟设备(AVD)配置,用于开发和测试目的。每个 AVD 可以有不同的屏幕尺寸、SD 卡大小,甚至是 Android SDK 的版本(这对于测试与以前的 Android 版本的向后兼容性很有用)。
  • 玩新版本。该模拟器允许开发人员尽情享受新版本的 Android,并对其有更多的了解。

现在让我们浏览一下在 x86 构建上构建和运行模拟器映像所需的步骤。为了简单起见,这一节重点介绍姜饼模拟器,尽管大部分内容也适用于冰淇淋三明治模拟器。

构建仿真器映像

第一步是遵循这里列出的设置说明:http://source.android.com/source/initializing.html

Google 已经在http://source.android.com/source/downloading.html发布了姜饼文档。

目前,默认的回购初始化指令仅用于获取最新的主分支或特定分支,例如使用命令android-2.3.7_r1时。没有给出构建 Gingerbread x86 模拟器映像的 repo 和 branch 指令。

使用标签为gingerbread的分支下载最新的 Android 源码分支,如清单 6-1 所示。这个分支拥有构建 x86 SDK 模拟器映像所需的构建文件和模块。

清单 6-1 。设置资源库

$ repo init –u
https://android.googlesource.com/platform/manifest-b gingerbread
$ repo sync

  • 1
  • 2
  • 3

初始化构建环境,如清单 6-2 所示。

清单 6-2 。构建环境初始化

$ source build/envsetup.sh
including device/htc/passion/vendorsetup.sh
including device/samsung/crespo4g/vendorsetup.sh
including device/samsung/crespo/vendorsetup.sh

  • 1
  • 2
  • 3
  • 4

使用lunch命令配置构建 x86 SDK 的版本,如清单 6-3 所示。这将为 x86 构建模拟器映像,以及其他 SDK 模块。

清单 6-3 。午餐命令

$ lunch sdk_x86-eng
=====================================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.3.7
TARGET_PRODUCT=sdk_x86
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=false
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=x86
TARGET_ARCH_VARIANT=x86_atom
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=GINGERBREAD
=====================================================

安卓 x86 教程(一)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

最后,发出make sdk命令来开始构建,如清单 6-4 中的所示。

清单 6-4 。制作 SDK

$ make sdk
=====================================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.3.7
TARGET_PRODUCT=sdk_x86
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=false
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=x86
TARGET_ARCH_VARIANT=x86_atom
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=GINGERBREAD
=====================================================

安卓 x86 教程(一)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

此外,您可以使用–j参数为make指定并行作业的数量,以加快构建过程。(建议您使用至少大于或等于系统中 CPU 总数的值。)

构建将创建三个映像,包括 QEMU Android 内核image:t system.img (Android)、userdata.imgramdisk.img

当构建完成后,图像会在build文件夹$ANDROID_BUILD_TOP/out/target/product/generic_x86/中,如图图 6-6 所示。

$ ls $ANDROID_BUILD_TOP/out/target/product/generic_x86/ -Altr

  • 1

安卓 x86 教程(一)

图 6-6 。图像位置

QEMU ( kernel-qemu)的 Android 内核映像来自 Android 源代码。位于预建文件夹($ANDROID_BUILD_TOP/prebuilt/android-x86/kernel)下,如图图 6-7 所示。

$ ls $ANDROID_BUILD_TOP/prebuilt/android-x86/kernel -Altr

  • 1

安卓 x86 教程(一)

图 6-7 。内核映像

现在,您已经拥有了在 Android x86 模拟器上运行 x86 Android Gingerbread 映像所需的所有映像文件。您需要用 SDK 设置图像文件,这将在下一节中介绍。

设置 SDK 以使用 x86 模拟器映像

Android SDK 工具(Android 和 AVD 管理器)期望 x86 模拟器映像出现在平台映像的默认 SDK 文件夹中,这是/platforms/android-10/images

下面的图片假设$ANDROID_SDK_TOP环境变量被设置为 Android SDK 安装文件夹的位置。

如图 6-8 中的所示,默认情况下,Android-10 自带 ARM 的模拟器图像。为了在 SDK 中设置 x86 模拟器映像,您需要创建一个x86文件夹,并将您构建的映像复制到该文件夹中。您也可以将手臂图像移动到它们自己的文件夹中,如清单 6-5 中的所示。

$ cd $ANDROID_SDK_TOP/platforms/android-img/
$ls -l

  • 1
  • 2

安卓 x86 教程(一)

图 6-8 。图像位置

清单 6-5 。手臂折叠器

$ mkdir arm
$ mv *.img kernel-qemu arm/

  • 1
  • 2

清单 6-6 显示了 x86 文件夹的指令。

清单 6-6 。x86 指令

$ mkdir x86
$ cp $ANDROID_BUILD_TOP/out/target/product/generic_x86/*img x86/
$ cp $ANDROID_BUILD_TOP/prebuilt/android-x86/kernel/kernel-qemu x86/

$ cp NOTICE.txt arm/
$ cp NOTICE.txt x86/

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Android-10 平台的最终图片文件夹如图图 6-9 所示。

$ ls –l *

  • 1

安卓 x86 教程(一)

图 6-9 。最终图像文件夹

在 x86 模拟器上使用 Gingerbread 英特尔架构映像之前,您必须创建一个 AVD 配置,指定所需的软件和硬件定制。有关 AVDs 的更多详细信息,请参阅英特尔软件网络文章“面向英特尔架构的 Android 虚拟设备仿真”(http://software.intel.com/en-us/articles/android-virtual-device-emulation-for-intel-architecture)。

此时,您的模拟器和 x86 映像就可以使用了。请注意,如果您将英特尔硬件加速执行管理器(英特尔 HAXM)与此系统映像配合使用,仿真器性能将会显著提高,否则性能可能会有所不同。(英特尔 HAXM 需要支持英特尔 VT-x 的英特尔处理器。有关英特尔 HAXM 的更多信息,请参见 第十一章:使用英特尔硬件加速执行管理器在 x86 仿真上加速 Android

现在是时候使用 Gingerbread x86 模拟器来模拟 x86 映像了。

打开 Android 工具或者直接从 Eclipse 中调出 AVD 创建工具。

图 6-10 和 6-11 显示了使用英特尔凌动(x86) CPU 为姜饼创建 AVD。

安卓 x86 教程(一)

图 6-10 。新的 AVD 创作

安卓 x86 教程(一)

图 6-11 。成功对话框

通过选择 AVD 并点击开始来测试 x86 姜饼 AVD,如图 6-12 所示。

安卓 x86 教程(一)

图 6-12 。发射选项

图 6-13 显示了模拟器 x86 上的英特尔凌动(x86)姜饼的主屏幕。

安卓 x86 教程(一)

图 6-13 。主屏幕

建议您使用支持英特尔 VT 硬件加速的 x86 仿真器。在 Linux 上,您可以使用 Linux KVM 来完成这项工作。Ubuntu 在https://help.ubuntu.com/community/KVM有更多关于如何配置和使用它的文档。

清单 6-7 。KVM

$ emulator-x86 –avd gbx86 –qemu –m 512 –enable-kvm

  • 1

使用 KVM,如清单 6-7 ( -enable-kvm)所示,用户可能会注意到 Android 启动期间的性能提升,以及更快的仿真器响应速度。

姜饼的主要特点

下一节重点介绍 Gingerbread 支持的一些关键特性。这些功能在最新版本的 Android 中也得到支持,并计划继续提供支持。

电池使用统计

在关于手机中,如图图 6-14 所示,有一个电池使用部分。电池状态会因仿真设备和版本而异。在开发者可以telnet进入仿真设备的情况下,一些简单的命令可以用来模拟电池消耗。在http://android-er.blogspot.com/2010/09/how-to-set-battery-status-of-android.html看一个这样的例子。

安卓 x86 教程(一)

图 6-14 。关于电话视图

任务管理器

从设置安卓 x86 教程(一)应用安卓 x86 教程(一)运行服务中,选择运行选项卡。它显示了当前正在运行的程序,如你在图 6-15 中所见。

安卓 x86 教程(一)

图 6-15 。任务管理器的运行选项卡

例如,开发者可以通过点击项目并选择停止来关闭设置过程。

剪切和粘贴文本

打开“信息”应用并选取“新信息”可让您像发送短信一样键入信息。通过在文本字段中点击并在主机键盘上键入,字符会无缝地出现在 Android 屏幕上。输入Hello 2.3.5!后,屏幕看起来像图 6-16 。

安卓 x86 教程(一)

图 6-16 。信息传递乐趣

如果您将鼠标拖动到找到Hello 2.3.5!的文本字段,然后按住鼠标按钮(或触摸板按钮)大约两秒钟,会出现一个工具提示菜单来编辑文本。如果选择全选,然后重复鼠标操作,可以剪切文本。剪切文本后,可以再次重复鼠标操作,将文本粘贴到其他地方。

冰淇淋三明治模拟

x86 Android 4.0.4 模拟器系统映像使您能够在开发机器上运行 Android 冰激凌三明治的模拟。结合 Android SDK,您可以在基于英特尔架构(x86)的虚拟 Android 设备上测试您的 Android 应用。

为了安装仿真器系统映像,您可以使用 Android SDK 管理器(推荐方法),或者您可以下载二进制 ZIP 文件并将包含的目录解压缩并复制到您的 Android SDK 安装的add-ons目录中。(请注意,此方法不允许自动更新附加组件。)

以下部分提供了 ICS 映像安装指南。

先决条件

Android x86 模拟器映像需要安装 Android SDK。有关安装和配置 Android SDK 的说明,请参考 Android 开发者网站(参见http://developer.android.com/sdk/)。

安卓 x86 教程(一) 注意可以使用英特尔硬件加速执行管理器(英特尔 HAXM)对面向 Android 的 x86 仿真器映像进行加速。有关更多信息,请参见 第十一章:使用英特尔硬件加速执行管理器在 x86 仿真上加速 Android

通过 Android SDK 管理器下载

  1. 启动 Android SDK 管理器。

  2. Under Android 4.0.4 (some screenshots may refer to older versions), select Intel x86 Atom System Image, as shown in Figure 6-17.

    安卓 x86 教程(一)

    图 6-17 。英特尔 x86 凌动系统映像

  3. 选中后,单击“安装软件包”按钮。

  4. 查看英特尔公司许可协议。如果您接受这些条款,请选择接受并单击安装。

  5. SDK 管理器将下载系统映像并提取到 Android SDK 目录中的适当位置。

使用系统图像

  1. Start the Android AVD Manager and create a new AVD, setting Target to Android 4.0.X, and CPU/ABI to Intel Atom (x86), as shown in Figure 6-18.

    安卓 x86 教程(一)

    图 6-18 。设定目标

    安卓 x86 教程(一) 注意如果英特尔凌动(x86) CPU/ABI 选项不可用,请确保系统映像安装正确。

  2. 单击创建 AVD 按钮。

  3. The AVD has been successfully created and is now ready to use, as shown in Figure 6-19.

    安卓 x86 教程(一)

    图 6-19 。图像就绪

手动下载

  1. 转到http://www.intel.com/software/android

  2. 下载英特尔 x86 凌动系统映像(位于工具和下载选项卡下)。

  3. Navigate to the directory containing the Android SDK, as shown in Figure 6-20.

    安卓 x86 教程(一)

    图 6-20 。Android SDK 目录

  4. The system-images directory contains Android’s system images, separated by architecture, as shown in Figure 6-21.

    安卓 x86 教程(一)

    图 6-21 。分离的图像

  5. Expand android-15 (this directory contains API level 15 system images), as shown in Figure 6-22.

    安卓 x86 教程(一)

    图 6-22 。API 级

  6. 将下载的系统镜像档案中包含的x86目录直接解压到android-15目录中。

  7. The directory structure should now look like Figure 6-23.

    安卓 x86 教程(一)

    图 6-23 。预期的目录结构

  8. 系统映像现在已经安装好,可以使用了。

CPU 加速

借助基于硬件的虚拟化和英特尔 VT-x 技术,您可以提高面向 Android 冰激凌三明治的英特尔凌动 x86 映像的性能。如果您的计算机配备了支持 VT-x 的英特尔处理器,建议您将英特尔 HAXM 用于此系统映像。有关英特尔 HAXM 的更多信息,请访问http://int-software.intel.com/en-us/android

安卓 x86 教程(一) 注意英特尔 HAXM 仅适用于 Windows 和 OS X 操作系统。对于 Linux 主机,您可以使用基于内核的虚拟机(KVM)来提高仿真性能。有关在 Ubuntu 上安装和配置 KVM 的信息,请参考下面的指南https://help.ubuntu.com/community/KVM/Installation

GPU 加速

面向 Android 冰激凌三明治的英特尔凌动 x86 映像可以利用硬件 GPU 特性来提高游戏、图形密集型程序和用户界面元素的性能。

安卓 x86 教程(一) 注意GPU 加速的功能和性能高度依赖于您计算机的显卡和图形驱动程序。

要使用硬件 GPU 加速,请执行以下步骤:

  1. 打开 Android AVD 管理器。

  2. 选择 AVD 并单击编辑。

  3. The AVD editor window will appear. In the Hardware section, click New, as shown in Figure 6-24.

    安卓 x86 教程(一)

    图 6-24 。硬件部分

  4. In the Property drop-down box, select GPU Emulation, as shown in Figure 6-25.

    安卓 x86 教程(一)

    图 6-25 。GPU 仿真选项

  5. 单击确定。

  6. After the GPU Emulation property has been added, change the Value to Yes, as shown in Figure 6-26.

    安卓 x86 教程(一)

    图 6-26 。值已更改为是

  7. 单击编辑 AVD 保存 AVD。

  8. After the AVD has been modified, a dialog box will appear confirming the AVD settings, shown in Figure 6-27.

    安卓 x86 教程(一)

    图 6-27 。确认对话框

在确认对话框中,hw.gpu.enabled=yes行表示为该特定 AVD 启用了 GPU 加速。

安卓 x86 教程(一) 注意必须基于每个 AVD 启用 GPU 加速。

概观

在这一章中,你建立了一个全功能的 Android 开发环境。您还安装了 Android 和 SDK 的先决条件。本章详细讨论了 Android 模拟器,您创建了一个 x86 模拟器以便于测试。您甚至在虚拟 x86 平台上创建了一个全功能的 Android 4.0.4(冰激凌三明治)模拟器,用于测试 Android 的最新特性。在下一章中,您将学习如何安装和使用 Android 原生开发套件,以便为英特尔平台创建和移植应用。

七、创建和移植基于 NDK 的 Android 应用

很明显,我们的科技已经超越了我们的人性。

—阿尔伯特·爱因斯坦

Android 应用可以使用本地开发工具包(NDK) 工具集合并本地代码。它允许开发人员重用遗留代码,为低级硬件编程,并通过利用非最佳或不可能的特性来区分他们的应用。

本章深入介绍了如何为英特尔架构创建基于 NDK 的应用。它还涵盖了移植现有的基于 NDK 的应用的案例。它深入讨论了英特尔编译器和默认 NDK 编译器之间的差异,并解释了如何充分利用英特尔 NDK 环境。

JNI 和 NDK 简介

JNI 简介

我们知道 Java 应用并不直接运行在硬件上,而是实际运行在一个虚拟机上。应用的源代码不是被编译以获得硬件指令,而是被编译以获得虚拟机的解释来执行代码。比如 Android 应用运行在 Dalvik 虚拟机上;它的编译代码是 DEX 格式的 Dalvik 虚拟机的可执行代码。这个特性意味着 Java 运行在虚拟机上,确保了它的跨平台能力:这就是它的“编译一次,随处运行”的特性。Java 的这种跨平台能力导致它与本地机器的各种内部组件的连接较少,并限制了它与本地机器的各种内部组件的交互,使得很难使用本地机器指令来利用机器的性能潜力。很难利用基于本地的指令来运行巨大的现有软件库,因此功能和性能受到限制。

有没有办法让 Java 代码和原生代码软件协同工作,共享资源?答案是肯定的——通过使用 Java 本地接口(JNI) ,这是一种 Java 本地操作的实现方法。JNI 是一个 Java 平台,被定义为与本地平台上的代码进行交互的 Java 标准。(一般称为主机平台。但这一章是针对移动平台的,为了和移动交叉开发主机区分,我们称之为本地平台。)所谓“接口”包括两个方向——一个是 Java 代码调用原生函数(方法),一个是本地应用调用 Java 代码。相对来说,前一种方法在 Android 应用开发中使用的更多。因此,本章主要关注 Java 代码调用本地函数的方法。

Java 通过 JNI 调用本地函数的方式是将本地方法以库文件 s 的形式存储,比如在 Windows 平台上,文件在。DLL 文件格式,并且在 UNIX/Linux 机器上文件位于。所以文件格式。通过调用本地库文件的内部方法,Java 可以与本地机器建立密切联系。这被称为各种接口的系统级方法

JNI 通常有两种使用场景:一是能够使用遗留代码(例如 C/C++、Delphi 等开发工具);第二,更直接地与硬件交互以获得更好的性能。当你阅读这一章的时候,你会看到其中的一些内容。

JNI 一般工作流程如下:Java 发起调用,让本地函数的侧代码(比如用 C/C++ 写的函数)运行。这一次,对象是从 Java 端传递过来的,并在本地函数完成时运行。在运行完一个本地函数之后,结果的值被返回给 Java 代码。这里,JNI 是一个适配器,在 Java 语言和本地编译语言(如 C/C++)之间映射变量和函数(Java 方法)。我们知道 Java 和 C/C++ 在函数原型定义和变量类型上有很大的不同。为了使两者匹配,JNI 提供了一个jni.h文件来完成两者之间的映射。这个过程如图图 7-1 所示。

安卓 x86 教程(一)

图 7-1 。JNI 通用工作流程

通过 JNI 和 Java 程序(尤其是 Android 应用)调用 C/C++ 函数的一般框架如下:

  1. 编译 native 的方式是在 Java 类中声明的(C/C++ 函数)。
  2. 编译包含原生方法的.java源代码文件(在 Android 中构建项目)。
  3. javah命令生成一个.h文件,根据.class文件对应本地方法。
  4. C/C++ 方法是用来实现本地方法的。
  5. 这一步推荐的方法是先将函数原型复制到.h文件中,然后修改函数原型,添加函数体。在此过程中,应注意以下几点:
    • JNI 函数调用必须使用 C 函数。如果是 C++ 函数,别忘了加上extern C 关键字。
    • 方法名的格式应该遵循以下模板:Java_package_class_method,即Java_package名称类名和函数方法名。
  6. C 或 C++ 文件被编译成一个动态库(在 Windows 下这是一个. DLL 文件,在 UNIX/Linux 下是一个. SO 文件)。

使用 Java 类中的System.loadLibrary()System.load()方法加载生成的动态库。

这两个功能略有不同:

  • System.loadLibrary() :加载本地链接库下的默认目录(例如对于 Windows,这是System32, jrein,以此类推)。
  • System.load() :根据添加到交叉链接库的本地目录,必须使用绝对路径。

第一步,Java 调用原生 C/C++ 函数;C 和 C++ 的格式不一样。例如,对于 Java 方法,如不传递参数和返回一个String类,C 和 C++ 代码在以下方面有所不同:

c 代码:

Call function:(*env) -> <jni function> (env, <parameters>)
Return jstring:return (*env)->NewStringUTF(env, "XXX");

  • 1
  • 2

C++ 代码:

Call function:env -> <jni function> (<parameters>)
Return jstring:return env->NewStringUTF("XXX");

  • 1
  • 2

其中两个 Java String对象NewStringUTF函数都是由 JNI 提供的 C/C++ 生成的。

Java 方法及其与 C 函数原型 Java 的对应关系

回想一下,为了让 Java 程序调用代码框架中的 C/C++ 函数,您使用了javah命令,该命令将根据.class文件为本地方法生成相应的.h文件。.h文件是按照一定的规则生成的,从而使正确的 Java 代码找到对应的 C 函数来执行。

例如,下面的 Android Java 代码:

public class HelloJni extends Activity

1.   {
2.      public void onCreate(Bundle savedInstanceState)
3.      {
4.         TextView tv.setText(stringFromJNI() );  // Use C function Code
5.      }
6.      publicnativeString  stringFromJNI();
7.   }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

对于第 4 行使用的 C 函数stringFromJNI(),由javah生成的.h文件中的函数原型是:

1.   JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI
2.     (JNIEnv *, jobject);

  • 1
  • 2

在这方面,C 源代码文件对于函数代码的定义大致有:

1.     /*
2.     ...
3.     Signature: ()Ljava/lang/String;
4.     */
5.     jstring Java_com_example_hellojni_HelloJni_stringFromJNI (JNIEnv* env,  jobject thiz )
6.     {
7.          ...
8.         return (*env)->NewStringUTF(env, "...");
9.     }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

从这段代码中可以看到,函数名相当长,但还是很有规律,完全符合命名约定:java_package_class_methodHello.java中的stringFromJNI()方法对应 C/C++ 中的Java_com_example_hellojni_HelloJni_stringFromJNI()方法。

注意Signature: ()Ljava/lang/String;的注释。()Ljava/lang/String;的括号()表示函数参数为空,这意味着除了两个参数JNIEnv *jobject之外,没有其他参数。JNIEnv *jobject是所有 JNI 函数必须拥有的两个参数,分别针对jni环境和对应的Java类(或对象)本身。Ljava/lang/String;表示函数的返回值是一个 Java String对象。

Java 和 C 数据类型映射

如前所述,Java 和 C/C++ 变量类型非常不同。JNI 提供了一种机制来完成 Java 和 C/C++ 之间的映射。主要类型之间的对应关系如表 7-1 所示。

表 7-1 。Java 到 C 类型映射

|

Java 类型

|

原生类型

|

描述

|
| — | — | — |
| boolean | jboolean | C/C++ 8 位整数 |
| byte | jbyte | C/C++ 无符号 8 位整数 |
| char | jchar | C/C+无符号 16 位整数 |
| short | jshort | C/C++ 有符号 16 位整数 |
| int | jint | C/C++ 有符号 32 位整数 |
| long | jlong | C/C++ 无符号 64 位整数 |
| float | jfloat | C/C++ 32 位浮点 |
| double | jdouble | C/C++ 64 位浮点 |
| void | void | 不适用的 |
| Object | jobject | 任何 Java 对象,或者不对应于java类型的对象 |
| Class | jclass | 类对象 |
| String | jstring | 字符串对象 |
| Object[] | jobjectArray | 任何对象的数组 |
| Boolean[] | jbooleanArray | 布尔数组 |
| byte[] | jbyteArray | 比特阵列 |
| char[] | jcharArray | 字符数组 |
| short[] | jshortArray | 短整数数组 |
| int[] | jintArray | 整数数组 |
| long[] | jlongArray | 长整数数组 |
| float[] | jfloatArray | 浮点数组 |
| double[] | jdoubleArray | 双浮点阵列 |

安卓 x86 教程(一)NoteJava 类型与本地(C/C++)类型的对应关系。

当传递一个 Java 参数时,使用 C 代码的思路如下:

  • 基本类型可以直接使用;比如doublejdouble可以互通。基本类型是从表 7-1 中的booleanvoid行所列的类型。在这种类型中,如果用户将一个boolean参数传递给方法,就会有一个名为jboolean的本地方法对应于boolean类型。类似地,如果本地方法返回一个jint,那么 Java 会返回一个int
  • Java 对象用法。一个Object对象有String对象和一个通用对象。这两个对象的处理方式略有不同。
  • String对象。Java 程序传递的String对象是本地方法中对应的jstring类型。C 中的jstring类型和char *不同。所以如果你只是把它当成一个char *,就会出现错误。因此,您需要在使用之前将jstring转换成 C/C++ 中的char *。这里我们用JNIEnv的方法进行换算。
  • Object对象。使用以下代码获取类的对象处理程序:
jclass objectClass = (env)->FindClass("com/ostrichmyself/jni/Structure");

  • 1

然后使用以下代码获取该类所需的域处理程序:

jfieldID str = (env)->GetFieldID(objectClass,"nameString","Ljava/lang/String;");
jfieldID ival = (env)->GetFieldID(objectClass,"number","I");

  • 1
  • 2

然后使用下面类似的代码为jobject对象的传入字段赋值:

(env)->SetObjectField(theObjet,str,(env)->NewStringUTF("my name is D:"));
(env)->SetShortField(theObjet,ival,10);

  • 1
  • 2
  • 如果没有传入的对象,那么 C 代码可以使用下面的代码来生成新的对象:

    jobject myNewObjet = env->AllocObject(objectClass);
    

    • 1
  • Java 数组处理。对于数组类型,JNI 提供了一些可操作的函数。例如,GetObjectArrayElement可以接受传入的数组,并使用NewObjectArray创建一个数组结构。

  • 资源释放的原则。C/C++ new的对象或者malloc的对象需要使用 C/C++ 来释放内存。

  • 如果JNIEnv方法的新对象没有被 Java 使用,就必须释放它。

  • 使用GetStringUTFChars从 Java 转换一个 string 对象得到 UTF,需要打开内存,使用完char *后必须释放内存。使用的方法是ReleaseStringUTFChars

这些是 Java 与 C/C++ 交换数据时类型映射的简要描述。有关 Java 和 C/C++ 数据类型的更多信息,请参考相关的 Java 和 JNI 书籍、文档和示例。

NDK 简介

从前面的描述中,你知道 Java 代码可以使用 JNI 访问本地函数(比如 C/C++)。要达到这种效果,你需要开发工具。有一整套基于核心 Android SDK 的开发工具,您可以使用它们将 Java 应用交叉编译为可以在目标 Android 设备上运行的应用。同样,您需要交叉开发工具来将 C/C++ 代码编译成可以在 Android 设备上运行的应用。这个工具就是安卓原生开发套件,或者安卓 NDK。

在 NDK 之前,Android 平台上的第三方应用是在一个特殊的基于 Java 的 Dalvik 虚拟机上开发的。原生 SDK 允许开发人员直接访问 Android 系统资源,并使用传统的 C 或 C++ 编程语言创建应用。应用包文件(.apk)可以直接嵌入到本地库中。简而言之,通过 NDK,原本在 Dalvik 虚拟机上运行的 Android 应用现在可以使用 C/C++ 等本地代码语言来执行程序。这提供了以下好处:

  • 性能提升。它使用本机代码来开发程序中需要高性能的部分,并直接访问 CPU 和硬件。
  • 重用现有本机代码的能力。

当然,相对于 Dalvik 虚拟机,使用原生 SDK 编程也有一些缺点,比如增加了程序复杂度,兼容性难以保证,无法访问框架 API,调试更加困难,灵活性降低等等。此外,访问 JNI 会导致一些额外的性能开销。

简而言之,NDK 应用开发有其优点和缺点。你需要根据自己的判断使用 NDK。最佳策略是使用 NDK 来开发应用中本机代码可以提高性能的部分。

NDK 包括以下主要部件:

  • 工具和构建文件从 C/C++ 生成本机代码库。这包括一系列的 NDK 命令,包括javah(使用.class文件生成相应的.h文件)、gcc(稍后描述)和其他命令。它还包括ndk-build可执行脚本等等,这些将在后面的会话中详细介绍。
  • 应用包(应用包文件,即.apk文件)中会嵌入一个一致的本地库,可以部署在 Android 设备中。
  • 对所有未来 Android 平台的一些原生系统头文件和库的支持。

NDK 应用开发的流程框架如图图 7-2 所示。Android 应用由三部分组成:Android 应用文件、Java 本地库文件和动态库。这三个部分通过各自的生成路径从不同的源生成。对于一个普通的 Android 应用,Android SDK 生成 Android 应用文件和 Java 原生库文件。Android NDK 生成动态库文件(带有。SO 扩展名)使用非本机代码(通常是 C 源代码文件)。最后在目标机器上安装 Android 应用文件、Java 库文件和本地动态库,并运行完整的协作应用。

安卓 x86 教程(一)

图 7-2 。安卓 NDK 应用开发流程图

NDK 开发的应用项目(简称 NDK 应用项目)有组件,如图图 7-3 所示。与使用 Android SDK 开发的典型应用相比,在 NDK 开发的项目添加了 Dalvik 类代码、清单文件、公共资源,以及 JNI 和 NDK 生成的共享库。

安卓 x86 教程(一)

图 7-3 。Android NDK 应用的应用组件

Android 在其关键 API 版本中增加了 NDK 支持。每个版本都包括一些新的 NDK 特性、简单的 C/C++、兼容的 STL、硬件扩展等等。这些特性使得 Android 更加开放,更加强大。Android API 及其与 NDK 的对应关系如表 7-2 所示。

表 7-2 。主要 Android API 与 NDK 版本的关系

|

API 版本

|

支持的 NDK 版本

|
| — | — |
| API 级 | Android 1.5 NDK 1 |
| API 级 | Android 1.6 NDK 2 |
| API 级 | Android 2.1 NDK 3 |
| API 级 | Android 2.2 NDK 4 |
| API 级 | Android 2.3 NDK 5 |
| API 级 | Android 3.1 NDK 6 |
| API 级 | Android 4.0.1 NDK 7 |
| API 级 | Android 4.0.3 NDK 8 |
| API 级 | Android 4.1 NDK 8b |
| API 级 | Android 4.2 NDK 8d |
| API 级 | Android 4.3 NDK 9b |

安卓 x86 教程(一) 提示使用安卓 NDK 生成的每一段原生代码都被赋予了一个匹配的应用二进制接口(ABI) 。ABI 精确地定义了应用及其代码在运行时如何与系统交互。ABI 可以大致理解为类似于计算机架构中的 ISA(指令集架构)。

典型的 ABI 包含以下信息:

  • CPU 指令集应该使用的机器代码。
  • 运行时内存访问排名。
  • 可执行二进制文件的格式(动态库、程序等)以及允许和支持的内容类型。
  • 在应用代码和系统之间传递数据时使用的不同约定(例如,函数调用何时注册和/或如何使用堆栈、对齐限制等)。
  • 枚举类型、结构字段和数组的对齐和大小限制。
  • 运行时应用机器码的可用函数符号列表通常来自一组非常特定的库。每个受支持的 ABI 都有一个唯一的名称。

Android 目前支持以下 ABI 类型:

  • ARM eabi–这是 ARM CPU 的 abi 名称,它至少支持 ARMv5TE 指令集。
  • ARM eabi-v7a–这是基于 ARM 的 CPU 的另一个 abi 名字;它扩展了 armeabi CPU 指令集扩展,如 Thumb-2 指令集扩展和用于向量浮点硬件的浮点处理单元指令。
  • x86——这是 ABI 的名字,一般称为支持 x86 或 IA-32 指令集的 CPU。更具体地说,它的目标在下面的会话中经常被称为 i686 或奔腾 Pro 指令集。英特尔凌动处理器属于这种 ABI 类型。

这些类型具有不同的兼容性。X86 与 armeabi 和 armeabi-v7a 不兼容。armeabi-v7a 机器与 armeabi 兼容,这意味着 armeabi 框架指令集可以在 armeabi-v7a 机器上运行,但不一定相反,因为一些 ARMv5 和 ARMv6 机器不支持 armeabi-v7a 代码。因此,当您构建应用时,应该根据用户对应的 ABI 机器类型仔细选择用户。

NDK 装置

这里我们以 NDK Windows 环境为例来说明 NDK 软件的安装。Windows NDK 包括以下模块:

  • Cygwin 在 Windows 命令行中运行 Linux 命令。
  • 安卓 NDK 包,包括ndk-build等按键命令,是 NDK 软件的核心;它将 C/C++ 文件编译成。所以共享库文件。
  • CDT (C/C++ 开发工具,C/C++ 开发工具)是一个 Eclipse 插件,可以将 C/C++ 文件编译成。所以 Eclipse 中的共享库。这意味着您可以使用它来ndk-build替换命令行命令。

CDT 模块不是必需的,但它确实支持在熟悉的 Eclipse IDE 中进行开发。Cygwin 模块必须安装在 Windows 环境中,但在 Linux 环境中不是必需的。当然,整个开发环境需要支持 Java 开发环境。以下部分分别解释了每个模块的安装步骤。

安卓 NDK 安装

本节介绍如何安装 Android NDK:

  1. Visit the Android NDK official web site at http://developer.android.com/sdk/ndk/index.html and download the latest NDK package, as shown in Figure 7-4. In this case, you click on the file android-ndk-r8d-windows.zip and download the files to the local directory.

    安卓 x86 教程(一)

    图 7-4 。NDK 包下载页面来自安卓官方网站

  2. 安装安卓 NDK。

Android NDK 安装相对简单。你需要做的就是把下载的android-ndk-r4b-windows.zip解压到指定的目录。在本例中,我们将 Android NDK 安装在目录D:Androidandroid-ndk-r8d中。您需要记住这个位置,因为下面的配置需要它来设置环境。

安装 Cygwin

本节介绍如何安装 Cygwin:

  1. Visit Cygwin’s official web site (http://www.cygwin.com/). Download the Cygwin software, as shown in Figure 7-5. Go to the download page, and then click on the setup.exe file to download and install packages.

    安卓 x86 教程(一)

    图 7-5 。Cygwin 下载页面

  2. Double-click the downloaded setup.exe file to start the installation. The pop-up shown in Figure 7-6 appears.

    安卓 x86 教程(一)

    图 7-6 。Cygwin 初始安装窗口

  3. The installation mode selection box is shown in Figure 7-7. In this example, select Install from Internet mode.

    安卓 x86 教程(一)

    图 7-7 。Cygwin 安装模式选择

  4. The display installation directory and user settings selection box is shown in Figure 7-8.

    安卓 x86 教程(一)

    图 7-8 。安装目录和用户设置选择

  5. You are next prompted to enter a temporary directory to store the downloaded files, as shown in Figure 7-9.

    安卓 x86 教程(一)

    图 7-9 。Cygwin 下载文件临时目录设置

  6. Next you are prompted to select an Internet connection type, as shown in Figure 7-10. For this example, select Direct Connection.

    安卓 x86 教程(一)

    图 7-10 。Cygwin 设置互联网连接类型选择

  7. You are now prompted to select a download mirror site, as shown in Figure 7-11.

    安卓 x86 教程(一)

    图 7-11 。Cygwin Install:提示选择下载镜像站点

  8. Start the download and install the basic parts, as shown in Figure 7-12(a). During the setup, a Setup alert will indicate that this is the first time you are installing Cygwin, as shown in Figure 7-12(b). Click OK to continue.

    安卓 x86 教程(一)

    图 7-12 。Cygwin 安装包下载安装

  9. Select the packages to install, as shown in Figure 7-13. The default is to install all of the packages.

    安卓 x86 教程(一)

    图 7-13 。Cygwin 软件包安装选择

    您下载了所有组件,总大小超过 3GB。这需要在正常的宽带网速下花费很长时间;实际上不建议安装所有组件。你需要安装 NDK Devel 组件和 Shells 组件,如图 7-14 所示。

    安卓 x86 教程(一)

    图 7-14 。NDK 要求的 Cygwin 组件包

    从安装组件包中选择 Devel 和 Shells 的一些技巧。你可以先点击所有旁边的循环图标;它将在安装、默认和卸载之间循环。将其设置为 Uninstall 状态,然后单击 Devel 和 Shells 条目旁边的循环图标,使其保持 install 状态。最后,单击“下一步”继续。

  10. The contents of the selected components are displayed next, as shown in Figure 7-15.

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fgitee.com%2FOpenDocCN%2Fvkdoc-android-pt2-zh%2Fraw%2Fmaster%2Fdocs%2Fandr-x86%2Fimg%2F9781430261308_Fig07-15.jpg&pos_id=img-pliw8dF2-1724204976325)

图 7-15 。选择 Cygwin 组件包后的依赖提醒

  • 1
  • 2
  • 3
  1. Start to download and install the selected components, as shown in Figure 7-16.
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fgitee.com%2FOpenDocCN%2Fvkdoc-android-pt2-zh%2Fraw%2Fmaster%2Fdocs%2Fandr-x86%2Fimg%2F9781430261308_Fig07-16.jpg&pos_id=img-mWZcCWB3-1724204976326)

图 7-16 。Cygwin 下载并安装选定的组件

  • 1
  • 2
  • 3
  1. Installation is complete. Message boxes appear, as shown in Figure 7-17.
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fgitee.com%2FOpenDocCN%2Fvkdoc-android-pt2-zh%2Fraw%2Fmaster%2Fdocs%2Fandr-x86%2Fimg%2F9781430261308_Fig07-17.jpg&pos_id=img-e78GSVuk-1724204976326)

图 7-17 。安装完成后,Cygwin 提醒框

  • 1
  • 2
  • 3
  1. 配置 Cygwin Windows path 环境变量。

按照以下步骤将 NDK 包安装目录和 Cygwin bin 目录添加到path环境变量中:

  1. 在桌面上,右键单击我的电脑并选择属性高级环境变量菜单项。
  2. 点击PATH变量中的系统变量。然后点击安装目录后添加的【变量值】NDK 包的对话框中的编辑按钮,在子目录 build oolscygwinin 下。

例如,如果 NDK 安装在目录D:Androidandroid-ndk-r8d中,而 Cygwin 安装在目录D:cygwin中,则在PATH变量后添加路径,如下所示:

PATH=...;D:Androidandroid-ndk-r8d;D:Androidandroid-ndk-r8duild	ools;D:cygwinin

  • 1

这样配置成功后,就可以使用 Linux 命令下的控制台命令cmd了。例如,图 7-18 显示了一个带有 Windows dir命令和 Linux ls命令的命令行窗口。

安卓 x86 教程(一)

图 7-18 。安装 NDK 后的命令行窗口

您为 NDK 配置 Cygwin 的内部环境变量,如下所示:

  1. Before configuring the NDK Cygwin internal environment variables, you must run Cygwin at least once, otherwise the cygwinhome directory will be empty. Click the Browse button in Windows Explorer and select the mintty.exe file under the bin subdirectory of the Cygwin installation directory (in this example, it is located at D:cygwinin). The window is shown in Figure 7-19.

    安卓 x86 教程(一)

    图 7-19 。第一次启动 Cygwin 时的初始窗口

  2. Then select the Windows menu programsCygwinCygwin terminal. You can directly enter the Cygwin window, as shown in Figure 7-20.

    安卓 x86 教程(一)

    图 7-20 。Cygwin 窗口(如果不是第一次运行)

    这将在emptycygwinhome下创建一个用户名(在本例中是 Windows 登录用户名hlgu)子目录,并在该目录下生成几个文件。

    D:cygwinhomehlgu>dir
    2013-01-30  00:42             6,054 .bashrc
    2013-01-30  00:52                 5 .bash_history
    2013-01-30  01:09             1,559 .bash_profile
    2013-01-30  00:42             1,919 .inputrc
    2012-12-01  08:58             8,956 .mkshrc
    2013-01-30  00:42             1,236 .profile
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  3. Find .bash_profile in the installation directory cygwinhome<username> file. In this case, it is D:cygwinhomehlgu.Bash_profile. To the end of the file, add the following code:

    NDK=<android-ndk-r4b unzipped_NDK_folder>
    export NDK
    ANDROID_NDK_ROOT=<android-ndk-r4b unzipped_NDK_folder >
    export ANDROID_NDK_ROOT
    

    • 1
    • 2
    • 3
    • 4

    <android-ndk-r4b unzipped_NDK_folder >对应 NDK 包的安装目录。(本例中是D:Androidandroid-ndk-r8d。)Cygwin 提供了一个目录转换机制。在目录前面加上/cygdrive/DRIVELETTER/,表示驱动器中的指定目录。这里,DRIVELETTER是目录的驱动字母。考虑这个例子:

    NDK= /cygdrive/d/Android/android-ndk-r8d
    export NDK
    ANDROID_NDK_ROOT=/cygdrive/d/Android/android-ndk-r8d
    export ANDROID_NDK_ROOT
    

    • 1
    • 2
    • 3
    • 4
  4. Determine whether the command can be run by testing the make command.

    C:Documents and Settingshlgu>make -v
    GNU Make 3.82.90
    Built for i686-pc-cygwin
    Copyright (C) 2010 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果你看到这个输出,这意味着make命令运行正常。确保 Make 的版本为 3.8.1 或更高版本,因为此会话中的所有示例都需要 3.8.1 或更高版本才能成功编译。

    现在您可以测试gccg+gcjgnat命令:

    C:Documents and Settingshlgu>gcc -v
    Access denied.
    C:Documents and Settingshlgu>g++ -v
    Access denied.
    C:Documents and Settingshlgu>gcj
    Access denied
    C:Documents and Settingshlgu>gnat
    Access denied.
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果您收到Access denied消息,您需要继续以下步骤。否则,安装成功完成。

  5. 在 Cygwin 的bin目录下,删除gcc.exeg++.exegcj.exegnat.exe文件。

  6. 在同一目录下,选择需要的与版本匹配的gccg++gcjgnat文件。比如版本 4 对应gcc-4.exeg++-4.exegcj-4.exegnat-4.exe。复制这些文件,并将复制的文件重命名为gcc.exeg++.exegcj.exegnat.exe

  7. Now test again to see if gcc and the other commands can run:

    C:Documents and Settingshlgu> gcc -v
    

    • 1

    使用内置规范,您可以看到哪些命令可用:

    COLLECT_GCC=gcc
    COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-pc-cygwin/4.5.3/lto-wrapper.exe
    Target: i686-pc-cygwin
    Configured with: /gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3/co
    nfigure --srcdir=/gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3 --
    prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdi
    r=/usr/lib --datadir=/usr/share --localstatedir=/var --sysconfdir=/etc --dataroo
    tdir=/usr/share --docdir=/usr/share/doc/gcc4 -C --datadir=/usr/share --infodir=/
    usr/share/info --mandir=/usr/share/man -v --with-gmp=/usr --with-mpfr=/usr --ena
    ble-bootstrap --enable-version-specific-runtime-libs --libexecdir=/usr/lib --ena
    ble-static --enable-shared --enable-shared-libgcc --disable-__cxa_atexit --with-
    gnu-ld --with-gnu-as --with-dwarf2 --disable-sjlj-exceptions --enable-languages=
    ada,c,c++,fortran,java,lto,objc,obj-c++ --enable-graphite --enable-lto --enable-
    java-awt=gtk --disable-symvers --enable-libjava --program-suffix=-4 --enable-lib
    gomp --enable-libssp --enable-libada --enable-threads=posix --with-arch=i686 --w
    ith-tune=generic --enable-libgcj-sublibs CC=gcc-4 CXX=g++-4 CC_FOR_TARGET=gcc-4
    CXX_FOR_TARGET=g++-4 GNATMAKE_FOR_TARGET=gnatmake GNATBIND_FOR_TARGET=gnatbind -
    -with-ecj-jar=/usr/share/java/ecj.jar
    Thread model: posix
    gcc version 4.5.3 (GCC)
    
    C:Documents and Settingshlgu>g++ -v
    

    安卓 x86 教程(一)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    使用内置的规范,比如gcc,您可以看到哪些命令是可用的:

    COLLECT_GCC=g++
    COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-pc-cygwin/4.5.3/lto-wrapper.exe
    Target: i686-pc-cygwin
    Configured with: /gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3/co
    nfigure --srcdir=/gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3 --
    prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdi
    r=/usr/lib --datadir=/usr/share --localstatedir=/var --sysconfdir=/etc --dataroo
    tdir=/usr/share --docdir=/usr/share/doc/gcc4 -C --datadir=/usr/share --infodir=/
    usr/share/info --mandir=/usr/share/man -v --with-gmp=/usr --with-mpfr=/usr --ena
    ble-bootstrap --enable-version-specific-runtime-libs --libexecdir=/usr/lib --ena
    ble-static --enable-shared --enable-shared-libgcc --disable-__cxa_atexit --with-
    gnu-ld --with-gnu-as --with-dwarf2 --disable-sjlj-exceptions --enable-languages=
    ada,c,c++,fortran,java,lto,objc,obj-c++ --enable-graphite --enable-lto --enable-
    java-awt=gtk --disable-symvers --enable-libjava --program-suffix=-4 --enable-lib
    gomp --enable-libssp --enable-libada --enable-threads=posix --with-arch=i686 --w
    ith-tune=generic --enable-libgcj-sublibs CC=gcc-4 CXX=g++-4 CC_FOR_TARGET=gcc-4
    CXX_FOR_TARGET=g++-4 GNATMAKE_FOR_TARGET=gnatmake GNATBIND_FOR_TARGET=gnatbind -
    -with-ecj-jar=/usr/share/java/ecj.jar
    Thread model: posix
    gcc version 4.5.3 (GCC)
    
    C:Documents and Settingshlgu>gcj
    gcj: no input files
    
    C:Documents and Settingshlgu>gnat
    GNAT 4.5.3
    Copyright 1996-2010, Free Software Foundation, Inc.
    
    List of available commands
    
    gnat bind               gnatbind
    gnat chop               gnatchop
    gnat clean              gnatclean
    gnat compile            gnatmake -f -u -c
    gnat check              gnatcheck
    gnat sync               gnatsync
    gnat elim               gnatelim
    gnat find               gnatfind
    gnat krunch             gnatkr
    gnat link               gnatlink
    gnat list               gnatls
    gnat make               gnatmake
    gnat metric             gnatmetric
    gnat name               gnatname
    gnat preprocess         gnatprep
    gnat pretty             gnatpp
    gnat stack              gnatstack
    gnat stub               gnatstub
    gnat xref               gnatxref
    Commands find, list, metric, pretty, stack, stub and xref accept project file sw
    itches -vPx, -Pprj and -Xnam=val
    

    安卓 x86 教程(一)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
  8. 最后,检查一下 NDK 核心命令ndk-build脚本,看看它是否可以运行。

    C:Documents and Settingshlgu>ndk-build
    Android NDK: Your Android application project path contains spaces: 'C:/./ Settings/'
    Android NDK: The Android NDK build cannot work here. Please move your project to a different location.
    D:Androidandroid-ndk-r8duild/core/build-local.mk:137: *** Android NDK: Aborting. Stop.
    

    • 1
    • 2
    • 3
    • 4

如果您的输出看起来像这样,这表明 Cygwin 和 NDK 已经安装和配置成功。

安装 CDT

CDT 是一个 Eclipse 插件,它将 C 代码编译成。所以共享库。事实上,在安装了 Cygwin 和 NDK 模块后,你可以将 C 代码编译成。所以在命令行共享库,这意味着 Windows NDK 的核心组件已经安装。如果你仍然喜欢使用 Eclipse IDE 而不是命令行编译器来编译本地库,你需要安装 CDT 模块;否则,跳过这一步,直接看 NDK 的例子。

如果需要安装 CDT,请使用以下步骤:

  1. Visit Eclipse’s official web site at http://www.eclipse.org/cdt/downloads.php to download the CDT package. As shown on the download page in Figure 7-21, you can click to download a version of the software. In this case, click cdt-master-8.1.1.zip to start the download.

    安卓 x86 教程(一)

    图 7-21 。CDT 下载页面

  2. 启动 Eclipse。选择menu HELPInstall new software开始安装 CDT。

  3. In the pop-up Install dialog box, click Add, as shown in Figure 7-22.

    安卓 x86 教程(一)

    图 7-22 。Eclipse 安装软件对话框

  4. In the pop-up Add Repository dialog box, enter a name for Name and a software download web site address in Location. You can enter the local address or the Internet address. If you’re using an Internet address, Eclipse will go to the Internet to download and install the package, while the local address will direct Eclipse to install the software from the local package. Enter the local address; then you can click the Archive button in the pop-up dialog box and enter the directory and filename for the downloaded cdt-master-8.1.1.zip file, as shown in Figure 7-23. If the file is downloaded from the Internet, the address is http://download.eclipse.org/tools/cdt/releases/galileo/.

    安卓 x86 教程(一)

    图 7-23 。Eclipse 软件更新安装地址对话框

  5. After returning to the Install dialog box, click to select the software components that need to be installed, as shown in Figure 7-24.

    安卓 x86 教程(一)

    图 7-24 。要安装的组件的 CDT 选择框

    在组件列表中,CDT 主要功能是必需的组件。在本例中,我们仅选择该组件。

  6. A list of detailed information about CDT components to install is displayed, as shown in Figure 7-25.

    安卓 x86 教程(一)

    图 7-25 。CDT 组件安装的详细信息

  7. Review the licenses dialog box. Click “I accept the terms of the license agreement” to continue, as shown in Figure 7-26.

    安卓 x86 教程(一)

    图 7-26 。CDT 执照审核窗口

  8. The installation process starts, as shown in Figure 7-27.

    安卓 x86 教程(一)

    图 7-27 。CDT 安装进度

  9. 当安装过程完成时,重启 Eclipse 以完成安装。

NDK 的例子

本节包括一个例子来说明 JNI 和 NDK 的用法。如前所述,NDK 可以从命令行运行,也可以在 Eclipse IDE 中运行。我们将使用这两种方法来生成相同的 NDK 应用。

使用命令行方法生成一个库文件

这个例子的名字是jnitest,它是一个演示 JNI 代码框架的简单例子。以下几节概述了这些步骤。

创建一个 Android 应用项目

首先,你需要创建一个 Android app 项目,编译代码,生成.apk包。在 Eclipse 中创建一个项目,并将项目命名为jnitest。选择 Build SDK 支持 x86 版本的 API(这里是 Android 4.0.3),如图图 7-28 所示。最后,您生成项目。

安卓 x86 教程(一)

图 7-28 。jnitest 项目参数设置

项目生成后,文件结构被创建,如图 7-29 所示。请注意库文件(在本例中为android.jar)所在的目录,因为下面的步骤将使用该参数。

安卓 x86 教程(一)

图 7-29 。jnitest 项目的文件结构

修改 Java 文件

接下来修改 Java 文件,使用 C 函数创建代码。在这种情况下,唯一的 Java 文件是MainActivity.java。您需要修改其代码,如下所示:

1.      package com.example.jnitest;
2.      import android.app.Activity;
3.      import android.widget.TextView;
4.      import android.os.Bundle;
5.      public class MainActivity extends Activity
6.      {
7.          @Override
8.          public void onCreate(Bundle savedInstanceState)
9.          {
10.             super.onCreate(savedInstanceState);
11.             TextView tv = new TextView(this);
12.             tv.setText(stringFromJNI() ); // stringFromJNIas a  C function
13.             setContentView(tv);
14.         }
15.     publicnativeString stringFromJNI();
16.
17.         static {
18.                     System.loadLibrary("jnitestmysharelib");
19.         }
20.     }

安卓 x86 教程(一)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

代码非常简单。在第 11 行到第 13 行,您使用一个TextView来显示从stringFromJNI()函数返回的字符串。但与之前讨论的 Android 应用不同的是,整个项目中没有任何地方可以找到这个功能的实现代码。那么函数的实现发生在哪里呢?在第 15 行中,您声明该函数不是用 Java 编写的,而是由本地(本机)库编写的,这意味着该函数在 Java 之外。既然它是在本地库中实现的,那么问题是什么库呢?答案在第 17–20 行中描述。System类的static函数LoadLibrary的参数描述了库的名称。该库是一个名为libjnitestmysharelib.so的 Linux 共享库。在静态区声明的应用代码将在Activity.onCreate之前执行。该库将在第一次使用时加载到内存中。

有趣的是,当loadLibrary函数加载库名时,它会自动在参数和*前加上lib前缀。*所以后缀结尾。当然,如果参数指定的库文件的名称以lib开头,该函数不会在文件名前添加前缀lib

在 Eclipse 中生成项目

只建(build),而不跑。这将编译项目,但是.apk文件不会被部署到目标机器上。

当这一步完成后,相应的.class文件将在名为binclassescomexamplejnitest的项目目录中生成。这一步必须在下一步之前完成,因为下一步需要合适的.class文件。

在项目根目录下创建一个子目录

将这个子目录命名为jni。例如,如果项目根目录是E: empAndroidDevworkspacejnitest,可以使用md命令创建jni子目录。

E:	empAndroid Devworkspacejnitest>mkdir jni

  • 1

然后测试目录是否已经建立:

E:	empAndroid Devworkspacejnitest>dir
...
2013-02-01  00:45    <DIR>          jni

  • 1
  • 2
  • 3

创建一个 C 接口文件

所谓 C 接口文件,就是配合本地(外部)函数工作的 C 函数原型。特定于这种情况的是stringFromJNI函数的 C 函数原型。您声明您需要使用external函数的原型,但是它是 Java 格式的:您需要将其更改为 C 格式构建 C-JNI 接口文件。这一步可以用javah命令来完成。命令格式是:

$ javah -classpath <directory of jar and .class documents>  -d <directory of .h documents>  <the package + class name of class>

  • 1

命令参数描述如下:

  • -classpath:表示类路径
  • -d ...:表示生成的头文件的存储目录
  • <class name> : 正在使用的本机函数的完整.class类名,由“包+类的类名”组件组成。

对于此示例,请遵循以下步骤:

  1. 从命令行输入根目录(在本例中是E: empAndroid Devworkspacejnitest)。
  2. 然后运行以下命令:
E:> javah -classpath "D:Androidandroid-sdkplatformsandroid-15android.jar";bin/classes  com.example.jnitest.MainActivity

  • 1

在这个例子中,使用的本机函数的stringFromJNI的类是MainActivity,编译这个类后的结果文件是MainActivity。类,它位于项目的根目录bin classescomexample目录下。其类MainActivity.java的源代码文件的第一行显示了该类的包在哪里:

package com.example.jnitest;

  • 1

在前面的命令中,class name = package name.Class name(注意不要使用.class后缀),-classpath首先需要解释整个包的 Java 库路径(本例中库文件为android.jar;其位置如图图 7-30 所示,即D:Androidandroid-sdk platformsandroid-15android.jar-classpath还需要说明目标类(MainActivity.class)目录。在本例中,它位于binclasses目录中,在binclassescomexample MainActivity.class下(两者都用分号分隔)。

安卓 x86 教程(一)

图 7-30 。jnitest 应用运行界面

经过前面的步骤后,在当前目录(项目根目录)下生成了.h文件。该文件定义了 C 语言的函数接口。

您可以测试前面步骤的输出:

E:	empAndroid Devworkspacejnitest>dir
...
2013-01-31  22:00      3,556 com_example_jnitest_MainActivity.h

  • 1
  • 2
  • 3

显然已经生成了一个新的.h文件。该文件内容如下:

1.      /* DO NOT EDIT THIS FILE - it is machine generated */
2.      #include <jni.h>
3.      /* Header for class com_example_jnitest_MainActivity */
4.
5.      #ifndef _Included_com_example_jnitest_MainActivity
6.      #define _Included_com_example_jnitest_MainActivity
7.      #ifdef __cplusplus
8.      extern "C" {
9.      #endif
10.     #undef com_example_jnitest_MainActivity_MODE_PRIVATE
11.     #define com_example_jnitest_MainActivity_MODE_PRIVATE 0L
12.     #undef com_example_jnitest_MainActivity_MODE_WORLD_READABLE
13.     #define com_example_jnitest_MainActivity_MODE_WORLD_READABLE 1L
14.     #undef com_example_jnitest_MainActivity_MODE_WORLD_WRITEABLE
15.     #define com_example_jnitest_MainActivity_MODE_WORLD_WRITEABLE 2L
16.     #undef com_example_jnitest_MainActivity_MODE_APPEND
17.     #define com_example_jnitest_MainActivity_MODE_APPEND 32768L
18.     #undef com_example_jnitest_MainActivity_MODE_MULTI_PROCESS
19.     #define com_example_jnitest_MainActivity_MODE_MULTI_PROCESS 4L
20.     #undef com_example_jnitest_MainActivity_BIND_AUTO_CREATE
21.     #define com_example_jnitest_MainActivity_BIND_AUTO_CREATE 1L
22.     #undef com_example_jnitest_MainActivity_BIND_DEBUG_UNBIND
23.     #define com_example_jnitest_MainActivity_BIND_DEBUG_UNBIND 2L
24.     #undef com_example_jnitest_MainActivity_BIND_NOT_FOREGROUND
25.     #define com_example_jnitest_MainActivity_BIND_NOT_FOREGROUND 4L
26.     #undef com_example_jnitest_MainActivity_BIND_ABOVE_CLIENT
27.     #define com_example_jnitest_MainActivity_BIND_ABOVE_CLIENT 8L
28.     #undef com_example_jnitest_MainActivity_BIND_ALLOW_OOM_MANAGEMENT
29.     #define com_example_jnitest_MainActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
30.     #undef com_example_jnitest_MainActivity_BIND_WAIVE_PRIORITY
31.     #define com_example_jnitest_MainActivity_BIND_WAIVE_PRIORITY 32L
32.     #undef com_example_jnitest_MainActivity_BIND_IMPORTANT
33.     #define com_example_jnitest_MainActivity_BIND_IMPORTANT 64L
34.     #undef com_example_jnitest_MainActivity_BIND_ADJUST_WITH_ACTIVITY
35.     #define com_example_jnitest_MainActivity_BIND_ADJUST_WITH_ACTIVITY 128L
36.     #undef com_example_jnitest_MainActivity_CONTEXT_INCLUDE_CODE
37.     #define com_example_jnitest_MainActivity_CONTEXT_INCLUDE_CODE 1L
38.     #undef com_example_jnitest_MainActivity_CONTEXT_IGNORE_SECURITY
39.     #define com_example_jnitest_MainActivity_CONTEXT_IGNORE_SECURITY 2L
40.     #undef com_example_jnitest_MainActivity_CONTEXT_RESTRICTED
41.     #define com_example_jnitest_MainActivity_CONTEXT_RESTRICTED 4L
42.     #undef com_example_jnitest_MainActivity_RESULT_CANCELED
43.     #define com_example_jnitest_MainActivity_RESULT_CANCELED 0L
44.     #undef com_example_jnitest_MainActivity_RESULT_OK
45.     #define com_example_jnitest_MainActivity_RESULT_OK -1L
46.     #undef com_example_jnitest_MainActivity_RESULT_FIRST_USER
47.     #define com_example_jnitest_MainActivity_RESULT_FIRST_USER 1L
48.     #undef com_example_jnitest_MainActivity_DEFAULT_KEYS_DISABLE
49.     #define com_example_jnitest_MainActivity_DEFAULT_KEYS_DISABLE 0L
50.     #undef com_example_jnitest_MainActivity_DEFAULT_KEYS_DIALER
51.     #define com_example_jnitest_MainActivity_DEFAULT_KEYS_DIALER 1L
52.     #undef com_example_jnitest_MainActivity_DEFAULT_KEYS_SHORTCUT
53.     #define com_example_jnitest_MainActivity_DEFAULT_KEYS_SHORTCUT 2L
54.     #undef com_example_jnitest_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL
55.     #define com_example_jnitest_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
56.     #undef com_example_jnitest_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL
57.     #define com_example_jnitest_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
58.     /*
59.      * Class:     com_example_jnitest_MainActivity
60.      * Method:    stringFromJNI
61.      * Signature: ()Ljava/lang/String;
62.      */
63.     JNIEXPORT jstring JNICALL Java_com_example_jnitest_MainActivity_stringFromJNI
64.       (JNIEnv *, jobject);
65.
66.     #ifdef __cplusplus
67.     }
68.     #endif
69.     #endif

安卓 x86 教程(一)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

在前面的代码中,请特别注意第 63–64 行,这是一个本地函数stringFromJNI的 C 函数原型。

编译相应的。c 文件

这是一个局部函数的真正实现(stringFromJNI)。按照前面的步骤,通过修改.h文件获得源代码文件。

创建新的。项目中jni子目录下的 c 文件。文件名可以随机创建。在本例中,它被命名为jnitestccode.c。内容如下:

1.      #include <string.h>
2.      #include <jni.h>
3.      jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,  jobject thiz )
4.      {
5.          return (*env)->NewStringUTF(env, "Hello from JNI !"); // Newly added code
6.      }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

前面的代码定义了函数实现,非常简单。第 3 行是函数stringFromJNI的原型定义中使用的 Java 代码。它基本上是从前面的步骤中获得的.h文件的相应内容的副本(com_example_jnitest_MainActivity.h的第 63–64 行),并稍微做了一些修改。该函数的原型格式是固定的— JNIEnv* envjobject thiz是 JNI 的固有参数。因为stringFromJNI函数的参数为空,所以生成的 C 函数只有两个参数。第 5 行代码的作用是返回字符串"Hello fromJNI!"作为返回值。

第 2 行的代码是包含 JNI 函数的头文件,任何使用 JNI 的函数都需要这个函数。因为它与string函数相关,所以在这种情况下,第 1 行包含相应的头文件。完成前面的步骤后,.h文件不再有用,可以删除。

在 jni 目录下创建 NDK Makefile 文件

这些文件主要包括Android.mkApplication.mk文件,其中需要Android.mk。但是,如果使用应用的默认配置,则不需要Application.mk。四个具体步骤如下:

  1. Create a new Android.mk text file in the jni directory in the project. This file tells the compiler about some requirements, such as which C files to compile, the filename for compiled code, and so on. Enter the following:

       LOCAL_PATH := $(call my-dir)
       include $(CLEAR_VARS)
       LOCAL_MODULE  := jnitestmysharelib
       LOCAL_SRC_FILES  := jnitestccode.c
       include $(BUILD_SHARED_LIBRARY)
    

    • 1
    • 2
    • 3
    • 4
    • 5

    接下来解释文件内容。

    第 3 行表示生成的。所以 filename(标识您的Android.mk文件中描述的每个模块)。它必须与 Java 代码中的System.loadLibrary函数的参数值一致。该名称必须是唯一的,并且不能包含任何空格。

    安卓 x86 教程(一) 注意构建系统会自动生成适当的前缀和后缀。换句话说,如果一个是名为jnitestmysharelib的共享库模块,那么就会生成一个libjnitestmysharelib.so文件。如果您将库命名为libhello-jni,编译器将不会添加lib前缀,也会生成libhello-jni.so

    第 4 行的LOCAL_SRC_FILES变量必须包含要编译并打包成模块的 C 或 C++ 源代码文件。前面的步骤创建了一个 C 文件名。

    安卓 x86 教程(一) 注意用户不必在这里列出头文件和包含文件,因为编译器会自动为你识别依赖文件。只列出直接传递给编译器的源代码文件。此外,C++ 源文件的默认扩展名是. CPP。只要定义了LOCAL_DEFAULT_CPP_EXTENSION变量,就可以指定不同的扩展名。不要忘记开头的小圆点(。cxx,而不是 cxx)。

    第 3 行到第 4 行的代码非常重要,必须根据每个 NDK 应用的实际配置进行修改。其他行的内容可以从前面的例子中复制。

  2. Create an Application.mk text file in the jni directory in the project. This file tells the compiler the specific settings for this application. Enter the following:

    APP_ABI := x86
    

    • 1

    这个文件非常简单。您使用由 x86 架构的应用指令生成的目标代码,因此您可以在英特尔凌动处理器上运行应用。对于 APP_ABI 参数,请使用 x86、armeabi 或 armeabi-v7a。

  3. Next, compile the .c file to the .SO shared library file.

    转到项目根目录(AndroidManifest.xml所在的位置)并运行ndk-build命令:

    E:	empAndroid Devworkspacejnitest>ndk-build
    D:/Android/android-ndk-r8d/build/core/add-application.mk:128: Android NDK: WARNI
    NG: APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in ./AndroidM
    anifest.xml
    "Compile x86  : jnitestmysharelib <= jnitestccode.c
    SharedLibrary  : libjnitestmysharelib.so
    Install        : libjnitestmysharelib.so => libs/x86/libjnitestmysharelib.so
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    前面的命令将在项目中添加两个子目录(libsobj)。包括的执行版本。所以将文件(名为libjnitestmysharelib.so的命令执行信息提示文件)放在obj目录下,最终会将最终版本放在libs目录下。

    如果前面的步骤没有定义指定 ABI 的Application.mk文件,使用ndk-build命令将生成 ARM 架构的目标代码(armeabi)。如果您必须生成 x86 体系结构指令,您也可以使用ndk-build APP_ABI = x86命令来补救这种情况。该命令生成的目标代码的架构仍然是 x86。

  4. Deployment: run the project.

    完成这一步后,就差不多可以部署和运行项目了。在目标设备的界面上运行的应用如图 7-30 所示。

在 IDE 中生成库文件

回想一下上一节中描述的将 C 文件编译成动态库的过程。所以可以在 Android 目标设备上运行的文件。您可以在命令行中运行ndk-build命令来完成这个过程。事实上,您也可以在 Eclipse IDE 中完成这一步。

在 IDE 中生成库文件时,前四个步骤中的代码与上一节中的代码完全相同。你只需要编译。c 文件转换成。所以改为共享库文件。这详细解释如下:

  1. Compile the .C file into the .SO shared library file. Right-click on the project name, and select Build Path, Configure Build Path. In the pop-up dialog box, select the Builders branch. Then click the New button in the dialog box. Double-click Program in the prompt dialog box. This process is shown in Figure 7-31.

    安卓 x86 教程(一)

    图 7-31 。在 Eclipse 中输入编译 C 代码界面的参数设置

  2. In the Edit Configuration dialog box, enter the following for the Main tab settings:

    • 地点:通往小天鹅的路径bash.exe
    • 工作目录:Cygwin 的 bin 目录。
    • 自变量:
    --login -c "cd '/cygdrive/E/temp/Android Dev/workspace/jnitest' && $ANDROID_NDK_ROOT/ndk-build"
    

    • 1

    其中E/temp/Android Dev/workspace/jnitest是项目的字母和路径。整个设置如图 7-32 中的所示。

    安卓 x86 教程(一)

    图 7-32 。编辑配置窗口中的主选项卡设置

  3. Then configure the Refresh tab, ensuring that these items are selected—The Entire Workspace and Recursively Include Sub-Folders—as shown in Figure 7-33.

    安卓 x86 教程(一)

    图 7-33 。编辑配置窗口刷新选项卡设置

  4. Reconfigure the Build Options tab. Check the During Auto Builds and Specify Working Set of Relevant Resources items, as shown in Figure 7-34.

    安卓 x86 教程(一)

    图 7-34 。编辑配置窗口构建选项选项卡设置

  5. Click on the Specify Resources button. In the Edit Working Set dialog box, select the jni directory, as shown in Figure 7-35.

    安卓 x86 教程(一)

    图 7-35 。选择相关文件所在的源代码目录

  6. 正确配置前面的步骤后,将保存配置。它会自动编译jni目录下的 C 相关代码并输出相应的。所以库文件放在项目的libs目录下。libs目录是自动创建的。在控制台窗口中,您可以看到构建的输出信息,如下所示:

    /cygdrive/d/Android/android-ndk-r8d/build/core/add-application.mk:128: Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in ./AndroidManifest.xml
    Cygwin         : Generating dependency file converter script
    Compile x86    : jnitestmysharelib <= jnitestccode.c
    SharedLibrary  : libjnitestmysharelib.so
    Install        : libjnitestmysharelib.so => libs/x86/libjnitestmysharelib.so
    

    • 1
    • 2
    • 3
    • 4
    • 5

NDK 应用开发工作流分析

前面描述的生成 NDK 项目的过程很自然地实现了 C 库与 Java 的集成。在最后一步,你编译。c 文件放入。所以共享库文件。库的中间版本放在obj目录中,最终版本放在libs目录中。项目文件结构创建完成,如图图 7-36 所示。

安卓 x86 教程(一)

图 7-36 。NDK 图书馆文件生成后的 jnitest 项目结构

共享库。所以文件在主机中的项目目录中,并将被打包在生成的.apk文件中。.apk文件本质上是一个压缩文件。可以使用 WinRAR 之类的压缩软件查看其内容。对于这个例子,您可以在项目目录的bin子目录中找到.apk文件。用 WinRAR 打开,显示文件结构。

.apklib子目录的内容是项目的lib子目录的克隆。在图 7-36 中生成。所以文件显示在libx86子目录中。

.apk被部署到目标机器时,它将被解包,在这种情况下。因此文件将被放在/data/dat/XXX/lib目录中,其中XXX是应用包的名称。例如,对于前面的例子,目录是/data/data/com.example.jnitest/lib。您可以在 Eclipse DDMS 下查看目标机器的文件结构;该示例的文件结构如图图 7-37 所示。

安卓 x86 教程(一)

图 7-37 。NDK 图书馆文件生成后的 jnitest 项目结构

在图 7-37 中,你可以找到。所以库文件放在/data/data/XXX/lib目录下,这样当应用运行时,System.loadLibrary函数可以加载到内存中运行。这里你可以看到。所以文件中有 DDMS 的图形显示。有兴趣的读者可以在命令行上尝试一下,使用adb shell 命令查看目标文件目录中的相应内容。

此外,如果您在模拟器中运行jnitest应用(在这种情况下,目标机器是一个虚拟机),您将在 Eclipse Logcat窗口中看到以下输出:

1.  07-10 05:43:08.579: E/Trace(6263): error opening trace file: No such file or directory (2)
2.  07-10 05:43:08.729: D/dalvikvm(6263): Trying to load lib /data/data/com.example.jnitest/lib/libjnitestmysharelib.so 0x411e8b30
3.  07-10 05:43:08.838: D/dalvikvm(6263): Added shared lib /data/data/com.example.jnitest/lib/libjnitestmysharelib.so 0x411e8b30
4.  07-10 05:43:08.838: D/dalvikvm(6263): No JNI_OnLoad found in /data/data/com.example.jnitest/lib/libjnitestmysharelib.so 0x411e8b30, skipping init
5.  07-10 05:43:11.773: I/Choreographer(6263): Skipped 143 frames!  The application may be doing too much work on its main thread.
6.  07-10 05:43:12.097: D/gralloc_goldfish(6263): Emulator without GPU emulation detected.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

第 2–3 行是关于。所以共享库加载到应用中。

NDK 编译器优化

从前面的例子中,您可以看到 NDK 工具的核心作用是将源代码编译到。所以库文件可以在 Android 机器上运行。那个。所以库文件被放在项目目录的lib子目录中,这样当您使用 Eclipse 部署应用时,您可以将库文件部署到目标设备上的适当位置,并且应用可以使用库函数运行。

安卓 x86 教程(一) NDK 应用的本质是建立一个符合 JNI 标准的代码框架。这将使 Java 应用能够使用超出虚拟机范围的本地函数。

用于将源代码编译成. SO 库文件的关键 NDK 命令是ndk-build。它实际上不是一个单独的命令,而是一个可执行的脚本。它调用 GNU 交叉开发工具中的make命令来编译一个项目,例如调用gcc编译器编译源代码来完成整个过程,如图图 7-38 所示。当然也可以直接用。所以 Android 应用中已经有第三方开发的共享库,从而避免了自己写库(函数代码)的需要。

安卓 x86 教程(一)

图 7-38 。NDK 工具的工作机理

如图 7-38 所示,核心 GNU 编译器gcc是 NDK 中完成 C/C++ 源代码编译的核心工具。gcc是 Linux 的标准编译器,可以在本地机器上编译链接 C、C++、Object-C、FORTRAN 等源代码。事实上,gcc编译器不仅可以进行本地编译,还可以进行交叉编译。Android NDK 和其他嵌入式开发工具已经使用了这个特性。在编译器用法上,gcc交叉编译兼容原生编译;也就是说,本地编译代码的命令参数和开关本质上可以被移植,而无需修改交叉编译代码。因此,下面描述的gcc编译方法对于本地编译和交叉编译都是通用的。

Chapter 9: Performance Optimizations for Android Applications on x86 中,我们将更详细地讨论编译器优化(即一些优化如何由编译器自动完成)。对于基于 Intel x86 架构处理器的系统,除了 GNU gcc编译器,Intel C/C++ 编译器也是一个不错的工具。相对来说,由于英特尔 C/C ++ 编译器充分利用了英特尔处理器的特性,代码优化结果会更好。对于 Android NDK,无论是 Intel C/C++ 编译器还是gcc都可以完成 C/C++ 代码编译。目前,英特尔 C/C ++ 编译器提供了适当的使用机制。普通用户需要一个专业的许可证,而gcc是开源的、免费的软件,更容易获得。以下部分使用gcc作为实验工具,解释如何为 Android 应用执行 C/C++ 模块编译器优化。

gcc优化由编译器开关的优化选项控制。这些选项有些是独立于机器的,有些是与机器相关联的。这里我们将讨论一些重要的选项。对于与机器相关的选项,我们将只描述与英特尔处理器相关的选项。

独立于机器的编译器开关选项

gcc编译器开关的独立于机器的选项是-Ox选项,它们对应不同的优化级别。详情如下。

-0 或-01

一级优化是默认的优化级别,使用-O选项。编译器试图减少代码大小和执行时间。对于大型函数,需要花费更多的编译时间,使用大量的内存资源进行优化编译。

当不使用-O选项时,编译器的目标是减少编译的开销,以便快速调试结果。在这种编译模式下,语句是独立的。通过在两个语句之间插入断点来中断程序运行,用户可以重新分配变量或修改程序计数器来跳转到其他当前正在执行的语句,这样就可以精确地控制运行过程。当用户想调试时,也可以得到结果。此外,如果不使用-O选项,只有寄存器声明的变量可以进行寄存器分配。

当您指定-O选项时,-fthread-jumps-fdefer-pop选项被打开。在带有延迟槽的机器上,打开-fdelayed-branch选项。即使对于支持无帧指针调试的机器,-fomit-frame-pointer选项也是打开的。一些机器可能还会激活其他选项。

-02

进一步优化。GCC 执行几乎所有支持的优化,不涉及空间速度的权衡。与-O相比,该选项增加了编译时间和生成代码的性能。

-03

进一步优化。选项-O3打开由-O2指定的所有优化,同时打开-finline-functions-funswitch-loops-fpredictive-commoning-fgcse-after-reload-ftree-vectorizefvect-cost-model-ftree-partial-pre-fipa-cp-clone选项。

-00

减少编译时间并使调试产生预期的结果。这是默认设置。

自动内联函数通常用作函数优化措施。c99(1999 年开发的 C 语言 ISO 标准)和 C++ 都支持inline关键字。inline函数反映了用内联空间换取时间的思想。编译器不把内联描述的函数编译成函数,而是直接为函数体扩展代码,从而省去函数调用,返回call ret指令和参数的push指令执行。例如,在下面的函数中:

inline long factorial (int i)
{
   return factorial_table[i];
}

  • 1
  • 2
  • 3
  • 4

所有出现的factorial ()调用都被替换为factorial_table []数组引用。

当处于优化状态时,一些编译器会将该函数视为内联函数,即使该函数不使用内联指令。只有在适当的情况下(比如函数代码体相对较短,定义在头文件中),它才会这样做,以换取执行时间。

循环展开是一种经典的速度优化方法,被许多编译器视为自动优化策略。例如,以下循环代码需要循环 100 个周期:

for (i = 0; i < 100; i++)
{
   do_stuff(i);
}

  • 1
  • 2
  • 3
  • 4

在所有 100 个循环中,在每个循环结束时,必须检查循环条件以进行比较判断。通过使用循环展开策略,代码可以转换如下:

for (i = 0; i < 100; )
{
   do_stuff(i); i++;
   do_stuff(i); i++;
   do_stuff(i); i++;
   do_stuff(i); i++;
   do_stuff(i); i++;
   do_stuff(i); i++;
   do_stuff(i); i++;
   do_stuff(i); i++;
   do_stuff(i); i++;
   do_stuff(i); i++;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

如您所见,新代码将比较指令从 100 次减少到 10 次,用于条件比较的时间可以减少 90%。

前面描述的两种方法都将提高目标代码的优化。这是时间优化思想的典型空间。

英特尔处理器相关的编译器开关选项

gccm选项是为英特尔 i386 和 x86 – 64 处理器家族定义的。主要命令选项在表 7-3 中解释。

表 7-3 。与英特尔处理器相关的 gcc 开关选项

|

切换选项

|

注意

|

描述

|
| — | — | — |
| -march=cpu-type -mtune=cpu-type | | 为指定类型的 CPU 生成的代码。CPU 类型可以是 i386、i486、i586、奔腾、i686、奔腾 4 等等 |
| -msse | | 编译器自动向量化。使用或不使用 MMX、SSE、SSE2 指令。例如,-msse表示编程入指令,–mno-sse表示未编程入 SSE 指令 |
| -msse2 | |
| -msse3 | |
| -mssse3 | gxx-4.3 new addition |
| -msse4.1 | gcc-4.3 new addition |
| -msse4.2 | gcc-4.3 new addition |
| -msse4 | Include 4.1, 4.2 ,gcc-4.3 new addition |
| -mmmx | |
| -mno-sse | |
| -mno-sse2 | |
| -mno-mmx | |
| -m32 -m64 | | 生成 32/64 机器码 |

在表 7-3 中,-march是机器的 CPU 类型,-mtune是编译器想要优化的 CPU 类型(默认与-march相同)。-march选项是“紧约束”,而-mtune是“松约束”-mtune选项可以提供向后兼容性。

-march = i686, -mtune = pentium4的编译器优化选项针对奔腾 4 处理器进行了优化,但也可以在任何 i686 上运行。

对于-mtune = pentium-mmx编译的程序,可以运行奔腾 4 处理器。

-march=cpu-type

  • 1

该选项将生成指定机器类型的cpu-type指令。-mtune = cpu-type选项仅可用于优化为cpu-type生成的代码。相比之下,-march = cpu-type为指定类型的处理器生成不能在非gcc上运行的代码,这意味着-march = cpu-type意味着-mtune = cpu-type选项。

与英特尔处理器相关的cpu-type选项值在表 7-4 中列出。

表 7-4 。gcc-March 参数的主要可选值为 cpu-type

|

cpu 类型值

|

描述

|
| — | — |
| native | 这通过确定编译机器的处理器类型来选择 CPU 在编译时生成代码。使用-march=native启用本地机器支持的所有指令子集(因此结果可能不会在不同的机器上运行)。使用-mtune=native在所选指令集的约束下产生针对本地机器优化的代码。 |
| i386 | 原装英特尔 i386 CPU。 |
| i486 | 英特尔 i486 CPU。(该芯片未实施任何调度。) |
| i586 | 不支持 MMX 的英特尔奔腾 CPU。 |
| pentium |
| pentium-mmx | 英特尔奔腾 MMX CPU,基于支持 MMX 指令集的奔腾内核。 |
| pentiumpro | 英特尔奔腾 Pro CPU。 |
| i686 | 与-march一起使用时,使用的是奔腾 Pro 指令集,所以代码运行在所有 i686 系列芯片上。当与-mtune一起使用时,它与“通用”具有相同的含义。 |
| pentium2 | 英特尔奔腾 II CPU,基于支持 MMX 指令集的奔腾 Pro 内核。 |
| pentium3 | 英特尔奔腾 III CPU,基于支持 MMX 和 SSE 指令集的奔腾 Pro 内核。 |
| pentium3m |
| pentium-m | 英特尔奔腾 M;支持 MMX、SSE 和 SSE2 指令集的低功耗版本英特尔奔腾 III CPU。由迅驰笔记本使用。 |
| pentium4 | 支持 MMX、SSE 和 SSE2 指令集的英特尔奔腾 4 CPU。 |
| pentium4m |
| prescott | 英特尔奔腾 4 CPU 的改进版本,支持 MMX、SSE、SSE2 和 SSE3 指令集。 |
| nocona | 英特尔奔腾 4 CPU 的改进版本,支持 64 位扩展、MMX、SSE、SSE2 和 SSE3 指令集。 |
| core2 | 具有 64 位扩展的英特尔酷睿 2 CPU,支持 MMX、SSE、SSE2、SSE3 和 SSSE3 指令集。 |
| corei7 | 具有 64 位扩展的英特尔酷睿 i7 CPU,支持 MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1 和 SSE4.2 指令集。 |
| corei7-avx | 具有 64 位扩展的英特尔酷睿 i7 CPU,支持 MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2、AVX、AES 和 PCLMUL 指令集。 |
| core-avx-i | 支持 64 位扩展、MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2、AVX、AES、PCLMUL、FSGSBASE、RDRND 和 F16C 指令集的英特尔酷睿 CPU。 |
| atom | 支持 64 位扩展、MMX、SSE、SSE2、SSE3 和 SSSE3 指令集的英特尔凌动 CPU。 |

Traditional gcc是一个本地编译器。这些命令选项可以添加到gcc来控制gcc编译器选项。例如,假设您有一个int_sin.c文件。

$ gcc int_sin.c

  • 1

前面的命令使用了-O1优化级别(默认级别),并将int_sin.c编译成一个可执行文件,默认名为a.out

$ gcc int_sin.c -o sinnorm

  • 1

前面的命令使用-O1优化级别(默认级别)将int_sin.c编译成可执行文件;可执行文件的文件名被指定为sinnorm

$ gcc int_cos.c -fPIC -shared -o coslib.so

  • 1

前面的命令使用-O1优化级别(默认级别)将int_cos.c编译成一个名为coslib.so的共享库文件。与之前编译成可执行程序的源代码文件不同,这个命令要求源代码文件int_cos.c不包含 main 函数。

$ gcc -O0 int_sin.c

  • 1

前面的命令用默认文件名将int_sin.c编译成可执行文件。编译器不执行任何优化。

$ gcc -O3 int_sin.c

  • 1

前面的命令使用最高的优化级别-O3int_sin.c文件编译成具有默认文件名的可执行文件。

$ gcc -msse int_sin.c

  • 1

前面的命令使用 SSE 指令将int_sin.c编译成一个可执行文件。

$ gcc -mno-sse int_sin.c

  • 1

前面的命令将int_sin.c编译成一个可执行文件,没有任何 SSE 指令。

$ gcc -mtune=atom int_sin.c

  • 1

前面的命令将int_sin.c编译成可以使用英特尔凌动处理器指令的可执行文件。

从前面由gcc本地编译的例子中,您有了一些使用编译器switch选项进行gcc编译器优化的经验。对于gcc原生编译器,可以在switch选项中直接使用gcc命令来实现编译器优化。然而,从前面的例子中,你知道 NDK 不直接使用gcc命令。那么如何设置gcc编译器switch选项来实现 NDK 优化呢?

回想一下使用 NDK 的例子,您使用了ndk-build命令来编译 C/C++ 源代码;该命令首先需要读取 makefile 文件Android.mk。这个文件实际上包含了gcc命令选项。Android.mk使用LOCAL_CFLAGS控制并完成gcc命令选项。ndk-build命令将把LOCAL_CFLAGS运行时间值传递给gcc,作为其命令选项来运行gcc命令。LOCAL_CFLAGS将数值传递给gcc并将其作为命令选项来运行gcc命令。

例如,您将Android.mk修改如下:

1.  LOCAL_PATH := $(call my-dir)
2.  include $(CLEAR_VARS)
3.  LOCAL_MODULE       := jnitestmysharelib
4.  LOCAL_SRC_FILES    := jnitestccode.c
5.  LOCAL_CFLAGS       := -O3
6.  include $(BUILD_SHARED_LIBRARY)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

第 5 行是新增加的。它设置了LOCAL_CFLAGS变量脚本。

当你执行ndk-build命令时,相当于增加了一个gcc -O3命令选项。它指示gcc在最高优化级别 O3 编译 C 源代码。同样,如果您将第 5 行编辑为:

LOCAL_CFLAGS       := -msse3

  • 1

你指示gcc使用 SSE3 指令将 C 源代码编译成目标代码。

感兴趣的读者可以将LOCAL_CFLAGS设置为不同的值,比较目标库文件的大小和内容差异。注意,前面的例子jnitest C 代码非常简单,不涉及复杂的任务。因此,当从不同的LOCAL_CFLAGS值编译时,库文件的大小或内容不会有很大的不同。

那么,库文件的大小或内容会有显著的不同吗?其实答案是肯定的。在这方面,我们将在下面的章节中给出实际的例子。

概观

学习完本章后,您应该对 Android 原生开发套件有了全面的了解,并了解如何使用它来创建面向英特尔平台的 Android 应用。我们还介绍了英特尔 C++ 编译器及其选项。重要的是要记住,英特尔 C++ 编译器只是可用于英特尔 Android 应用的可能编译器之一。我们详细讨论了用于与您的 NDK 应用交互的 Java 本地接口,以及它是如何操作的。我们还介绍了各种代码示例,以便更好地解释英特尔 C++ 编译器的各种基本优化。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...