首页 > 编程语言 >实现JNI的另一种方法:使用RegisterNatives方法传递和使用Java自定义类

实现JNI的另一种方法:使用RegisterNatives方法传递和使用Java自定义类

时间:2022-12-26 10:44:33浏览次数:41  
标签:RegisterNatives Java 函数 自定义 void env obj JNI

除了使用传统方法实现JNI外,也可以使用RegisterNatives实现JNI。和传统方法相比,使用RegisterNatives的好处有三点: 1、C++中函数命名自由,不必像javah自动生成的函数声明那样,拘泥特定的命名方式; 2、效率高。传统方式下,Java类call本地函数时,通常是依靠VM去动态寻找.so中的本地函数(因此它们才需要特定规则的命名格式),而使用RegisterNatives将本地函数向VM进行登记,可以让其更有效率的找到函数; 3、运行时动态调整本地函数与Java函数值之间的映射关系,只需要多次call RegisterNatives()方法,并传入不同的映射表参数即可。 为了使用RegisterNatives,我们需要了解JNI_OnLoad和JNI_OnUnload函数。JNI_OnLoad()函数在VM执行System.loadLibrary(xxx)函数时被调用,它有两个重要的作用:

  • 指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,例如JNI 1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。
  • 初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当,RegisterNatives也在这里进行。

JNI_OnUnload()当VM释放该组件时被调用,JNI_OnUnload()函数的作用与JNI_OnLoad()对应,因此在该方法中进行善后清理,资源释放的动作最为合适。

Java代码和使用哪种方式实现JNI无关,如下所示:

 

复制代码
class MyJavaClass
{
 public int iValue;
 public void Squa(){iValue = iValue*iValue;}
}
public class RegisterNativesTest
{
 static{
  System.load("/home/zmh/workspace/RegisterNativesTest/lib/libCallClass.so");
 }
 public static void main(String[] args)
 {
  RegisterNativesTest app = new RegisterNativesTest();
  MyJavaClass obj = new MyJavaClass();
  obj.iValue = 10;
  System.out.println("Before callCustomClass: " + obj.iValue);
  app.callCustomClass(obj);
  System.out.println("After callCustomClass: " + obj.iValue);
 }
 private native void callCustomClass(MyJavaClass obj);
}
复制代码

 

C++的代码可以分为两部分:实现callCustomClass方法和注册callCustomClass。
实现callCustomClass方法的代码如下:

复制代码
void callCustomClass(JNIEnv* env, jobject, jobject obj)
{
 jclass cls = env->GetObjectClass(obj);
 jfieldID fid = env->GetFieldID(cls, "iValue", "I");
 jmethodID mid = env->GetMethodID(cls, "Squa", "()V");
 int value = env->GetIntField(obj, fid);
 printf("Native: %d\n", value);
 env->SetIntField(obj, fid, 5);
 env->CallVoidMethod(obj, mid);
 value = env->GetIntField(obj, fid);
 printf("Native:%d\n", value);
}
复制代码

C++函数内忽略this指针,他所接收的jobject是“Java程序代码传递过来的Java object reference“在原生端的形式,在C++中对jobject的改变在java中也是有效的。如果想要访问Java数据成员和函数,得先使用GetFieldID或GetMethodID分别获取数据成员和函数的识别码,这两个函数的参数依次为1)class object;2)包含元素名称的字符串;3)表示类型的字符串。
注册callCustomClass在JNI_OnLoad中进行,代码如下:

复制代码
static JNINativeMethod s_methods[] = {
{"callCustomClass", "(LMyJavaClass;)V", (void*)callCustomClass},
};

int JNI_OnLoad(JavaVM* vm, void* reserved)
{
 JNIEnv* env = NULL;
   if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
   {
      return JNI_ERR;
   }

 jclass cls = env->FindClass("LRegisterNativesTest;");
   if (cls == NULL)
   {
      return JNI_ERR;
   }

   int len = sizeof(s_methods) / sizeof(s_methods[0]);
   if (env->RegisterNatives(cls, s_methods, len) < 0)
   {
      return JNI_ERR;
   }

   return JNI_VERSION_1_4;
}
复制代码

在C++和Java中创建关联的是JNINativeMethod,它在jni.h中定义:

复制代码
/*
 * used in RegisterNatives to describe native method name, signature,
 * and function pointer.
 */
typedef struct {
    char *name;
    char *signature;
    void *fnPtr;
} JNINativeMethod;
复制代码

name是java中定义的native函数的名字,fnPtr是函数指针,也就是C++中java native函数的实现。signature是java native函数的签名,可以认为是参数和返回值。比如(LMyJavaClass;)V,表示函数的参数是LMyJavaClass,返回值是void。对于基本类型,对应关系如下:

复制代码
字符     Java类型     C/C++类型
V           void         void
Z         jboolean      boolean
I            jint           int
J           jlong         long
D         jdouble       double
F          jfloat         float
B          jbyte         byte
C          jchar          char
S          jshort        short
复制代码

数组则以"["开始,用两个字符表示,比如int数组表示为[I,以此类推。
如果参数是Java类,则以"L"开头,以";"结尾,中间是用"/"隔开包及类名,例如Ljava/lang/String;,而其对应的C++函数的参数为jobject,一个例外是String类,它对应C++类型jstring。

标签:RegisterNatives,Java,函数,自定义,void,env,obj,JNI
From: https://www.cnblogs.com/kn-zheng/p/17005189.html

相关文章

  • 【HMS Core】升级SDK报错app_id failed: java.io.FileNotFoundException: agconnect-s
    问题描述:升级推送服务最新版SDK报错app_idfailed:java.io.FileNotFoundException:agconnect-services.json问题分析:从日志来看,是没有找到agconnect-services.json文件。......
  • Java小白必会!Intellij IDEA安装、配置及使用详细教程
    一.前言所谓工欲善其事必先利其器,一个好的工具对工作效率的提升是有非常大的帮助的。而在咱们程序员开发时,一个好的集成开发工具对于提高我们的编码效率和编程体验来说也是......
  • Java版小米商城项目简介
    小米商城课程详情1.课程介绍本套课程主要是基于Servlet、HTML、JS、Ajax、JSP、MySQL、JDBC、Tomcat、微信支付等web技术,仿照小米商城来实现一个电子商城项目。注意:本课程配......
  • 1:Web开发入门-Java Web
    (目录)1.1什么是Web应用程序在Sun的JavaServlet规范中,对JavaWeb应用作了这样定义:“JavaWeb应用由一组Servlet、HTML页、类、以及其它可以被绑定的资源构成。它可以在......
  • java的异常体系图
    本文主要讲述java的异常体系图老韩图解:  自己理解: ......
  • JavaVM & JNIEnv
    JNIEnv提供了大多数的JNI函数。你的本地方法都会接收JNIEnv作为第一个参数。JNIEnv用于本地线程存储。因此,你不能在线程间共享同一个JNIEnv。如果一个代码段没有其他方式获......
  • 【HMS Core】升级SDK报错app_id failed: java.io.FileNotFoundException: agconnect-s
    ​问题描述:升级推送服务最新版SDK报错app_idfailed:java.io.FileNotFoundException:agconnect-services.json 问题分析:从日志来看,是没有找到agconnect-services.js......
  • 第一个Java程序——Hello World
    环境:记事本、DOS窗口步骤:编写代码文件,后缀改为java打开DOS窗口找到代码存放位置使用 javacHelloWorld.java进行编译使用javaHelloWorld 执行代码:1p......
  • JavaScript Array 对象高阶方法 some、filter、indexOf
    前言1.some()检测数组中的元素是否满足指定条件2.filter()过滤掉数组中不满足指定条件的值3.indexOf()判断一个元素是否在数组中存在前言JavaScriptArra......
  • Java 字符编码
    转义字符一些特殊字符。转义字符含义\n换行\r回车\0结束字符\s空格\’单引号\"双引号\\反斜杠字符编码ASCIIAmericanStandard......