Android NDK 概述(Android NDK Overview)

来源:互联网 发布:红豆薏米粉品牌 知乎 编辑:程序博客网 时间:2024/05/19 01:07

Android NDK 功能概述

Android NDK就是一套用于把C/C++源码编译得到的二进制机器码嵌入应用安装包的工具。

Android NDK是对Android SDK的一个补充,可以帮助你:

1)生成符合JNI规范的共享库(运行在Android 1.5以上系统,主要是ARM CPU)

2)将共享库拷贝到工程合适位置(拷贝之后,在生成apk时,该共享库自动打包进最终的apk文件)。

3)将来的NDK版本,会提供调试本地码的工具(利用gdb连接和大量源码及符号信息)。

Android NDK提供了

1)一套交叉编译工具链(编译器、链接器等),可在Linux, OS X和Windows(通过 Cygwin)系统上直接编译出ARM代码。

2)一套系统头文件。这些头文件对应Android平台的稳定API(见Android Stable APIs)。所谓稳定API,就是今后更高版本的Android系统都会支持的API。

3)一个编译系统。开发人员只需编写很短的编译脚本文件,描述哪些源码文件需要编译,如何编译。该编译系统能处理繁琐的工具链/平台/CPU/ABI 等细节。今后版本的NDK将支持更多工具链、平台、系统接口,而不用修改编译脚本。

注意事项:

1)Android NDK只能用于Cupcake以后(即Android 1.5 以后)。尤其是1.0和1.1版Android,NDK完全不支持(因为ABI和工具链有一些修改,导致了不兼容)。

2)Android系统除稳定API,还自带很多共享库,但大部分都是可变的,今后有可能做修改。使用非稳定的API,在系统更新之后,程序可能无法工作。

I. Android NDK的目标

Android 虚拟机支持通过JNI调用本地码实现的函数(本地码就是C/C++编写,在不同平台上编译得到的机器指令。对arm而言,就是arm指令;对x86而言,就是x86指令)。这意味着:

1)你在用java开发Android应用时,可以用native关键字,声明相应方法是在本地码中实现的。例如:

    native byte[] loadFile(String filePath);

2)对于这些native方法,你必须提供一个共享库(*.so文件),包含这些方法的实现。这个共享库将来会打包进apk。这个共享库必须按照UNIX共享库的命名规则命名,例如:

        lib<something>.so

    它还必须包含一个标准的JNI入口函数。

3)java程序在使用这些native方法之前,必须加载该共享库。例如,下面代码是在启动的时候加载:

    static {

        System.loadLibrary("FileLoader");   // 这里应该取共享库文件名中间部分,不含“lib”前缀和“.so”后缀。

    }

II. Android NDK不能完成的事情

用NDK开发一个一般化程序不是好方法,应该尽量用Java开发,处理各种Android系统事件和Android程序生命周期。

不过,你可以利用NDK来编写一个程序(大部分是C/C++),然后用一个小的启动器(Java)来加载。

对JNI有一程度的了解,能够帮助你更好地使用NDK。

NDK其实只提供了Android系统库的少数有限API,更多API因为可能修改所以没有在NDK中提供(因此找不到相应的头文件)。

III. NDK开发步骤

这里初步描述一下如何使用Android NDK进行开发:

1)将源码文件放在 $PROJECT/jni/...

2)编写 $PROJECT/jni/Android.mk 文件(编译脚本,供Android NDK编译系统使用)

3)可选:编写 $PROJECT/jni/Application.mk (供Android NDK编译系统使用。非必须。

可以用来支持更多的CPU平台,或用来修改编译器参数)。

4)进入工程根目录之后,运行ndk-build,完成编译。编译完成后,会自动拷贝strip过的共享库

到工程的根目录。然后,通过正常步骤来生成最终的apk文件。


上面步骤更详细的描述如下:

III.1 配置 NDK: 老版本NDK需要运行 build/host-setup.sh 脚本(配置NDK)。NDK r4之后(含r4),不需要该步骤。

