首页 > 代码库 > Jni如何传递并且修改两个基础参数

Jni如何传递并且修改两个基础参数

最近在开发jni时,需要返回多个参数给java。这个过程中,碰到了一些问题,值得探讨一下。

 
具体是这样,jni方法jni_do_something作了底层处理后,得出两个int数据,需要将他们的值传递给java。在C语言中,直接用指针就可以了。Java中可以传递两个Integer的引用。用JNI怎么实现呢?
我在android frameworks源代码中看了一下,对于类似传值需求,android都是在java层自定义了一个class,用来封装各个需要传递的参数。jni中需要修改时,获得该class的成员id,然后用SetIntField来修改。
 
我不想这么做,因为类似的native方法比较多,我总不能每次都定义结构体吧,而且将不同方法的参数封装在一个class中,也不太对,因为它们没有共同意义。
为了让jni能修改,Java层毫无疑问需要传入Integer类型参数,这样jni才认为它是一个jobject,才可以修改。好的,问题出现了。jni方法实现:
jni_do_something(JNIEnv *env, jobject thiz, jobject p1, jobject p2) {    jclass c;    jfieldID id;    c = env->FindClass("java/lang/Integer");    if (c==NULL)    {        LOGD("FindClass failed");        return -1;    }    id = env->GetFieldID(c, "value", "I");    if (id==NULL)    {        LOGD("GetFiledID failed");        return -1;    }    env->SetIntField(p1, id, 5);    env->SetIntField(p2, id, 10);    return 0;}

java层调用如果这样写:

native int do_something(Integer p1, Integer p2);Integer p1=0, p2=0;do_something(p1, p2);Log.d("test", "p1: "+p1);Log.d("test", "p2: "+p2);

这样打印出的值是(10,10),而不是期望的(5,10)。为什么呢?

我在stackoverflow上发了一个贴,大家众说纷纭。有的说跟mutable/imutable object有关,有的说跟autoboxing有关。
我再次做了试验。如果写成:
Integer p1=0, p2=1;
或者:
Integer p1 = new Integer(0);Integer p2 = new Integer(0);
则打印的是预期结果。
 
原来,这跟autoboxing有关。当你用Integer p1 = 0这种方式时,java使用autoboxing机制将0封装在一个Integer对象中,这时使用了Integer类的valueOf方法。在java 语言中,有一个很诡异的现象,对于在-128~127间的小数字,会在static pool中返回一个静态对象,在这个范围外的,会new一个Integer。
 
可以参考这个链接:http://stackoverflow.com/questions/1995113/strangest-language-feature
574: Fun with auto boxing and the integer cache in Java
/** * Returns a <tt>Integer</tt> instance representing the specified * <tt>int</tt> value. * If a new <tt>Integer</tt> instance is not required, this method * should generally be used in preference to the constructor * {@link #Integer(int)}, as this method is likely to yield * significantly better space and time performance by caching * frequently requested values. * * @param  i an <code>int</code> value. * @return a <tt>Integer</tt> instance representing <tt>i</tt>. * @since  1.5 */public static Integer valueOf(int i) {    if (i >= -128 && i <= IntegerCache.high)        return IntegerCache.cache[i + 128];    else        return new Integer(i);}

回到程序中来,如果写成Integer p0 = 0, p1 = 0,它们是static pool中同一个对象的引用,因此jni中修改的是同一个对象。正确做法应该是使用new。

 

Jni如何传递并且修改两个基础参数