概念
JNI(Java Native Interface,Java本地接口),实现了Java和其他语言的交互(主要是C/C++),如:Java程序通过JNI调用C/C++编写的在Windows上运行的DLL动态链接库。
so(shared object,共享对象),Linux系统中的动态库,类似于Windows系统中的DLL。.so有时被直接调用,有时会参与到编译中。Android由Linux内核发展而来,因此在Android系统中也使用.so。
Android NDK(Android Native Development Kit),是Google提供的一系列的工具,简化通过JNI将C/C++动态库编译为.so库的过程。NDK集成了交叉编译器,并提供了相应的.mk文件隔离CPU、平台、ABI等差异,开发者只需要简单修改mk文件,执行编译脚本就可以创建.so。
NDK与JNI的关系:
JNI是Java与其他语言交互的机制,是Java语言自身的特性,与Android无关。
Android在框架上分为Application应用层、Application Framework应用框架层、libraries类库、Linux kernel内核。在应用框架层以及之上,使用Java语言进行开发;在此之下,Android自身的类库、驱动使用C/C++编写,再通过JNI提供接口给上层的Java调用。所以,Android框架使用了大量的JNI技术,让应用层的开发人员使用Java操控C/C++。
通常的Android开发都在应用框架层以及之上进行,但有时也需要对底层进行实现。显然,对底层的开发要复杂得多,NDK则是Google推出的帮助开发者通过C/C++编写应用的开发包,包含部分Android底层中常用的C/C++的头文件、库文件、说明文档和示例代码。
NDK只需要简单地编写几个.mk文件即可将c/c++代码编译为Android的java工程/Android手机可以识别、加载和运行的库或者应用程序。
Android.mk
ndk-build命令实际是执行了%NDK_PROJECT_PATH%/jni/Android.mk这个makefile文件,如果未配置%NDK_PROJECT_PATH%环境变量,则需要定位到工程目录。同时,待编译的.c和编译配置文件Android.mk也需要位于名为jni的目录下。
Android.mk是NDK编译的配置文件,其中定义了需要编译的.c文件、依赖的.h头文件、编译出的so库名等等信息,是使用NDK进行编译的关键。
为什么使用NDK
-
代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
-
可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
-
提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
- 便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。
mk文件中的语法:
1.LOCAL_PATH
这个变量用于给出当前文件的路径。你必须在Android.mk的开头定义。例如:
LOCAL_PATH := $(call my-dir)
2.LOCAL_MODULE
这是你模块的名字,它必须是唯一的,而且不能包含空格。你必须在包含任一的$(BUILD_XXXX)脚本之前定义它。模块的名字决定了生成文件的名字。
3.LOCAL_MODULE_FILENAME
默认的情况下,编译生成的文件名为lib$(LOCAL_MODULE).so/a在使用的此变量后,会用此变量的名来覆盖系统默认的生成文件名。例如:
LOCAL_MODULE := foo-version-1
LOCAL_MODULE_FILENAME := foo生成的文件名为libfoo.so/a
4.LOCAL_SRC_FILES
要直接传递给编译器的源代码文件列表。只要列出要传递给编译器的文件即可,因为编译系统自动为你计算依赖
5.LOCAL_C_INCLUDES
可选的路径列表,相对于NDK的根目录,当编译所有的源文件(C、C++、或者汇编)时将被追加到搜索路径中
例如:
LOCAL_C_INCLUDES:=sources/foo
或者
LOCAL_C_INCLUDES:=$(LOCAL_PATH)/../foo
6.LOCAL_EXPORT_CFLAGS
定义这个变量用来记录C/C++编译器标志集合
include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo/foo.c
LOCAL_EXPORT_CFLAGS := -DFOO=1
include $(BUILD_STATIC_LIBRARY)
定义-DFOO 为1
7.import-module
此函数用于按模块名称来查找和包含模块的 Android.mk
文件。 典型的示例如下所示:
$(call import-module,<name>)
在此示例中,构建系统在 NDK_MODULE_PATH
环境变量所引用的目录列表中查找以 <name>
标记的模块,并且自动包括其 Android.mk
文件。
8.include
include可Android多以这样的形式出现,如:include $( CLEAR_VARS),include $(BUILD_SHARED_LIBRARY).其实这个include可以理解成"执行"的意思,那么执行什么呢?当然是看后边的宏了.
宏CLEAR_VARS表示清除一些变量.
宏BUILD_SHARED_LIBRARY表示生成共享库,即生成.so文件,它负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。并决定编译为什么。
因此include $(BUILD_SHARED_LIBRARY)就是指定在/system/lib/目录下生成一个lib$(LOCAL_MOUDULE).so文件,同样类型的宏如下:
CLEAR_VARS 清除LOCAL_xxx变量
BUILD_SHARED_LIBRARY 在/system/lib/目录下生成lib$(LOCAL_MOUDULE).so文件
BUILD_STATIC_LIBRARY 生成lib$(LOCAL_MOUDULE).a文件
BUILD_EXECUTABLE 在/system/bin/目录下生成可执行文件
BUILD_PACKAGE 编译成一个apk文件