标签:
由于网上关于JNI/NDK相关的知识点介绍的比较零散而且不具备参照性,所以写了这篇JNI/NDK学习笔记,便于作为随时查阅的工具类型的文章,本文主要的介绍了在平时项目中常用的命令、JNI数据类型、签名等,便于查阅相关资料。文末相关参考资料比较适合刚接触或者不熟悉Android NDK开发的朋友参阅。
由于JNI对应的头文件由javah工具根据对应的.class文件生成,所以在进行JNI编程之前,写好Java代码后需要先编译,在使用javah生成对应的头文件
举例说明:
生成普通的JNI头文件
javah -classpath path -jni -d outputdirpath com.mrljdx.JavaNativeCode
在Java函数中包含Android相关的参数代码,则需要在classpath中添加android.jar包的绝对路径地址
javah -classpath path:$ANDROID_HOME/path/android.jar -jni -d outputdirpath com.mrljdx.JavaNativeCodeWithAndroid
-s: 显示签名(只显示public类型的签名) -p:显示所有函数、成员变量的签名
举例说明:
javap -classpath pacakage_path_dir -s -p com.mrljdx.JavaCode
JNI的数据类型包括:基本类型和引用类型。这一点和Java的语言特性一致,基本类型包括jboolean、jchar、jint、jlong、jbyte、jshort、jfloat、jdouble、void,与Java类型的对应关系如下:
JNI类型 |
Java类型 |
描述 |
jboolean |
boolean |
无符号8位整型 |
jbyte |
byte |
有符号8位整型 |
jchar |
char |
无符号16位整型 |
jshort |
short |
有符号16位整型 |
jint |
int |
32位整型 |
jlong |
long |
64位整型 |
jfloat |
float |
32位整型 |
jdouble |
double |
64位整型 |
void |
void |
无类型 |
JNI中引用类型主要有类、对象和数组,这点也上符合Java的语法规范,对应的关系如下:
JNI 类型 |
Java引用类型 |
描述 |
jobject |
Object |
Object类型 |
jclass |
Class |
Class类型 |
jstring |
String |
String类型 |
jobjectArray |
Object[] |
对象数组 |
jbooleanArray |
boolean[] |
boolean数组 |
jbyteArray |
byte[] |
byte数组 |
jcharArray |
char[] |
char数组 |
jshortArray |
short[] |
short数组 |
jintArray |
int[] |
int数组 |
jlongArray |
long[] |
long数组 |
jfloatArray |
float[] |
float数组 |
jdoubleArray |
double[] |
double数组 |
jthrowable |
Throwable |
Throwable |
JNI的类型签名标识了一个特定的Java类型,这个类型可以是类和方法,也可以是数据类型。
Java类型 |
签名 |
boolean |
Z |
byte |
B |
char |
C |
short |
S |
int |
I |
long |
J |
float |
F |
double |
D |
void |
V |
可以发现除了 long基本数据类型的签名为J之外其他的都比较容易辨识,估计是由于之前的类类型的签名开头为L+包名+类名+;设计者为了区分所以签名为J
小技巧:使用 类似于javap -classpath pathdir -s -p com.sample.JavaCode 的 javap -s -p 命令也可以帮助查看一些类中各种方法和成员变量的签名。
注:JNIEnv * 可以简单的理解为Java和C/C++ 之间相互调用的桥梁,我们可以通过JNIEnv * 调用C/C++定义的方法,也可以在C/C++中通过JNIEnv * 来调用Java类中的方法。下面将会讲到C/C++中调用Java的方法,注意JNIEnv *的作用。
首先说明一点,在Android开发过程中使用NDK主要是为了提高代码的安全性,有些游戏公司可能是为了方便利用已有的C/C++开源库来进行平台移植,其实在性能提升方面,NDK的作用并不是很明显。所以有时候一些在Java中实现起来非常简单的代码放在JNI里面做会显得吃力不讨好,所以干脆就直接在JNI中调用Java的方法,我们只把加密和验证的一些逻辑写到JNI层就行了。
在JNI中调用Java方法流程如下:
可能流程说的比较抽象,用代码简单说明一下:
定义静态方法:
//对应包名:com.mrljdx.jni.HelloJNIpublic static void helloJava() {
System.out.println("Hello JavaCode");
}
JNI声明静态方法:
static void static_helloJava(JNIEnv *env){
jclass clazz = env->FindClass("com/mrljdx/jni/HelloJNI");
jmethodID mid = env->GetStaticMethodID(clazz, "helloJava", "()V");
env->CallStaticVoidMethod(clazz, mid);
}
调用声明好的静态方法:
static_helloJava(env);
在项目的gradle.properties中添加ndk支持:
android.useDeprecatedNdk=true
配置build.gradle看代码注释:
defaultConfig {
minSdkVersion 9
targetSdkVersion 23
versionCode 1
versionName "1.0"
//配置ndk 支持
ndk {
//编译的so库名称 libsecurity.so
moduleName "security"
//指定编译后的库支持的平台
abiFilters "armeabi", "mips", "x86", "armeabi-v7a"
//用于指定应用应该使用哪个标准库,此处添加c++库支持
stl "stlport_static"
}
}
在AndroidStudio中写JNI代码有一个比较爽的地方,就是Android.mk系统会在编译时自动帮你生成,你只需要配置build.gradle就行了。注意jni相关代码需要放在src/main/jni目录下。
在我们做产品的时候,应该考虑该用JNI&NDK的时候就用,一切出发点是基于用户的体验和数据安全,我觉得在以下几种情况下建议使用NDK:
文章来自:Sun‘刺眼的博客
标签: