【再探JNI__2】

昨天,我写了一下JNI的基本组成和功能。今天继续完善以下几个部分

  0:关于实例引用和类引用(昨天遗留的关于第二个参数的问题)

其实这个有点想太多了。今天验证了一下,写了两个不同的native方法。

//Demo.javaclass Demo {
        //略...
        private static native  void StaticMethod(String userInput);//静态方法
	private native void InstaceMethod();//实例方法
	public static void main(String args[]) {
	//省略...
	}
}

对应编译出来的头文件是这样的

//略...
JNIEXPORT void JNICALL Java_Demo_StaticMethod
  (JNIEnv *, jclass, jstring);//...
JNIEXPORT void JNICALL Java_Demo_InstaceMethod
  (JNIEnv *, jobject);
//略...

一些简单的例子都不会用到jclass,但是一旦有用到静态的方法去操作一些东西,就会用到jclass这个类引用了。他们唯独的区别其实也就在这了。

  1:JNI的传参

  在上一篇博文里已经提到了JAVA层的对象传递给了JNI层。它是通过一个C指针类型,指向了JAVA VM的内部结构,因此千万不可以为了效率或者方便起见而绕过JNI的内部函数直接操作这个指针。同时JNI中的前面两个参数也在上篇文章已经介绍过,如果涉及到在native方法内传递参数,那么就涉及到第三个参数了。比如需要传递一个字符串,那么第三个参数就会是jstring。这里的jstring是一种类型。在JNI中,数据类型基本上可以这么认为,在原有的大家所熟悉的数据类型前面加了j。比如int类型为jint、boolean为jboolean。对应的方法也一应俱全,相信老前辈们已经将它完善的很好了。况且如今IDE这么强大(我目前都是用sublime写java,命令行编译,更别说别的IDE了),再加上网络。用多了自然就熟悉啦。下面举个小例子介绍一下。

    1.1字符串

先看java代码:

//Prompt.javaclass Prompt {
	static {
		System.loadLibrary("Prompt");//提示加载名字为Prompt.dll库
	}

	private native String getLine(String prompt);//声明native方法

	public static void main(String argv[]) {
		Prompt p = new Prompt();
		String input = p.getLine("Type a line:");//字符串作为参数
		System.out.println("User typed:" + input);
	}
}

  

//Prompt.c#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <jni.h>
#include "Prompt.h"

JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
	char buf[128];
	const jbyte *str;
	//将字符串转换成UTF-8
	str = (*env)->GetStringUTFChars(env, prompt, NULL);
	if (str == NULL)
	{
		return NULL;
	}
	printf("%s", str);
	//释放转换时候的内存
	(*env)->ReleaseStringUTFChars(env, prompt, str);

	scanf("%s", buf);
	//构建一个新的字符串返回
	return (*env)->NewStringUTF(env, buf);
}

  如果直接打印printf("%s", prompt);jstring和char*是不同类型。需要转换成C能识别的char*,JNI支持Unicode/UTF-8字符编码互转的。这里的 几个方法要记住,字符串操作很普遍哦。这里看记得调用ReleaseStringUTFChars释放GetStringUTFChars中分配的内存。其中“str = (*env)->GetStringUTFChars(env, prompt, NULL);”中第三个参数原型是jboolean *。为JNI_TRUE会返回一个拷贝,不关心的时一般设置为NULL。因此这里不确定是否会分配内存。由于GC(垃圾回收机制)的原因,这里可以理解是回收内存,或者回收了一个内存占用标记。如果是后者,那么string的内存呢暂时未被及时回收,并且又有大量的操作,这样就很有可能导致程序崩溃,这是不安全的,所以要及时释放内存。这里也体现了一点,JNI让JAVA丢失了一部分安全性,需要程序员自己去考虑了。

    1.2数组

    这里代码就不贴了,上一篇文章中有一段关于二维数组的代码。

    数组是由基本数据类型构成的集合,先调用FindClass获得一个一维某类型数组。可以调用NewObjectArray分配一个对象数组。也是Stirng的操作方式。

    数组类型就不再是基本数据类型的传递了,它大致和下面要说的类成员和方法的访问方式类似。这里不多说了。

  2:类的成员和方法的访问

    2.1成员的访问方式

      有静态成员和实例成员两种访问方式。其实过程都一样,这里贴出静态成员访问方法,实例成员只要把实现方法中的static去掉就可以了,其实这都是VS会自动提示你的。只管放心大胆的用。

