Skip to content

NDK中动态注册JNI方法

Java中定义了native方法后,在C/C++中使用JNI_OnLoad函数来注册相应的方法。

一般做法如下:

  • Java中定义native方法,确定Java文件的包名和路径
  • 编写C/C++文件
    • 实现相应的jni方法
    • 根据Java文件路径和方法签名确定JNINativeMethod
    • 实现JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm,void *reserved)方法
      • 注册jni方法 RegisterNatives
  • 编译生成so文件
  • Java中加载so库文件
  • Java中调用native方法

相比于之前的静态注册,这里没有用javah生成头文件。

关于查询Java方法签名

关于查询Java方法签名,可以使用javap -s命令查询相应的class文件。

例如查看DynamicJNI.java的方法签名,先编译出DynamicJNI.class文件,再查询。

$ javap -s ./build/intermediates/classes/debug/com/rustfisher/appndkground/jni/DynamicJNI.class
Compiled from "DynamicJNI.java"
public class com.rustfisher.appndkground.jni.DynamicJNI {
  public com.rustfisher.appndkground.jni.DynamicJNI();
    descriptor: ()V

  public static native java.lang.String getHello();
    descriptor: ()Ljava/lang/String;

  public static native int meaningOfTheUniverse();
    descriptor: ()I

  static {};
    descriptor: ()V
}

查询得知getHello()的方法签名为()Ljava/lang/StringmeaningOfTheUniverse()方法签名为()I

如果方法签名错了,编译能通过,但运行时会报NoSuchMethod异常

动态注册JNI方法示例

主要文件: * dynamic_jni.cpp 方法实现文件 * DynamicJNI.java jni接口

DynamicJNI.java中定义native方法。ndkground是模块名称。

public class DynamicJNI {
    static {
        System.loadLibrary("ndkground");
    }

    public static native String getHello();

    public static native int meaningOfTheUniverse();

}

文件路径是"com/rustfisher/appndkground/jni/DynamicJNI",这个路径确定后一般不再改动。

dynamic_jni.cpp实现相关的方法

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <jni.h>

#define JNI_REG_CLASS "com/rustfisher/appndkground/jni/DynamicJNI" // path of Java file

JNIEXPORT jstring JNICALL get_hello(JNIEnv *env, jclass clazz) {
    return env->NewStringUTF("hello from jni");
}

JNIEXPORT jint JNICALL meaning_of_the_universe(JNIEnv *env, jclass clazz) {
    return 42;
}

static JNINativeMethod g_methods[] = {
    { "getHello", "()Ljava/lang/String;", (void*)get_hello},
    { "meaningOfTheUniverse", "()I", (void*)meaning_of_the_universe},
};

// must define this function
JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm,void *reserved) {
  JNIEnv *env;
  if (vm->GetEnv((void **) &env,JNI_VERSION_1_6) != JNI_OK) {
    return JNI_ERR;
  }

  jclass javaClass = env->FindClass(JNI_REG_CLASS);
  if (javaClass == NULL){
    return JNI_ERR;
  }

  int method_count = sizeof(g_methods) / sizeof(g_methods[0]);
  if (env->RegisterNatives(javaClass, g_methods, method_count) < 0) {
    return JNI_ERR;
  }

  return JNI_VERSION_1_6;
}

这里使用Cygwin执行ndk-build来编译生成so文件。

调用动态注册的jni方法 DynamicJNI.getHello()

相应代码参见 https://github.com/RustFisher/android-Basic4/tree/master/appNdkGround