-----------------
英文文档见android-ndk-r5b的documentation.html
属于Android Native Development Kit (NDK)的一部分
见http://developer.android.com/sdk/ndk/(需要代理)
翻译仅个人见解
-----------------
Android NDK & ARM NEON instruction set extension support
Android NDK 和 ARM NEON指令集扩展支持
--------------------
Introduction:
介绍:
-------------
Android NDK r3 added support for the new 'armeabi-v7a' ARM-based ABI that allows native code to use two useful instruction set extensions:
Android NDK r3添加对新的基于ARM的armeabi-v7a ABI的支持,允许原生代码使用两个有用指令集扩展:
- Thumb-2, which provides performance comparable to 32-bit ARM instructions with similar compactness to Thumb-1
- Thumb-2,它提供与32位ARM指令可比的性能,也提供与Thumb-1相似的紧凑性
- VFPv3, which provides hardware FPU registers and computations, to boost floating point performance significantly.
- VFPv3,它提供硬件浮点处理单元寄存器和计算能力,显著提升浮点性能。
More specifically, by default 'armeabi-v7a' only supports VFPv3-D16 which only uses/requires 16 hardware FPU 64-bit registers.
更显著的是,默认armeabi-v7a只支持VFPv3-D16,VFPv3-D16只使用/需要16个硬件浮点处理单元64位寄存器。
More information about this can be read in docs/CPU-ARCH-ABIS.html
更多相关信息可以阅读docs/CPU-ARCH-ABIS.html。
The ARMv7 Architecture Reference Manual also defines another optional instruction set extension known as "ARM Advanced SIMD", nick-named "NEON". It provides:
ARMv7架构参考手册还定义另一个可选的指令集合,即ARM高级SIMD,昵称为NEON。它提供:
- A set of interesting scalar/vector instructions and registers (the latter are mapped to the same chip area as the FPU ones), comparable to MMX/SSE/3DNow! in the x86 world.
- 一组有趣的标量/矢量指令(注:标量指令是指处理器每次处理一条数据,而矢量指令则相反,允许并行处理多条数据)和寄存器(后来被映射为相同的芯片领域如浮点运算单元寄存器),可以和x86世界的MMX/SSE/3DNow!相比。
- VFPv3-D32 as a requirement (i.e. 32 hardware FPU 64-bit registers, instead of the minimum of 16).
- VFPv3-D32作为一种最低需要(即32个硬件浮点单元64位寄存器,而非至少16个)。
Not all ARMv7-based Android devices will support NEON, but those that do may benefit in significant ways from the scalar/vector instructions.
不是所有基于ARMv7的Android设备会支持NEON,而那些支持NEON的设备可以从标矢量指令中很有意义地获得好处。
The NDK supports the compilation of modules or even specific source files with support for NEON. What this means is that a specific compiler flag will be used to enable the use of GCC ARM Neon intrinsics and VFPv3-D32 at the same time. The intrinsics are described here:
NDK支持模块的编译或甚至是特定的源代码,拥有对NEON的支持。这意味着将使用一个特定的编译器开关同时打开对GCC ARM Neon内建和VFPv3-D32的使用。内建功能在这里描述:
http://gcc.gnu.org/onlinedocs/gcc/ARM-NEON-Intrinsics.html
LOCAL_ARM_NEON:
LOCAL_ARM_NEON
---------------
Define LOCAL_ARM_NEON to 'true' in your module definition, and the NDK will build all its source files with NEON support. This can be useful if you want to build a static or shared library that specifically contains
NEON code paths.
在你的模块定义中把LOCAL_ARM_NEON定义为true,NDK则会用NEON支持构建所有源文件。如果你想构建一个特定地包含NEON代码路径的静态或动态库,这可能有用。
Using the .neon suffix:
使用.neon后缀:
-----------------------
When listing sources files in your LOCAL_SRC_FILES variable, you now have the option of using the .neon suffix to indicate that you want to corresponding source(s) to be built with Neon support. For example:
当在你的LOCAL_SRC_FILES变量中列出源文件时,你现在拥有使用.neon的选择,以指出你想把源代码相应地用Neon支持进行构建。例如:
LOCAL_SRC_FILES := foo.c.neon bar.c
Will only build 'foo.c' with NEON support.
将只对foo.c用NEON支持构建。
Note that the .neon suffix can be used with the .arm suffix too (used to specify the 32-bit ARM instruction set for non-NEON instructions), but must appear after it.
注意.neon后缀可以同时使用.arm后缀(用于指明对非NEON指令的32位ARM指令集),但必须出现在后面。
In other words, 'foo.c.arm.neon' works, but 'foo.c.neon.arm' does NOT.
换句话说,foo.c.arm.neon可以,但foo.c.neon.arm不可以。
Build Requirements:
构建需要:
------------------
Neon support only works when targetting the 'armeabi-v7a' ABI, otherwise the NDK build scripts will complain and abort. It is important to use checks like the following in your Android.mk:
Neon支持仅在目标是armeabi-v7a ABI时才可工作,否则NDK构建脚本将解释和中止。在你的Android.mk中使用类似如下方式的检查是很重要的。
# define a static library containing our NEON code
# 定义一个静态库,包含我们的NEON代码
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
include $(CLEAR_VARS)
LOCAL_MODULE := mylib-neon
LOCAL_SRC_FILES := mylib-neon.c
LOCAL_ARM_NEON := true
include $(BUILD_STATIC_LIBRARY)
endif # TARGET_ARCH_ABI == armeabi-v7a
Runtime Detection:
运行时检测:
------------------
As said previously, NOT ALL ARMv7-BASED ANDROID DEVICES WILL SUPPORT NEON ! It is thus crucial to perform runtime detection to know if the NEON-capable machine code can be run on the target device.
正如前面所说的,不是所有基于ARMv7的Android设备支持NEON!因此最重要的是执行运行时检测以知道NEON能力的机器代码是否能运行在目标设备上。
To do that, use the 'cpufeatures' library that comes with this NDK. To lean more about it, see docs/CPU-FEATURES.html.
为了做到那一点,可以使用NDK提供的cpufeatures库。想知道更多相关信息,请参考docs/CPU-FEATURES.html。
You should explicitly check that android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM, and that android_getCpuFeatures() returns a value that has the ANDROID_CPU_ARM_FEATURE_NEON flag set, as in:
你应该显式地检查android_getCpuFamily()返回ANDROID_CPU_FAMILY_ARM,并且android_getCpuFeatures()返回一个拥有ANDROID_CPU_ARM_FEATURE_NEON标记位设置的值,正如这样:
#include <cpu-features.h>
...
...
if (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM &&
(android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0)
{
// use NEON-optimized routines
// 使用NEON优化例程
...
}
else
{
// use non-NEON fallback routines instead
// 改为使用非NEON倒退例程
...
}
...
Sample code:
示例代码:
------------
Look at the source code for the "hello-neon" sample in this NDK for an example on how to use the 'cpufeatures' library and Neon intrinsics at the same time.
查看这份NDK中hello-neon例子的源代码以获得关于如何同时使用cpufeatures库和Neon内建的例子。
This implements a tiny benchmark for a FIR filter loop using a C version, and a NEON-optimized one for devices that support it.
它实现了一个使用C版本的FIR(注:有限脉冲响应)滤波器循环,以及针对支持硬件NEON的设备的经过NEON优化的小型性能比较测试。
废话少说,直接上代码吧
main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ViewFlipper android:layout_height="wrap_content" android:id="@+id/viewFlipper1" android:layout_width="fill_parent" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="第一屏" /> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="第二屏" /> </LinearLayout> </ViewFlipper> </LinearLayout>
ViewFlipperActivity.java
package cn.xhlx.viewflipper; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.GestureDetector; import android.view.GestureDetector.OnGestureListener; import android.view.MotionEvent; import android.widget.ViewFlipper; public class ViewFlipperActivity extends Activity implements OnGestureListener{ private static final String TAG = "ViewFlipperActivity"; private ViewFlipper viewFlipper; private GestureDetector detector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); detector = new GestureDetector(this); viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper1); } @Override public boolean onTouchEvent(MotionEvent event) { Log.v(TAG, "touched"); return this.detector.onTouchEvent(event); } public boolean onDown(MotionEvent e) { // TODO Auto-generated method stub return false; } public void onShowPress(MotionEvent e) { // TODO Auto-generated method stub } public boolean onSingleTapUp(MotionEvent e) { // TODO Auto-generated method stub return false; } public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // TODO Auto-generated method stub return false; } public void onLongPress(MotionEvent e) { // TODO Auto-generated method stub } public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.i(TAG, "fling…"); if(e1.getX()>e2.getX()){ viewFlipper.showNext(); }else if(e1.getX()<e2.getX()){ viewFlipper.showPrevious(); }else { return false; } return true; } }
-----------------
英文文档见android-ndk-r5b的documentation.html
属于Android Native Development Kit (NDK)的一部分
见http://developer.android.com/sdk/ndk/(需要代理)
翻译仅个人见解
-----------------
Android module paths (sharing code made easy):
Android模块路径(方便共享代码):
==============================================
Starting from r5, the Android NDK comes with a cool feature that allows you to share and reuse other people's modules more easily.
从r5开始,Android NDK引入一个很酷的特性,允许你更容易地共享和重用他人的模块。
I. Overview:
一、概述:
------------
The main idea behind this feature are:
这个特性背后的主要思想:
- You can install NDK modules outside of your main project source tree.
- 你可以在你的主工程源码树以外安装NDK模块
- You can easily 'import' them into your project with a one-line command.
- 你可以用一个单行命令简单地“导入”它们到你的工程。
In practice, here's how this works:
实际上,这里介绍它是如何工作的:
1. Your NDK_MODULE_PATH environment variable will contain a list of search paths on your system to lookup for modules.
1. 你的NDK_MODULE_PATH环境变量将包含一个在你的系统上查找模块的搜索路径列表。
It is up to you to set the variable, and to copy other modules to the directories you listed in it.
由你来决定设置变量和复制你在其中列出的目录下的其他模块。
2. To import a module, place a line like the following to, preferably at the *end* of, your Android.mk:
2. 要想导入一个模块,放置如下所示的一行指令,最好放在你的Android.mk文件结束处:
$(call import-module,<tag>)
This will look for <tag>/Android.mk under any of the directories listed in your NDK_MODULE_PATH.
这将查找在你的NDK_MODULE_PATH中列出的任意目录下的<tag>/Android.mk
(The reason why it must be at the end is to avoid messing with the results of the 'my-dir' function. See its description in docs/ANDROID-MK.html for details).
(它必须放在结束处的理由是为了避免my-dir函数结果的干扰。详细请参考docs/ANDROID-MK.html中的描述)。
3. Declare that your project's modules depend on the imported one by listing them in either your LOCAL_STATIC_LIBRARIES or LOCAL_SHARED_LIBRARIES. For example:
3. 通过在你的LOCAL_STATIC_LIBRARIES或LOCAL_SHARED_LIBRARIES中列出它们,声明你的工程模块依赖于这个导入模块。例如:
LOCAL_STATIC_LIBRARIES += <tag>
4. Rebuild!
4. 重新构建!
Remember that NDK r5 also added the ability for a module to "export" declarations to other modules that depend on it (for example, see the definition of LOCAL_EXPORT_CFLAGS in docs/ANDROID-MK.html).
记住NDK r5还添加模块的能力以“导出”声明到其它依赖于它的模块(例如,参考docs/ANDROID-MK.html中LOCAL_EXPORT_CFLAGS的定义)
A well-written module will correctly export all the things its dependees need, et voila.
一个书写良好的模块将正确地导出它的被依赖者所需要的所有东西,就是这样子(注:et voila是法语)。
Now for the full details:
现在是完全的细节:
I. NDK_MODULE_PATH:
一、NDK_MODULE_PATH:
-------------------
The NDK_MODULE_PATH variable must contain a list of directories.
NDK_MODULE_PATH变量必须包含一个目录列表。
* Due to GNU Make limitations, NDK_MODULE_PATH must not contain any space. The NDK will complain if this is not the case.
* 由于GNU Make的限制,NDK_MODULE_PATH不准包含任何空格。NDK将解释它是否违背要求。
* Use ':' as the path separator.
* 使用分号作为路径分隔符。
* On Windows, use '/' as the directory separator.
* 在Windows上,使用正斜杠作为目录分隔符(注:应该是指把Windows风格的反斜杠改为正斜杠)。
The directories of NDK_MODULE_PATH will be searched in order. The first <path>/<tag>/Android.mk file that is found during the lookup will be included automatically.
NDK_MODULE_PATH的目录将被依次搜索。第一个在查找种被找到的<path>/<tag>/Android.mk文件将自动被包含。
As a convenience, $NDK/sources is appended to your NDK_MODULE_PATH definition by the NDK build system. This allows you to easily import the helper libraries that come with it (see docs/CPU-FEATURES.html for a practical example).
作为约定,$NDK/sources被NDK构建系统尾加到你的NDK_MODULE_PATH定义中。这允许你简单地导入由它生成的辅助库(见docs/CPU-FEATURES.html中的一个实际例子)
II. Writing an import module:
二、书写一个导入模块:
-----------------------------
Writing an import module is trivial and very similar to what you do when writing project modules:
书写一个导入模块是细小而且非常类似于你在书写工程模块时所做的东西:
1. Create a sub-directory from one of your NDK_MODULE_PATH directories.
1. 在你的NDK_MODULE_PATH目录中的一个目录下创建子目录。
For example, if NDK_MODULE_PATH is defined to /home/user/ndk-modules, then create the directory /home/user/ndk-modules/my-module/
例如,如果NDK_MODULE_PATH被定义为/home/user/ndk-modules,那么创建目录/home/user/ndk-modules/my-module/
2. Place an Android.mk and eventual source code there.
2. 在那里放置一个Android.mk和最终源码。
Just like you would for a project module, where these files normally go to $PROJECT_PATH/Android.mk. In the example above, this would go to /home/user/ndk-modules/my-module/Android.mk
就像你对一个工程模块那样,这些文件通常去到$PROJECT_PATH/Android.mk中。在上面的例子中,它将集中到/home/user/ndk-modules/my-module/Android.mk
NOTE: Any Application.mk file here will be ignored.
注意:这里任何Application.mk文件将被忽略。
3. Any module that depends on your new module, would import by calling the import-module function. For example:
3. 依赖于你的新模块的任何模块,将通过调用import-module函数进行导入。例如:
$(call import-module,my-first-module)
Import modules *can* import other modules, but circular dependencies are not permitted and will be detected. Dependencies are transitive and the build system will compute all the things that need to be built for you.
导入模块可以导入其他模块,但不允许循环依赖,它会被检测到。依赖是及物的,构建系统将为你计算所有需要被构建的所有东西。
The NDK build system will not place object files or executables in your import module directory (they will all be placed under the project's build directory, e.g. $PROJECT_PATH/obj/).
NDK构建系统将不会在你的导入模块目录中放置对象文件或可执行文件(它们将全部放在工程的构建目录下,例如$PROJECT_PATH/obj/)
You can however distribute prebuilt binaries in your import module with the new PREBUILT_STATIC_LIBRARIES or PREBUILT_SHARED_LIBRARIES feature (see docs/ANDROID-MK.html).
然而你可以通过新的PREBUILT_STATIC_LIBRARIES或PREBUILT_SHARED_LIBRARIES特性,在你的导入模块中分发预构建二进制文件(见docs/ANDROID-MK.html)。
This makes it easy to package and redistribute your import module directory to third-parties.
这将使打包和重新分发你的导入模块目录到第三方变得简单。
III. Naming an import module:
三、命名一个导入模块:
-----------------------------
It is important to understand a few things related to the naming of your import module:
重要是要理解关于你的导入模块命名的一些事情:
- What 'import-module' does is really search for a file named Android.mk using the list provided by NDK_MODULE_PATH, then include while performing very little bit of house-keeping.
- import-module所做的实际上是使用NDK_MODULE_PATH提供的列表搜索名为Android.mk的文件,然后当执行非常小量的内部工作时包含它。
Your imported Android.mk can define any number of modules, with any name. As a consequence, there is no direct relationship between <name> in the following line:
你的导入Android.mk可以通过名称定义任意数量的模块。因此,在下面指令行中<name>之间不会有直接的关联:
$(call import-module,<tag>/<name>)
And the names of the modules defined under <tag>/<name>/Android.mk.
以及定义在<tag>/<name>/Android.mk下的模块名称。
IN CASE OF DOUBT, KEEP IT SIMPLE!
如果还是不明白,就让它保持简单!
If you only plan to provide one import module, just name it like the base import directory.
如果你只是计划提供一个导入模块,只要像导入基目录那样命名它就可以了。
On the other hand, you may want to provide a static and a shared version of your module: use distinct names under the same top-level Android.mk. Consider the following build script:
另一方面,你可能像提供你的模块的静态和动态版本:在相同的顶级Android.mk下使用不同的名称。考虑以下构建脚本:
$NDK_MODULE_PATH/foo/bar/Android.mk:
LOCAL_PATH := $(call my-dir)
# Static version of the library is named 'bar_static'
# 库的静态版本被命名为bar_static
include $(CLEAR_VARS)
LOCAL_MODULE := bar_static
LOCAL_SRC_FILES := bar.c
# Ensure our dependees can include <bar.h> too
# 确保我们的被依赖者还可以包含<bar.h>
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
include $(BUILD_STATIC_LIBRARY)
# Shared version of the library is named 'bar_shared'
# 库的动态版被命名为bar_shared
LOCAL_MODULE := bar_shared
LOCAL_SRC_FILES := bar.c
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
include $(BUILD_SHARED_LIBRARY)
Another module would refer to it by doing the following:
另一个模块将通过以下方法引用它:
1. Import 'foo/bar', as in:
1. 导入foo/bar,像这样:
$(call import-module,foo/bar)
2. To use the static library:
2. 要使用静态库:
...
LOCAL_STATIC_LIBRARIES := bar_static
3. Or to use the shared library:
3. 或者要使用动态库:
...
LOCAL_SHARED_LIBRARIES := bar_shared
- The module namespace is flat, so try to give your modules names that are likely to not collide with other. Note that your can use LOCAL_MODULE_FILENAME to give the name of your module's binary file, independently from its LOCAL_MODULE (see docs/ANDROID-MK.html for definition and usage). For example:
- 模块的命名空间是平的,所以尝试给你的模块一个不可能和其它模块名称冲突的名字。注意你可以使用LOCAL_MODULE_FILENAME设置你的模块的二进制文件的名称,独立于LOCAL_MODULE(见docs/ANDROID-MK.html获得其定义和用法)。例如:
include $(CLEAR_VARS)
LOCAL_MODULE := super_foo
LOCAL_MODULE_FILENAME := foo # will give libfoo.so # 将给定为libfoo.so
LOCAL_SRC_FILES := foo-src.c
LOCAL_CFLAGS := -DVOLUME=11
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := normal_foo
LOCAL_MODULE_FILENAME := foo # will also give libfoo.so # 还将给定为libfoo.so
LOCAL_SRC_FILES := foo-src.c
include $(BUILD_SHARED_LIBRARY)
Defines two modules named "super_foo" and "normal_foo" which both produce a shared library named 'libfoo.so'
定义两个模块,名为super_foo和normal_foo,它们都将产生一个名为libfoo.so的动态库。
As a consequence, only one of them can be used by your project or a conflict will happen at build time. This allows you to select either the normal or optimized version in your NDK build scripts, while keeping the same simple loading instruction in your Java sources as:
因此,只有它们其中一个可以被你的工程使用或者在构建期发生冲突。这允许你用你的NDK构建脚本选择正常或者优化版本,保持在你的Java源代码中这样子的相同而且简单的加载指令:
static {
System.loadLibrary("foo");
}
IV. Tips & Recommendations:
四、提示和建议:
---------------------------
* You don't need to import a module before you can reference it!
* 你不需要在你可以引用它前导入模块!
* Use import-module at the *end* of your Android.mk to avoid messing with the result of 'my-dir'. See the description of this function in docs/ANDROID-MK.html to understand why.
* 在你的Android.mk结束处使用import-module以避免my-dir结果的干扰。参考docs/ANDROID-MK.html中这个函数的描述以理解为什么。
* It is *strongly* suggested to use a subdirectory for your import tags, that describes its origin, as in:
* 强烈建议对你的导入标签使用子目录,以描述它的来源,像这样子:
$(call import-module,gtk/glib)
or something like:
或者类似这样:
$(call import-module,com.example/awesomelib)
IMPORTANT: THE 'android' IMPORT DIRECTORY, AND ANY OF ITS SUB-DIRECTORIES IS *RESERVED* FOR NDK USAGE. FEEL FREE TO ORGANIZE YOUR OTHER IMPORT MODULES AS YOU WANT OTHERWISE.
重要:“android”导入目录,以及它的任意子目录因为NDK的使用而保留。但是欢迎你按照自己的意愿组织其它导入模块。