JNI方法注册
JNI提供了两种Native方法的注册方式:静态注册和动态注册。 一般来说,静态注册更为简单方便,但是为了安全考虑,在Android源码与各个Android SDK源码的设计来看,大量使用到了动态注册,增加代码破译难度。
1.静态注册
静态注册的方式对应java版本和kotlin版本
JAVA
// java代码
package com.android.hudson;
class Test {
// ...
public native void stringFromJNI();
...
}
extern "C"
JNIEXPORT
void // 函数返回值
JNICALL
Java_com_android_hudson_Test_stringFromJNI(JNIEnv* env, jobject thiz) {
}
这样就建立起了JNI和Java方法的关联关系 注意:如果java native方法有static关键字修饰,那么对应的JNI的静态注册的方法就必须是将形参二类型由jobject改为jclass。
KOTLIN
package com.android.hudson
class Test {
...
extern fun stringFromJNI(): String
}
extern "C"
JNIEXPORT
jstring
JNICALL
Java_com_android_hudson_Test_stringFromJNI(JNIEnv* env, jobejct thiz) {
std::string s = "Hello World!";
return env->NewStringUTF(s.c_str());
}
总结:java中的native方法对应到JNI的静态方法格式为 Java_包名_类名_方法名(<参数列表>)
2.动态注册
动态注册的好处是可以简化函数名称,有效提高代码安全性 注册过程,声明JNINativeMethod结构体数组->重写JNI_OnLoad(JavaVM* jvm, void* revered)方法
package com.android.ahri;
class Main {
public native void setStringFromJNI(String s);
public native String getStringFromJNI();
}
const JNINativeMethod methods[] = {
{"setStringFromJNI", "(Ljava/lang/String;)V", "(void*)setstring"},
{"getStringFromJNI", "()Ljava/lang/String", "(jstring*)getstring"}
};
// JNI_OnLoad方法在java层调用System.loadLibrary()方法后由native层调用
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* jvm, void* reserved) {
JNIEnv* env = nullptr;
int result;
// 通过GetEnv拿到JNIEnv地址
result = jvm->GetEnv(reinterpret_cast<void**> (&env), JNI_VERSION_1_6);
if (result != JNI_OK) {
return -1;
}
jclass javaClassName = env->FindClass("com/android/ahri/Main");
// jni动态注册方法
jniEnv->RegisterNative(javaClassName, methods, sizeof(methods) / sizeof(JNINativeMethod));
return JNI_VERSION_1_6;
}
void setstring(JNIEnv* env, jobject thiz, jstring s) {
}
jstring getstring(JNIEnv* env, jobject thiz) {
std::string hello = "hello, world!";
return env->NewStringUTF(hello.c_str());
}
native层和java的类型映射关系
native层和java的类类型映射关系
| JNI类型 | Java类型 |
|---|---|
| jint | int |
| jlong | long |
| lboolean | boolean |
| jbyte | byte |
| jchar | char |
| jshort | short |
| jfloat | float |
| jdouble | double |
| jobject | Object |
| jclass | Class |
| jstring | String |
| jintArray | int[] |
| jlongArray | long[] |
| jbooleanArray | boolean[] |
| jbyteArray | byte[] |
| jcharArray | char[] |
| jshortArray | short[] |
| jfloatArray | float[] |
| jdoubleArray | double[] |
| jobjectArray | Object[] |
java方法签名在native层展示和java类类型对应关系
java方法签名在native层所看到的即是java经过编译后形成字节码方法签名。
| 字节码类型 | java数据类型 |
|---|---|
| Z | boolean |
| I | int |
| J | long |
| B | byte |
| S | short |
| C | char |
| F | float |
| D | double |
| V | void |
| L包名/类名; | 引用类名 |
| 前缀[ | 数组 |
- 注意:当 Java 类型为数组时,在标识前会有'['符号,例如:String[] 类型标识为 [Ljava/lang/String;
还有一种方式,通过命令行直接查看,通过Android Studio CMake运行的项目可以直接先访问该模块下build/intermediates/javac/debug/classes/对应的包名/类名,运行如下命令
javap -s 包名.类名
展示样例