NDK Android* 应用程序移植方法





0/5 (0投票)
本指南旨在帮助开发者将现有的基于 ARM* 的 NDK 应用程序移植到 x86。
概述
本指南旨在帮助开发者将现有的基于 ARM* 的 NDK 应用程序移植到 x86。如果您已经有一个可以运行的应用程序,并且需要了解如何让您的应用程序在 x86 设备上快速获得 Android* Market 的认可,本文档应能为您提供入门所需的信息。本指南还提供了在移植过程中遇到编译器问题时的技巧和指导。
Content
NDK 概述
NDK 是一个强大的工具,可以将原生 x86 代码的强大功能与包装器 Android* 应用程序的图形界面结合起来。虽然该工具可用于在某些应用程序中获得性能优势,但需要谨慎,因为并非总是如此。
NDK 的目的是为开发者提供以下方面的帮助:
- 将原生 C/C++ 库编译成可在 Android* 包中使用(由包装的 Java* 代码调用)的库。
- 将 ARM* 原生库(库)重新编译为 x86(Intel® Atom™ 微架构),并在需要时进行移植。
第二点提到的可能只需要更改构建标志并重新编译即可,但有时并非如此简单。例如,如果原生库在 C 代码中包含内联汇编,则该代码不能简单地进行汇编并在两种不同架构上“按原样”工作;需要进行一些重写(请参阅讨论 ARM* NEON* 与 Intel SSE 的部分)。
连接 Android* Java* 代码与 NDK 预编译原生代码的接口称为 Java* Native Interface (JNI)。更多信息请参见:http://java.sun.com/docs/books/jni/。
上述链接是对 JNI 规范的广泛深入研究。为了获得更快速的概述,Wiki 页面已足够(如有疑问,请始终交叉引用规范以确保正确性):http://en.wikipedia.org/wiki/Java_Native_Interface。
JNI 的开销很大,因此理想情况下,应用程序中 JNI 调用应保持最少。具体来说,在 Android* 应用程序中使用原生代码并不能保证性能提升!通常,在原生代码涉及 CPU 密集型操作(例如 SSE 指令的重度使用)的情况下,可以获得性能提升,但在其他情况下,例如,当应用程序只是为用户提供复杂的 Web 界面时,通过 JNI 使用原生代码可能会降低性能。关于何时应该使用 NDK 和何时不应该使用 NDK,没有“成文规则”,但这些观点提供了一些通用指南和注意事项。
开发者可以在此处获取最新版本:https://developer.android.com.cn/sdk/ndk/index.html。从 NDK r6b 开始,NDK 可用于构建基于 ARM* 和 x86(Intel® Atom™ 微架构)的原生库。这为开发者提供了一个方便的、集成的原生代码移植包。
开发者将为项目创建一个 Android.mk makefile,并可选地创建一个 Application.mk 文件。Application.mk 文件用于描述您的应用程序所需的原生模块。Android.mk 文件用于控制模块(静态/共享库)的构建方式和构建来源。下面是一个简单的 Android.mk 文件的代码片段:
构建系统将前缀 `lib` 并生成名为 `libtest.so` 的库。`LOCAL_SRC_FILES`,顾名思义,是开发者提供项目源文件名称的地方。`LOCAL_LDLIBS` 和 `LOCAL_CFLAGS` 分别提供了指定链接标志和编译标志的选项。
在命令行中,这是一个指定构建目标为 x86 架构的示例:ndk-build APP_ABI=x86
有两种方法可以调用原生库:`System.loadLibrary("relative_path_and_name")` 和 `System.load("full_path_to_lib_file")`。前者更常用且更健壮。使用前者时,可以省略 Android.mk 文件中指定的库名称的 "lib" 部分。下面是一个此类调用的示例:
此外,在原生代码方面,开发者需要确保进入原生代码的入口方法具有正确的 `JNIEXPORT` 方法签名,而不是典型的 C/C++ 头文件。上述 JNI 链接提供了更多关于此的信息。
开发者可以选择在 Android* apk 包中提供原生库并在运行时引用它,或者提供库在 Android* 文件系统上的绝对路径。这取决于开发者的偏好,并应 accordingly 正确处理。
使用 `adb logcat` 命令,开发者可以确保目标原生库在运行时成功加载。这是一个描述原生库已加载的示例系统日志。请注意,这里提供了原生库文件的完整路径。
上述各部分提供了使用 NDK 的入门指南。要了解更详细的信息,请从 NDK 包附带的优秀文档开始。其中包含一些很棒的教程以及各种应用程序的示例源代码。
移植概述
对于大多数应用程序来说,将现有的 NDK 应用程序移植到 x86 应该非常直接。除非原生代码使用了 ARM*-特定的功能,否则移植应用程序应该就像重新编译、重新打包和重新发布一样简单。
以下将引导您完成将 NDK 应用程序移植到 x86 所需的步骤。
- 获取最新的 NDK 工具。X86 支持首次在 android-ndk-r6 中添加,但仍存在一些问题,并很快得到修复。请确保您已从Android* NDK 网站下载并安装了最新版本(撰写本文时,最新版本是 android-ndk-r6b)。
- 如果您有 `Application.mk` 文件,请编辑 `APP_ABI` 行以包含 `x86`。例如:
APP_ABI := armeabi armeabi-v7a x86
如果您没有使用 `Application.mk` 文件,请将 `x86` 添加到命令行构建中,下面是构建 NDK 示例应用程序之一的命令行和输出。
$ ndk-build APP_ABI="armeabi armeabi-v7a x86"
Install : test-libstl => libs/armeabi/test-libstl
Install : test-libstl => libs/armeabi-v7a/test-libstl
Install : test-libstl => libs/x86/test-libstl - 从上一步可以看出,在 `libs` 目录下创建了一个包含每个架构二进制文件的文件夹。下一步是重新打包 APK 以包含新的库。由于 `libs` 文件夹位于根项目文件夹下,用于创建 APK 的构建工具已经知道该文件夹中的二进制文件。在 Eclipse 中,只需重新构建项目 APK 即可包含新的 x86 二进制文件。命令行构建也是如此。以下是重新构建示例 hello-jni 的示例输出:
$ android.bat update project --path C:/Tools/android-ndk-r6b/samples/hello-jni
Updated local.properties
Added file C:\Tools\android-ndk-r6b\samples\hello-jni\build.xml
Added file C:\Tools\android-ndk-r6b\samples\hello-jni\proguard.cfg
$ ant -f hello-jni/build.xml debug
Buildfile: C:\Tools\android-ndk-r6b\samples\hello-jni\build.xml
…
debug
[echo] Running zip align on final apk...
[echo] Debug Package: android-ndk-r6b\samples\hello-jni\bin\HelloJni-debug.apk
BUILD SUCCESSFUL - 就是这样,下一步是在 Intel 架构设备或 x86 模拟器上运行和测试。最后一步是验证所有二进制文件是否已正确打包,可以使用 zip 存档工具打开 APK 并确保二进制文件存在。这是 APK 结构中包含 x86 二进制文件时的屏幕截图。
ARM* 到 x86 的移植技巧
应用程序移植到 x86 应该很简单,但有可能在此过程中做出了一些假设,现在需要解决 Intel® Atom™ 和 ARM* 架构之间的差异。以下主题将讨论您可能遇到的一些潜在问题以及如何解决它们。
您的构建环境有可能直接使用工具链而不是使用 Android* 构建脚本。对于 ARM*,使用的路径是:
android-ndk\toolchains\arm-linux-androideabi-4.4.3
对于 x86,请使用路径:
android-ndk\toolchains\x86-4.4.3
有关更多信息,请参阅位于 `android-ndk/docs/STANDALONE-TOOLCHAIN.html` 的 NDK 文档。
在 ARM* 和 Intel® Atom™ 微架构之间移植 C/C++ 代码时,可能会出现内存对齐不匹配。以下文章提供了一个很好的例子:/en-us/blogs/2011/08/18/understanding-x86-vs-arm-memory-alignment-on-android。主要观点是,开发者应考虑在代码设计中显式强制数据对齐。否则,无法保证它能在不同平台上被正确处理。
当前编译 NDK 库时有三种支持的应用程序二进制接口 (ABI) 选项:
- ‘armeabi’ – 默认选项,将创建针对 ARM* v5TE 设备的二进制文件。此目标的浮点运算使用软件浮点运算。使用此 ABI 创建的二进制文件将在所有 ARM* 设备上运行。
- ‘armeabi-v7a’ – 创建支持 ARM* v7 设备并使用硬件 FPU 指令的二进制文件。
- ‘x86’ – 生成用于支持 IA-32 指令集的二进制文件,包括基于硬件的浮点运算。
所有这些 ABI 选项都支持浮点运算,并且除非使用了 ARM*-特定的汇编指令,否则在将代码移植到 x86 时不应引起问题。一个优点是,如果您碰巧只为 ‘armeabi’ 编译并且现在支持 x86,您应该会发现大多数浮点运算的性能有所提升。
将 ARM* NEON* 指令移植到 Intel® SSE 以用于 Intel® Atom™
虽然本文无法涵盖所有方面,但下面提供的信息应能让您快速了解 Intel 架构和 ARM* 的 SIMD 扩展实现有何不同。通过这个概述,开发者将获得开始一些简单编码练习的工具。
NEON* 是一项 ARM* 技术,主要用于多媒体(智能手机、高清电视等)应用程序。ARM* 文档指出,其基于 128 位 SIMD 引擎的技术是 ARM* Cortex*–A 系列的扩展,其性能比 ARM* v5 架构至少高 3 倍,比后续的 ARM* v6 至少高 2 倍。有关此技术的更多信息,请参见此处,以深入了解 NEON 和其他性能考虑因素:http://www.arm.com/products/processors/technologies/neon.php
核心思想是将寄存器“分块”作为一个向量,其中向量中的每个寄存器都是一个元素,其数据类型与其他元素匹配。然后,以跨通道的方式执行操作,使该方法称为打包 SIMD。
SSE 是 Intel 架构 (IA) 的流式 SIMD 扩展。Intel® Atom™ 目前支持 SSSE3(Supplemental Streaming SIMD Extensions 3)。Atom™ 不支持 SSE4.x。这同样是一个 128 位引擎,用于处理浮点数据的打包。执行模型始于 MMX 技术,而 SSx essentially 是取代 MMX 使用的新一代技术。有关更多信息,请查阅 Intel 的 IA-32 和 IA-64 软件开发者手册的“卷 1:基本架构”部分:http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html。当前,SSE 概述部分位于第 5.5 节。它提供了 SSE、SSE2、SSE3 和 SSSE3 的操作码。请注意,数据操作通常涉及打包的、基于精度的浮点值的操作,以及 XMM 寄存器之间或这些寄存器与内存之间的数据批量移动。XMM 寄存器本质上旨在取代 MMX 寄存器。
在使用上述 IA 软件开发者手册作为所有 SSE(x) 助记符的交叉引用时,鼓励开发者也查看此链接中的各种 SSE 汇编指令:http://neilkemp.us/src/sse_tutorial/sse_tutorial.html。
在该链接中,“目录”部分允许您直接跳转到代码示例,或先浏览一些背景信息。同样,ARM* 直接提供的以下手册提供了信息和小的 NEON* 汇编代码片段:/sites/default/files/m/b/4/c/DHT0002A_introducing_neon.pdf。请参阅 ARM* 文档中的第 1.4 节。
以下是在比较 NEON 和 SSE 汇编代码时需要注意的一些关键点(请注意,信息可能随时过时,并且根据 SIMD 技术和手头的应用程序编码问题,可能存在其他细微差别):
字节序。Intel 仅支持小端字节序汇编,而 ARM* 支持大端或小端字节序(ARM* 是双端序)。在提供的代码示例中,ARM* 代码与 Intel 一样是小端字节序。但请注意,对于 ARM*,可能存在一些编译器影响。例如,使用 GCC* 为 ARM* 编译有标志 –mlittle-endian 和 –mbig-endian。此处获取更多信息:http://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html
粒度。在上面引用的简单汇编代码示例中(再次说明,这并非详尽列出了开发者可能看到的 NEON 和 SSE 之间的所有差异),请比较 SSE(Intel)的 `ADDPS` 指令与 NEON 的 `VADD.ix`(例如:x = 8 或 16)。请注意,后者将一些粒度固定在要作为助记符引用的数据的一部分。
在将 C/C++ 代码从 NEON 移植到 SSE 时,可能会遇到许多 API 上的细微差别。请记住,这里的假设是不使用内联汇编,而是使用真正的 C/C++ 代码。
在更高级别的编程中看到的 NEON 和 SSE 之间的一个区别涉及处理大数据块(128 位)。本文提供了一个简短的此类移植练习示例:http://stackoverflow.com/questions/7203231/neon-vs-intel-sse-equivalence-of-certain-operations
结论
希望本指南能帮助您为将现有的基于 NDK 的应用程序移植到 x86 做准备。移植到 x86 可以让您的设备被一整类新的 Android* 设备下载、购买和使用。如果您在移植过程中遇到问题,请随时在此文章下评论,我们将很乐意为您提供帮助和解答问题。
相关文章与资源
注意事项
* 其他名称和品牌可能被声称为他人财产。
版权所有 © 2011 Intel 公司。保留所有权利。
Intel 和 Atom 是 Intel Corporation 在美国和/或其他国家的商标。
本文档中提供的信息是与 Intel 产品相关的信息。本文档不授予任何明示或暗示的、基于禁止反言或其他方式的任何知识产权许可。除 Intel 产品销售条款和条件另有规定外,Intel 对此不承担任何责任,并否认与 Intel 产品销售和/或使用相关的任何明示或暗示的保证,包括对特定用途的适用性、适销性或对任何专利、版权或其他知识产权的侵权的保证。
除非 Intel 书面同意,否则 Intel 产品不设计也不用于任何可能导致人员伤亡的应用程序。
Intel 可随时更改规格和产品描述,恕不另行通知。设计人员不得依赖任何标有“保留”或“未定义”的特性或指令的缺失或特征。Intel 保留这些用于未来定义的权利,并且对于因未来对其进行更改而引起的任何冲突或不兼容性不承担任何责任。本文档中的信息如有更改,恕不另行通知。请勿使用此信息完成设计。
本文档中描述的产品可能包含已知为勘误的设计缺陷或错误,这可能导致产品偏离已发布的规范。当前的已表征勘误可应要求提供。
请联系您当地的英特尔销售办事处或您的经销商以获取最新的规范,并在下订单前进行咨询。
可通过致电 1-800-548-4725 获取本文档中引用的带有订单号的文档副本,或其他 Intel 文献,或访问:http://www.intel.com/design/literature.htm