//访问类的静态成员
JNIEXPORT void JNICALL Java_StaticField_accessField
(JNIEnv *env, jobject obj)
{
	jclass jcls;
	jint num;
	jfieldID jfid;

	//得到jclass
	jcls = (*env)->GetObjectClass(env, obj);      //GetObjectClass和FindClass一样,都能得到jclass。前者需要jni传入的一个引用,后者需要完整类名
	if (NULL == jcls)
	{
		return;
	}

	//得到jfield
	jfid = (*env)->GetStaticFieldID(env, jcls, "num", "I");//和取得实例成员ID的不同的是加了static
	if (NULL == jfid)
	{
		return;
	}

	//得到成员值
	num = (*env)->GetStaticIntField(env, jcls, jfid);
	printf("in C:\n");
	printf("StaticField.num = %d\n", num);

	//构建并修改值
	(*env)->SetStaticIntField(env, jcls, jfid, 9999);
}

    2.2类方法的访问方式

  

JNIEXPORT void JNICALL Java_StaticMethod_StaticMethod
(JNIEnv *env, jobject obj)
{
	jclass jcls;
	jmethodID jmid;

	jcls = (*env)->GetObjectClass(env, obj);
	if (NULL == jcls)
	{
		return;
	}

	jmid = (*env)->GetStaticMethodID(env,jcls,"callback","()V");
	if (NULL == jmid)
	{
		return;
	}

	//改进方法
	static jmethodID jmid_s;
	if (NULL == jmid_s)
	{
		jmid_s = (*env)->GetStaticMethodID(env, jcls, "callback", "()V");
		if (NULL == jmid_s)
		{
			return;
		}
	}
		//再将下边的jmid改成jmid_s,这样便可以实现加载一次之后,知道被卸载都不再对名称和符号表进行查询操作
		//可以将初始化的field和method一起缓存。这样再次装载就会更新值。

		//改进二:可以首次加载的时候将一个类的所有field和method的信息加载进Hash表。
		//以后的操作只要最这个表进行操作就OK了。用名字和类型做键值

	printf("In C call the Java instance method:\n-------------\n");
	(*env)->CallStaticVoidMethod(env, jcls, jmid);
}

    2.3 访问优化方式

     见变代码中的注释。同样可以类推到其他方式总

  3:关于全局引用和局部引用

  JNI支持3种引用,全局引用,局部引用,弱全局引用。

  全局引用:和全部变量差不多,和程序共存亡,必须通过NewGlobalRef由程序员主动创建。

  局部引用:每创建一个实例就会返回一个指向这个实例的局部引用,只在本线程中native方法中有效。当native 返回时会自动释放。

  弱全局引用 特性与GlobalRef相同,但该类型的引用不保证不被自动回收

时间: 2024-08-29 03:14:58

【再探JNI__2】的相关文章

再探Linux动态链接 -- 关于动态库的基础知识

  在近一段时间里,由于多次参与相关专业软件Linux运行环境建设,深感有必要将这些知识理一理,供往后参考. 编译时和运行时 纵观程序编译整个过程,细分可分为编译(Compiling,指的是语言到平台相关目标文件这一层次)和链接(Linking,指目标文件到最终形成可执行文件这一层次),这个总的过程可称为编译时:就动态链接而言,还存在一个运行时,即程序在被操作系统加载的过程中,系统将该程序需要的动态库加载至内存到程序开始运行的这一段过程.明确这两个过程在一般linux开发中的地位,以及了解每个"

再探css3

再探css3 我们知道,这几年来智能手机的高速发展使得人们使用移动端上网的时间和人数已经超过了PC端.例如在2015年,就中国电商而言,各电商平台在移动端持续发力,移动端购物占比不断攀升,双11期间,天猫交易额突破912亿元,其中移动端交易额占比68%,京东移动端下单量占比达到74%,其余各大电商平台移动端的支付比例也在60%-80%之间.即移动端在2015年超越PC端,成为网购市场的主流选择.这也使得网页设计师需要更加注重移动端的网页制作,而移动端对于HMTL5和CSS3目前已经支持的非常好了