III.2 拷贝C/C++文件到jni目录:  将 C/C++ 文件拷贝到 $PROJECT/jni/ 。该目录下的文件结构任意,不影响最终的apk。

    C++代码的默认文件扩展名为cpp,其他扩展名也可以(见 android-mk )。 也可以把源码放到其他地方,只要在Android.mk中写明即可。

III.3 编写 Android.mk:  该文件有自己的语法(见android-mk)。NDK是把那些C/C++代码文件看做不同的“模块”,每个模块可以是一个静态库,也可以是一个动态库。在一个Android.mk文件中可以定义多个模块。也可以为每个模块写一个android.mk。

注意:同一个Android.mk可能被Android编译系统解析多次,所以要注意前面的环境变量对后面的影响。

默认情况下,NDK会寻找 $PROJECT/jni/Android.mk 文件。如果jni目录下还有子目录,可以在这些子目录中创建Android.mk,然后在最顶层的Android.mk文件中把这些Android.mk包含进来。有一个辅助函数(将把jni目录下所有的Android.mk包含进来):

include $(call all-subdir-makefiles)

III.4 编写 Application.mk(可选): Application.mk描述程序自身,而Android.mk描述模块如何编译。Application.mk提供很多功能,一些重要功能如下:

1)列出你的程序必需的模块   2)指定CPU架构   3)可选信息:例如想要release还是debug版本,C/C++编译器标志等全局性信息。

该文件是可选的,如果你没有提供,则编译系统用默认的Application.mk(将Android.mk文件中描述的模块全部编译,默认CPU为armeabi)

使用Application.mk的两种方法:

1)放在 $PROJECT/jni/Application.mk ,会被ndk-build脚本自动检测。

2)放在 $NDK/apps/<name>/Application.mk,进入NDK所在目录,执行 make APP=<name> 来完成编译。

以上第2)种方法用于NDK r4以前,现在依然支持(兼容)。 建议用方法1),因为它更简单,且不需要修改NDK安装目录。

III.5 调用NDK编译系统

调用的方式有2种:1)直接执行 ndk-build (更好) 2)在 $NDK/apps 下新建一个子目录的方法(老方法)

这两种方法编译成功后,将把strip过的二进制模块(即共享库)拷贝到工程目录(没有strip的模块也会保留,用于调试)。

1)用 ndk-build 命令

ndk-build脚本位于NDK的根目录,可以在进入工程目录之后调用(工程目录就是你的Android工程中,AndroidManifest.xml文件所在的目录)。

例如:

        $cd /home/wuxiao/workspace/NotePad  (NotePad 是用eclipse创建的)

        $ndk-build

        $ndk-build clean         相当于 make clean

        $ndk-build -B V=1        -B 是强制重新编译,  V=1 表示打印详细信息

编译出二进制代码后,按普通Android的app的步骤,生成apk。此时共享库已拷贝到工程的根目录,

所以这个共享库会进入apk。

2)老方法:用 $NDK/apps/<name>/Application.mk 方式(为了兼容而保留,即将被删除,此处省略)

IV. 编译生成apk

用NDK生成二进制模块后,按正常步骤编译生成apk(可用ant或ADT,在Android SDK文档中讲解)。

生成的apk包含该共享库。用户在安装该apk的时候,共享库会被系统自动提取。

V. 调试该共享库

NDK提供了一个ndk-gdb,运行后开启一个调试会话,连接到你的应用程序。

这种本地码调试只能在Android 2.2以上系统应用,不需要root权限或其他特权,只需要开启调试。

大致步骤如下(详细步骤见文档 NDK-BUILD):

1)在 AndroidManifest.xml 中设置调试功能为开启(将 android:debuggable 设置为 true)。

2)用 ndk-build 编译共享库,生成apk,安装apk

3)启动该程序

4)cd进入工程目录,执行 ndk-gdb,就进入了gdb命令行。

原创粉丝点击