• 2010-12-06

    [Code] Android备忘2 - JNI相关问题 - [Code]

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/sgzxy-logs/87155058.html

    问题1:NDK的函数调用时出现 “UnsatisfiedLinkError : 函数名” 这样的异常

    解决办法:

    网上能找到的提醒无一不是让你去仔细检查NDK中的函数命名是否正确,也就是“JAVA_调用该函数的JAVA类名(完整路径区分大小写)_函数名”这样的JNI格式。然而我在确定这个命名准确无误后仍得到这个异常。

    为什么呢?反复捣鼓后幡然醒悟:我用的是C++(文件后缀用.cpp,按C++来编译)。

    由于JNI是采用了C风格的函数命名,所以如果用C++编译,则必须记得在每个函数前加上extern "C"的标记。这个东西记得NDK的docs里面有提到(现在找不到在哪了,那些docs只能自己一份份地看),实际写的时候还是忘了。

     

    问题2:进行jfieldID和jmethodID的cache时,不能转化为global reference

    解答:local/global reference这些概念,只是跟jobject的指针相关(包括它的派生类如jclass),因为这些指针所指空间,如果为local的话,则会在作用域结束时被回收,所以做cache时才需要转化为global reference。

    而jfieldID和jmethodID查看头文件便可知与jobject无关,只是一些native的空间分配,因此与local/global这些JNI加入的新概念无关,按C/C++的做法去cache即可。

     

    问题3:为何调用GetFieldID时,传入参数如果为类则必须按照这种格式:"Ljava/lang/String;",而调用FindClass时则直接“"java/lang/String"即可?

    解答:因为GetFieldID函数要解析类型签名,这些签名既包括int、bool这些原生类,也包括String或者自定义的JAVA类,因此需要对String这些需要指定类路径的设计一个额外的格式,以作为区分(估计可以提高解析签名的效率)。

    而FindClass函数不针对int、bool那些原生类(这些在C++中不算class),因此无需要加入这种格式上的区分。

     

    问题4:能够创建的local ref的数量是有限的吗?如果要创建一个比较大的jobjectArray并返回怎么办?

    解答:local ref的数量的确是有限的,这个限制大概比512小一点点(JNI背后是用一张表来记录你创建的每个local ref,这个表有长度限制),意味着你调用一个native函数时不能创建超过这个数量的local ref。

    于是你试图访问/返回一个超过该限制长度的jobjectArray时就很郁闷了,不过这是因为你对Get/SetObjectArrayElement这一对函数产生了一个误解。以SetObjectArrayElement为例,你传入一个local ref的参数,看上去是把这个local ref给放到了你的array中,实际上这个local ref并未放进去,放进去的只是ref指向的内容本身,所以这个函数执行后那个local ref就可以删除了,所以就不存在长度限制的问题。同样,GetObjectArrayElement每次会新产生一个local ref,所以你用完要立刻删掉,不然就可能超过local ref的数量限制。

    这个误解应该很普遍吧?其实Sun官方的JNI Spec文档中都有写,范例代码也都这么做了,不过还是强调得不够,第一次写的时候还是很容易被表面代码所误导。

     

    问题5:GetStringRegion和GetStringUTFRegion的特别提醒

    这两个函数都有一个len的参数,表示Unicode字符的个数,但是要注意,这两个函数会在copy完字符串后,还会操作数组的第len+1个元素(GetStringUTFRegion会把[len+1]赋值为0,但是GetStringRegion很奇怪,并不会把[len+1]赋为0)!因此分配空间时就要分配len+1,否则嘛。。你的程序随时crash(当你执行delete[]时)

    分享到: