跳转至

NDK中动态注册JNI方法

更新日期 2022-6-21
  • 2022-6-21 更新例子
  • 2016-8-20 创建文档

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异常

动态注册native方法示例1

主要文件:

  • 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

动态注册native方法示例2

这是一个修改老版本webrtc代码的例子。在原来的基础上增加动态注册的native方法。

头文件加入方法声明

.h头文件
1
2
3
4
static void JNICALL VerticeToNegStatic(JNIEnv * env,jobject,jlong context);
void VerticeToNeg();
static void JNICALL VerticeToPosStatic(JNIEnv * env,jobject,jlong context);
void VerticeToPos();

cpp代码中注册,先找到Java的类_javaRenderClass;然后调用注册的方法env->RegisterNatives

.cc文件 动态注册部分
// get the ViEAndroidGLES20 class
jclass javaRenderClassLocal =
    env->FindClass("org/webrtc/videoengine/ViEAndroidGLES20");
if (!javaRenderClassLocal) {
  WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id,
                "%s: could not find ViEAndroidGLES20", __FUNCTION__);
  return -1;
}

// create a global reference to the class (to tell JNI that
// we are referencing it after this function has returned)
_javaRenderClass =
    reinterpret_cast<jclass> (env->NewGlobalRef(javaRenderClassLocal));
if (!_javaRenderClass) {
  WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id,
                "%s: could not create Java SurfaceHolder class reference",
                __FUNCTION__);
  return -1;
}

// Delete local class ref, we only use the global ref
env->DeleteLocalRef(javaRenderClassLocal);

// ....

JNINativeMethod nativeFunctions[5] = {
  { "DrawNative",
    "(J)V",
    (void*) &AndroidNativeOpenGl2Channel::DrawNativeStatic, },
  { "CreateOpenGLNative",
    "(JII)I",
    (void*) &AndroidNativeOpenGl2Channel::CreateOpenGLNativeStatic },
  { "ChangeMirrorXNative",
    "(J)V",
    (void*) &AndroidNativeOpenGl2Channel::ChangeMirrorXNativeStatic },
  { "VerticeToNegNative",
    "(J)V",
    (void*) &AndroidNativeOpenGl2Channel::VerticeToNegStatic },
  { "VerticeToPosNative",
    "(J)V",
    (void*) &AndroidNativeOpenGl2Channel::VerticeToPosStatic },
};
if (env->RegisterNatives(javaRenderClass, nativeFunctions, sizeof(nativeFunctions) / sizeof(nativeFunctions[0])) == 0) {
  // 注册成功
} else {
  // 报警告..
}

.cc文件调用代码
void JNICALL AndroidNativeOpenGl2Channel::VerticeToNegStatic(
    JNIEnv * env, jobject, jlong context) {
  // 业务逻辑
}

void AndroidNativeOpenGl2Channel::VerticeToNeg() {
  // 业务逻辑
}

void JNICALL AndroidNativeOpenGl2Channel::VerticeToPosStatic(
    JNIEnv * env, jobject, jlong context) {
  // 业务逻辑
}

void AndroidNativeOpenGl2Channel::VerticeToPos() {
  // 业务逻辑
}

Java代码中对应的方法

Java里的native方法
private native int CreateOpenGLNative(long nativeObject,
                                      int width, int height);

private native void DrawNative(long nativeObject);

private native void ChangeMirrorXNative(long nativeObject);

private native void VerticeToNegNative(long nativeObject);

private native void VerticeToPosNative(long nativeObject);

本文也发布在

简书

本站说明

一起在知识的海洋里呛水吧。广告内容与本站无关。如果喜欢本站内容,欢迎投喂作者,谢谢支持服务器。如有疑问和建议,欢迎在下方评论~

📖AndroidTutorial 📚AndroidTutorial 🙋反馈问题 🔥最近更新 🍪投喂作者

Ads