zoukankan      html  css  js  c++  java
  • Android Studio NDK JNI动态注册本地方法

    概述

    可能大家觉得javah生成的函数名又臭又长,不太好看。这里可以提供另外一种方法来动态注册c++函数,让其根Java中的native方法关联起来。

    实现

    这里通过JNIEnv的Resisternatives方法完成方法的注册。相关方法介绍:

    //方法映射描述结构体
    typedef struct {
        const char* name;//Java方法名
        const char* signature;//方法签名
        void*       fnPtr;//C++ 方法指针
    } JNINativeMethod;
    
    //这是JNIEnv提供的注册本地方法
    //clazz:方法对应的class
    //methods:对应的方法数组指针
    //nMethods:有几个方法
    //返回值:注册成功返回JNI_OK
    jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
            jint nMethods);
    
    //当本地库被加载时VM调用JNI_OnLoad(例如,通过系统调用LoadLibrary)。JNI_OnLoad必须返回由本地库所需的JNI版本。
    //为了使用任何新的JNI函数,一个本地库必须导出JNI_OnLoad函数并返回JNI_VERSION_1_2或更高的版本。
    //如果本地库不导出JNI_OnLoad功能,VM假定库只需要JNI_VERSION_1_1版本。
    //如果虚拟机不认JNI_OnLoad返回的版本号,本地库不能加载。
    JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved);

    看了上面的函数,我们可以知道在loadLibrary的时候会首先调用JNI_OnLoad。因此打算在JNI_OnLoad中完成方法注册:

    /filename:my.cpp
    // Created by wastrel on 2016/9/8.
    //
    #include <stddef.h>
    #include "jni.h"
    
    //返回一个字符串
    JNIEXPORT jstring JNICALL native_hello(JNIEnv *env, jclass clazz) {
        return env->NewStringUTF("Hello from C++");
    }
    //求两个int的值
    JNIEXPORT jint JNICALL native_add(JNIEnv *env, jobject object, jint a, jint b) {
        return a + b;
    }
    //方法数组,JNINativeMethod的第一个参数是Java中的方法名,第二个参数是函数签名,第三个参数是对应的方法指针。
    //Java方法的签名一定要与对应的C++方法参数类型一致,否则注册方法可能失败。
    static JNINativeMethod method_table[] = {
            {"native_hello", "()Ljava/lang/String;", (void *) native_hello},
            {"native_add",   "(II)I",                (void *) native_add}
    };
    
    JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
        //OnLoad方法是没有JNIEnv参数的,需要通过vm获取。
        JNIEnv *env = NULL;
        if (vm->AttachCurrentThread(&env, NULL) == JNI_OK) {
           //获取对应声明native方法的Java类
          jclass  clazz = env->FindClass("com/example/registerjni/HelloJNI");
            if (clazz == NULL) {
                return JNI_FALSE;
            }
            //注册方法,成功返回正确的JNIVERSION。
            if (env->RegisterNatives(clazz, method_table, sizeof(method_table)/ sizeof(method_table[0]))==JNI_OK) {
                return JNI_VERSION_1_4;
            }
        }
        return JNI_FALSE;
    }

    对应的native声明Java文件:

    package com.example.registerjni;
    
    /**
     * Created by wastrel on 2016/9/8.
     */
    public class HelloJNI {
        static {
            System.loadLibrary("helloJNI");
        }
    
        public native static String native_hello();
        public native int native_add(int a,int b);
    }
    注意:如果你的方法声明了static,那么对应的第二个参数应该是jclass类型。如果你清楚他的实际类型,即便你写成jobject也不会引起程序错误,因为jclass本身也是jobject。但还是建议写为正确的方式,这样可以显得清晰一些。
    
    注意:C++和Java有所不同,如果把static JNINativeMethod method_table 写在开头,你编译的时候会提示找不到函数指针,这是因为自上而下编译的原因。所以应该把定义写在实现方法后面,或者用一个头文件来完成函数的定义。

    后记

    这只是一种建立Native方法与Java方法的方式,如果没有特殊的需要,不建议使用这种方法来注册。因为使用这种优雅的注册方法,你必须确保你的函数方法和签名的正确性。这可能会增加出错的概率。

  • 相关阅读:
    videojs播放直播源rtmp时画面在左上角解决方案
    常用git操作命令
    vue使用vue-video-player在直播中的应用
    element-ui隐藏组件scrollbar的使用
    Array.reduce()学习
    阿里云物联网套件(iot)设备间通信(M2M)在web端的实践
    前端axios下载excel(二进制)
    nodejs连接阿里云物联网套件(mqtt)
    base64格式图片转换为FormData对象进行上传
    NodeJS之微信开发
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/10606061.html
Copyright © 2011-2022 走看看