NDK浅深,层层拨开,适时淡尝辄止(一):JNI方法注册

126 阅读2分钟

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类型
jintint
jlonglong
lbooleanboolean
jbytebyte
jcharchar
jshortshort
jfloatfloat
jdoubledouble
jobjectObject
jclassClass
jstringString
jintArrayint[]
jlongArraylong[]
jbooleanArrayboolean[]
jbyteArraybyte[]
jcharArraychar[]
jshortArrayshort[]
jfloatArrayfloat[]
jdoubleArraydouble[]
jobjectArrayObject[]

java方法签名在native层展示和java类类型对应关系

java方法签名在native层所看到的即是java经过编译后形成字节码方法签名。

字节码类型java数据类型
Zboolean
Iint
Jlong
Bbyte
Sshort
Cchar
Ffloat
Ddouble
Vvoid
L包名/类名;引用类名
前缀[数组
  • 注意:当 Java 类型为数组时,在标识前会有'['符号,例如:String[] 类型标识为 [Ljava/lang/String;

还有一种方式,通过命令行直接查看,通过Android Studio CMake运行的项目可以直接先访问该模块下build/intermediates/javac/debug/classes/对应的包名/类名,运行如下命令

javap -s 包名.类名

展示样例

image.png