【再探JNI】

上一次了解了一点JNI,然后不甘心的找到了JNI的官方文档.(官方文档绝对是一个最好的学习资料),百度找出来的一些资料大多数是比较零零碎碎的,不具有系统学习的可能,对于我这样的初学者,先全面的了解一个技术比往一个死角里钻研要好很多.并且百度出来的部分资料估计就是跟我这样的半吊子水平还不到的人的一些心得体会呢.因此,个人建议是看官方文档去全面了解一项技术,然后不理解的地方去再去搜集资料.加上自己的理解和实践,这样会进步的快一点. 好了,闲话少说.进入今天的真题.<再探JNI> (1)什么时候用J

再探 butterfly.js - grunt.js篇(一)

再探 butterfly.js - grunt.js篇(一) 神器 grunt.js 久仰grunt.js的大名,学习grunt.js一直是我todo List的第一位.趁着新春佳节来临之际(打酱油的日子),就来填了这个坑,完了这个心愿. grunt.js的强大,强大在于它拥有很多用途丰富的插件,和不同插件之间的联动实现更牛逼的功能. 这里默认大家已经安装了npm和会用npm install等指令,就不详细讲了.下面讲用到grunt-contrib-watch和grunt-contrib-con

【足迹C++primer】33、再探迭代器

再探迭代器 这里有插入迭代器,有流迭代器,反向迭代器,移动迭代器. 插入迭代器 这是一种迭代器适配器,接受一个容器,生成一个迭代器,实现向给定容器添加元素. 插入迭代器有三种类型,差异在于元素插入的位置 back_inserter创建一个使用push_back的迭代器. front_inserter创建一个使用push_front的迭代器. inserter创建一个使用insert的迭代器. void fun1() { list<int> lst={1,2,3,4}; list<int&

再探jQuery

再探jQuery jQuery是一个JavaScript库,它极大的简化了JavaScript编程,虽然目前网络上有大量开源的JS框架,但是jQuery是目前最流行的JS框架,而且提供了大量的扩展.包括Google.Microsoft.IBM等大公司都在使用jQuery框架,值得注意的是,jQuery团体知道JS咋不同浏览器中存在这大量的兼容性问题,所以jQuery兼容所有主流浏览器,包括Internet Explorer 6! 第一部分:使用方法 那么如何使用jQuery呢?很简单,只需要在j

再探oVirt-配置一个2节点的ovirt环境

日期:2015/11/4 - 2015/11/5 主机:engine, node01, node02 目的:再探oVirt-配置一个2节点的ovirt环境 操作内容: 一.基础操作 1.资源 ovirt engine: engine ovirt node: node01, node02 2.hosts 10.50.200.141 engine.ovirt 10.50.200.101 n101.ovirt 10.50.200.102 n102.ovirt 3.防火墙放行同一个局域网内的访问限制 后

[老老实实学WCF] 第五篇 再探通信--ClientBase

原文:[老老实实学WCF] 第五篇 再探通信--ClientBase 老老实实学WCF 第五篇 再探通信--ClientBase 在上一篇中,我们抛开了服务引用和元数据交换,在客户端中手动添加了元数据代码,并利用通道工厂ChannelFactory<>类创建了通道,实现了和服务端的通信.然而,与服务端通信的编程模型不只一种,今天我们来学习利用另外一个服务类ClientBase<>来完成同样的工作,了解了这个类的使用方法,我们对服务引用中的关键部分就能够理解了. ClientBase

再探 butterfly.js - 奇异的留白

再探 butterfly.js - 奇异的留白 事情经过 在 梓凡兄 捣鼓他的 豆瓣FM 播放器的时候,发现了butterfly.js会在ipad的横屏模式(landscape mode)的时候对<html>添加class="ipad ios7".更加离奇的是在butterfly.css有以下样式: @media (orientation:landscape){ html.ipad.ios7 > body{ position:fixed;bottom:0;width: