JNI开发中那些坑爹的事


前些日子,有机会接触到一个JNI相关的项目,做项目时遇到一些坑爹的问题,在这里记录下来,供有需要的朋友参考。这些问题之所以坑爹,一方面是因为网上的资料比较少,有些问题说的不是很明白,另一方面也和开发环境运行环境等相关,毕竟是Native语言。

这里假设读者已经初步的了解了JNI是什么,并且可以写出一些JNI的代码。

本文提到的开发环境是:

Jdk6.0+

Windows 2003 +

VS2008 +

一、关于加载、System.loadLibrary

Javanative方法对应着一个由C/C++写的动态链接库,在调用native方法前需要先加载这个动态链接库,这里要注意,dll动态链接库的位置,一定要在 java.library.path 路径下。别问我这个路径是什么,不知道的把它打印出来,选个合适的路径放进去,当然还要把动态链接库所依赖的相关资源也放进去。我比较习惯放在  jre/lib/i386 目录下,这个纯粹是个人习惯。

路径放对了,那就要加载,我习惯在有native调用的这个类里面写一个static块代码进行加载,也就这么一句 System.loadLibrary("XXX");这里要说一下,这个XXX是不要写后缀名的,写了就报错。我估计是Java的设计者们为了跨平台考虑的,毕竟linux环境下是so文件甚至没有后缀名,而在windows环境下就不一样,写java代码时把所有的动态库都以同样的名称命名,这样子在不同平台下系统会自动选择合适的链接文件。

二、关于调试

习惯了在一个语言环境下开发的筒子们突然进入跨语言层开发,估计最不适应的就是代码的调试问题了,程序崩溃了,是Java代码的原因还是C++代码的原因?或者是环境的问题?这些问题的确很蛋疼。而且跨语言调试的确没有啥好的代码调试工具,我只好用最古老的办法,一句一句的打印到输出上来跟踪代码到底运行到什么地方,确定出问题的语句。其实这个过程并不复杂,但是要耐得住烦,不怕烦最后一定可以调出来的。

Android环境下JNI开发好像有一套很好的工具,做Android的同学不妨去找找看。

三、jclass变量

我是真没理解这个变量具体是怎么一回事,按理说jclass 就是描述一个类的定义或者类似的事情,但是在JNI中,有俩种jclass变量,一种是通过 env->FindClass 获得的,而另一种是通过env->GetObjectClass( jobject )获得的。当时这个问题困扰了我很长时间,后来总结出了,如果你想调用某个类中的static方法,那么你就用第一种方式获取这个jclass变量。而如果你是想改变某个object对象中某个属性的值(注意是object)这个时候set方法会让你传入一个jclass值,这时候你就可以通过第二种方式获取这个jclass变量了。

四、在不同的线程里回调Java方法

通常做JNI开发的时候,很有可能会用到观察者模式或者类似的设计,这个时候就要求我们要使用C++代码反过来调用Java的代码。而且绝大多数情况,都是在异步线程中间调用的,如Windows消息处理函数等。

网上很多人的博客都是这么建议的: 加载动态链接库 %JAVA_HOME%/jre/client/jvm.dll 然后调用这个库的很多代码什么的。这里我要说一下,这个方法是一个C++应用程序去调用一个Java库或者class的方法,这种方法下,宿主语言是C++,而我们开发JNI程序,宿主语言任然是Java

解决方法如下:

虽然JNIEnv* 变量的生存周期是无法跨线程的,但是 JavaVM* 变量是可以在多个线程中公用的。我们可以通过

extern JavaVM *gs_jvm ;

env->GetJavaVM(&gs_jvm) ;

创建一个全局的JavaVM 实例,然后在新的线程中调用:

jvm->AttachCurrentThread( (void**)&env , NULL  ) ;

就可以获得一个JNI上下文环境

这样就可以在不同的线程去操控jobject了。这里要提一下,jobject对象也并非全局了,不过你可以通过env->NewGlobalRef( jobject ) 方法获得一个全局的jobject实例。

既然说到观察者模式,这里我给大家一个建议,使用JNI的时候,不要把太多的逻辑放到C++代码中去处理,观察者模式使用的时候可以考虑变通一下,把回调方法设置成一个静态的Java方法,观察者队列的维护和消息分发采用Java代码来处理可以减少很多麻烦。

五、native方法阻塞无法返回。

我曾经遇到一个很诡异的情况,一个native方法调用迟迟无法结束,我以为是我写的C++代码发生了阻塞,于是跟踪了一遍,结果发现没有,我的C++函数是已经return了的,但是我的Java native方法就是在阻塞中。当时这个问题完全超出了我的认知范围,以为大白天的撞见鬼了,吓了一身冷汗。

如果筒子们也遇到这种情况,不要慌张,其实native方法调用,调用的并非只有我们所写的C++代码,调用之前会做一些初始化工作,调用结束后会做一些扫尾工作,这里代码无缘无故的阻塞就是在这扫尾工作中出现了问题。具体什么问题,我也说不上,有可能是内存问题,也有可能是存在未处理的异常等等。最好的方法就是在C++方法结束之前加上这么一句代码

if( env->ExceptionOccurred() )

{

env->ExceptionDescribe() ;

}

看看是否有异常,一般看看虚拟机对异常的描述大概也就清楚问题出现在哪里了。



好了,就是这么多。卖个萌,喵 ~~~~~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值