• jni学习4.动态注册


    静态注册

    静态注册较为简单,只需要在Java程序中声明jni函数,随后在cpp程序中实现此jni函数即可。

    public native String stringFromJNI();//声明
    1. extern "C"
    2. JNIEXPORT jstring JNICALL
    3. Java_com_example_hello_1cmake_MainActivity_stringFromJNI(JNIEnv* env,jobject ){
    4. std::string hello = "JNI test demo";
    5. return env->NewStringUTF(hello.c_str());
    6. }//实现函数

    静态注册的实现方式为:当java程序第一次使用某个jni函数时,会进行搜索,根据包名-类名-函数名进行搜索,随后与对应的jni函数建立连接,随后便可以调用。

    由于虚拟机自动实现了注册匹配,所以在实际编写中比较省力,名字匹配了就可以直接用。

    但是静态注册有一些缺点:1.函数名称需要匹配,不能自定义。2.每个jni函数第一次被调用时都需要搜索,增加了额外开销。如果工程中的jni函数较多,在搜索方面可能会花费较多时间。

    动态注册

    动态注册通过手动注册实现虚拟机与jni的连接。优点是节约了搜索所需的开销,缺点是需要额外进行函数编写,增加了人工工作量。

    创建一个java类,里面声明两个jni函数:

    1. package com.example.hello_cmake;
    2. public class TestJni {
    3. static {
    4. System.loadLibrary("native-lib");
    5. }
    6. public native int text(String message);
    7. public static native int static_text(String message);
    8. }

    创建一个cpp文件,代码实现如下:

    1. #include
    2. #include
    3. #include
    4. #define LOG_TAG "CPPLOG"
    5. #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
    6. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    7. #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
    8. //jni函数实现1
    9. jint native_text(JNIEnv *env, jobject jobject1, jstring msg){
    10. const char *jmsg = env->GetStringUTFChars(msg, JNI_FALSE);
    11. LOGD("message = %s",jmsg);
    12. return 0;
    13. }
    14. //jni函数实现2
    15. jint native_static_text(JNIEnv *env, jobject jobject1, jstring msg){
    16. const char *jmsg = env->GetStringUTFChars(msg, JNI_FALSE);
    17. LOGD("message = %s",jmsg);
    18. return 0;
    19. }
    20. //方法注册结构体
    21. static const JNINativeMethod nativeMethod[] = {
    22. {"text", "(Ljava/lang/String;)I", (void *) native_text},
    23. {"static_text", "(Ljava/lang/String;)I", (void *) native_static_text}
    24. };
    25. //注册函数
    26. static int registNativeMethod(JNIEnv *env) {
    27. int result = -1;
    28. jclass class_text = env->FindClass("com/example/hello_cmake/TestJni");
    29. if (env->RegisterNatives(class_text, nativeMethod,
    30. sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
    31. result = 0;//返回0代表成功
    32. }
    33. return result;
    34. }
    35. //加载动态库
    36. jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    37. JNIEnv *env = NULL;
    38. int result = -1;
    39. if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
    40. if (registNativeMethod(env) == JNI_OK) {
    41. result = JNI_VERSION_1_6;
    42. }
    43. //返回值代表动态库需要的jni版本
    44. return result;
    45. }
    46. }

    在活动中进行调用:

    1. TestJni.static_text("it is a static method");
    2. new TestJni().text("it is a normal method");

     

     代码讲解

    从cpp代码中可知,为了实现动态注册,额外实现了两个函数与一个结构体。

    1. //加载动态库
    2. jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    3. JNIEnv *env = NULL;
    4. int result = -1;
    5. if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
    6. if (registNativeMethod(env) == JNI_OK) {
    7. result = JNI_VERSION_1_6;
    8. }
    9. //返回值代表动态库需要的jni版本
    10. return result;
    11. }
    12. }

    首先被调用的是JNI_OnLoad函数,该函数的返回值是需要的jni版本。

    需要注意的是,该函数在jni.h中只是进行了声明,并没有被实现,因此在此处可以直接实现。

    1. /*
    2. * Prototypes for functions exported by loadable shared libs. These are
    3. * called by JNI, not provided by JNI.
    4. */
    5. JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
    6. JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);

    随后被调用的是:

    1. //注册函数
    2. static int registNativeMethod(JNIEnv *env) {
    3. int result = -1;
    4. jclass class_text = env->FindClass("com/example/hello_cmake/TestJni");
    5. if (env->RegisterNatives(class_text, nativeMethod,
    6. sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
    7. result = 0;//返回0代表成功
    8. }
    9. return result;
    10. }

    该函数在JNI_OnLoad函数中被调用,该函数的目的是加载方法注册结构体,将jni函数与java里的声明进行注册链接。

    该函数返回值为0时说明方法注册结构体已经加载成功。

    方法注册结构体:

    1. //方法注册结构体
    2. static const JNINativeMethod nativeMethod[] = {
    3. {"text", "(Ljava/lang/String;)I", (void *) native_text},
    4. {"static_text", "(Ljava/lang/String;)I", (void *) native_static_text}
    5. };

    方法注册结构体是我们自定义函数名称与建立对应关心的核心。

    结构体内的成员依次是:java中声明的jni函数名称,java中声明的jni函数的函数签名,自定义实现的jni函数名称。

  • 相关阅读:
    矩阵论复习提纲
    安装GPU驱动,CUDA Toolkit和配置与CUDA对应的Pytorch
    SELinux零知识学习十一、SELinux策略语言之客体类别和许可(5)
    Linux笔记
    重新定义客户服务 UniPro Mailhandler 彻底改变团队处理请求模式
    前端开发总结的一些技巧和实用方法(1)
    polarssl网址
    开放实验室管理系统 毕业设计 JAVA+Vue+SpringBoot+MySQL
    c-index的计算
    $nextTick属性使用与介绍
  • 原文地址:https://blog.csdn.net/m0_37872216/article/details/126163353