Android提供NDK开发包来提供Android平台的C++开发,用来扩展Android SDK的功能。主要包括Android NDK构建系统和JNI实现与原生代码通信两部分。
Android NDK的构建系统是基于GNU Make的。Android GNU Make系统除了需要一些内部的GNU片段外,还需要两个文件:Android.mk和Application.mk。Android NDK源码给了很多的例子,以HelloJni为例,Android.mk源码:
#Android.mk必须以LOCAL_PATH变量开头
LOCAL_PATH := $(call my-dir) #清除除了LOCAL_PATH以外的LOCAL_<name>变量,例如LOCAL_MODULE与LOCAL_SRC_FILES等 include $(CLEAR_VARS) #每一个原生组件被称为一个模块 LOCAL_MODULE := hello-jni
#源文件 LOCAL_SRC_FILES := hello-jni.c #编译为共享库,即后缀名为.so include $(BUILD_SHARED_LIBRARY)
Application.mk源码:
APP_ABI := all
一般选择APP_ABI := armeabi-v7a就够了
为了建立可供主应用程序使用的模块,必须将该模块变成共享库。按照上述必不可少的步骤,可以继续编译多个共享库。
Android也可以编译静态库(后缀名为.a),但是实际的Android应用程序并不直接使用静态库,并且应用程序包中也不包含静态库。静态库可以用来构建共享库。但是,当静态库与多个共享库相连时,应用程序包中会包含静态库的多个副本,徒增应用程序包的大小。这种情况下,可以不构建静态库,而是将通用模块作为共享库建立起来,动态连接依赖模块以消除重复的副本。
……
……
……
Prebuilt库
共享模块编译时要求有源代码,为此Android提供了Prebuilt库,以下场合,Prebuilt库是非常有用的:
其他构建系统变量:
LOCAL_CFLAGS:一组可选的编译器标志,在编译C和C++源文件的时候会被传送给编译器;
LOCAL_CPP_FLAGS:一组可选的编译器标志,在只编译C++源文件时被传送给编译器;
LOCAL_LDLIBS:链接标志的可选列表,它主要用于传送要进行动态链接的系统库列表。如链接日志库:
LOCAL_LDFLAGS += -llog
APP_CPPFLAGS:编译器标志,在编译任何模块的C++源文件时这些标志都会被传送给编译器。
nkd-build脚本命令:
ndk-build –C /project path ndk-build –B ndk-build clean
JDK自带一个名为javah的命令行工具,该工具由Java类文件的原始定义生成原生函数名及其参数列表。C/C++源文件只需要包含这个头文件并提供原生方法实现。
Javah的参数列表如下:
C:\Users\jiayayao>javah
用法:
javah [options] <classes>
其中, [options] 包括:
-o <file> 输出文件 (只能使用 -d 或 -o 之一)
-d <dir> 输出目录
-v -verbose 启用详细输出
-h --help -? 输出此消息
-version 输出版本信息
-jni 生成 JNI 样式的标头文件 (默认值)
-force 始终写入输出文件
-classpath <path> 从中加载类的路径
-cp <path> 从中加载类的路径
-bootclasspath <path> 从中加载引导类的路径
<classes> 是使用其全限定名称指定的
(例如, java.lang.Object)。
生成头文件的命令行参数如下:
javah -classpath bin/classes com.example.hellojni.Hellojni
Java方法stringFromJNI不带任何参数,但是原生方法带两个参数:
这里注意C与C++代码稍有不同,C代码如下:
return (*env)->NewStringUTF(env, "Hello from JNI ! ");
C++代码如下:
return env->NewStringUTF("Hello from JNI ! ");
这是因为C++代码中,JNIEnv实际上是一个C++类实例,JNI函数以成员函数的形式存在,因此JNI方法调用不要求JNIEnv实例作参数。
Java有两种数据类型:基本数据类型和引用数据类型:基本数据类型中Java/JNI/C++的映射关系如下:
引用类型的类型映射关系如下:
引用类型以不透明的引用方式传递给原生代码,而不是以原生数据类型的的形式呈现,因此引用类型不能直接使用和修改。JNI提供了与这些引用类型密切相关的一组API。
Get<Type>ArrayRegion函数将给定的基本Java数组复制到给定的C数组中,如程序:
从C数组向Java数组提交所作的修改,如程序:
原生方法的内存分配超出了虚拟机的管理范围,且不能用虚拟机的垃圾回收器回收原生方法中的内存。
原生代码回到Java损耗性能,建议将所有需要的参数传递给原生代码调用,而不是让原生代码回到Java中。
GetStringUTFChars
ReleaseStringUTFChars