[Android的系统移植与平台开发]JNI介绍(7)

2014-06-06
浏览
导读:2)手动释放局部引用情况 虽然局部引用会在本地代码执行之后自动释放,但是有下列情况时,要手动释放: l 本地代码访问一个很大的Java对象时,在使用完该对象后,本地代码要去执行比较复杂耗时的运算时,由于本地代

2)        手动释放局部引用情况

虽然局部引用会在本地代码执行之后自动释放,但是有下列情况时,要手动释放:

l  本地代码访问一个很大的Java对象时,在使用完该对象后,本地代码要去执行比较复杂耗时的运算时,由于本地代码还没有返回,Java收集器无法释放该本地引用的对象,这时,应该手动释放掉该引用对象。

/* A native method implementation */

JNIEXPORT void JNICALL

func(JNIEnv *env, jobject this)

{

    lref =...              /* a large Java object*/

    ...                     /* last use of lref */

   (*env)->DeleteLocalRef(env, lref);

   lengthyComputation();   /* maytake some time */

    return;                 /* all local refs are freed */

}

这个情形的实质,就是允许程序在native方法执行期间,java的垃圾回收机制有机会回收native代码不在访问的对象。

l  本地代码创建了大量局部引用,这可能会导致JNI局部引用表溢出,此时有必要及时地删除那些不再被使用的局部引用。比如:在本地代码里创建一个很大的对象数组。

for (i = 0; i < len; i++) {

    jstring jstr= (*env)->GetObjectArrayElement(env, arr, i);

    ... /*process jstr */

   (*env)->DeleteLocalRef(env, jstr);

}

在上述循环中,每次都有可能创建一个巨大的字符串数组。在每个迭代之后,native代码需要显示地释放指向字符串元素的局部引用。

l  创建的工具函数,它会被未知的代码调用,在工具函数里使用完的引用要及时释放。

l  不返回的本地函数。例如,一个可能进入无限事件分发的循环中的方法。此时在循环中释放局部引用,是至关重要的,这样才能不会无限期地累积,进而导致内存泄露。

局部引用只在创建它们的线程里有效,本地代码不能将局部引用在多线程间传递。一个线程想要调用另一个线程创建的局部引用是不被允许的。将一个局部引用保存到全局变量中,然后在其它线程中使用它,这是一种错误的编程。

3)        全局引用

在一个本地方法被多次调用时,可以使用一个全局引用跨越它们。一个全局引用可以跨越多个线程,并且在被程序员手动释放之前,一直有效。和局部引用一样,全局引用保证了所引用的对象不会被垃圾回收。

JNI允许程序员通过局部引用来创建全局引用, 全局引用只能由NewGlobalRef函数创建。下面是一个使用全局引用例子:

jstring

MyNewString(JNIEnv *env, jchar *chars, jint len)

{

    static jclassstringClass = NULL;

    ...

    if(stringClass == NULL) {

        jclasslocalRefCls =

           (*env)->FindClass(env, "java/lang/String");

        if(localRefCls == NULL) {

           return NULL;

        }

        /* Createa global reference */

       stringClass = (*env)->NewGlobalRef(env, localRefCls);

        /* Thelocal reference is no longer useful */

       (*env)->DeleteLocalRef(env, localRefCls);

        /* Is theglobal reference created successfully? */

        if(stringClass == NULL) {

           return NULL; /* out of memory exception thrown */

        }

    }

    ...

}

 

4)        释放全局引用

在native代码不再需要访问一个全局引用的时候,应该调用DeleteGlobalRef来释放它。如果调用这个函数失败,Java VM将不会回收对应的对象。

 

1.8 本地C代码中创建Java对象及本地JNI对象的保存

1)        Android中Bitmap对象的创建

通常在JVM里创建Java的对象就是创建Java类的实例,再调用Java类的构造方法。而有时Java的对象需要在本地代码里创建。以Android中的Bitmap的构建为例,Bitmap中并没有Java对象创建的代码及外部能访问的构造方法,所以它的实例化是在JNI的c中实现的。

BitmapFactory.java中提供了得到Bitmap的方法,时序简化为:

BitmapFactory.java->BitmapFactory.cpp -> GraphicsJNI::createBitmap()  [graphics.cpp]

GraphicsJNI::createBitmap()[graphics.cpp]的实现:

jobjectGraphicsJNI::createBitmap(JNIEnv* env, SkBitmap*bitmap, bool isMutable,

                                  jbyteArrayninepatch, intdensity)

{

   SkASSERT(bitmap != NULL);

    SkASSERT(NULL!= bitmap->pixelRef());

 

    jobject obj=env->AllocObject(gBitmap_class);

    if (obj) {

       env->CallVoidMethod(obj,gBitmap_constructorMethodID,

                           (jint)bitmap,isMutable, ninepatch, density);

        if(hasException(env)) {

            obj =NULL;

        }

    }

    return obj;

}

而gBitmap_class的得到是通过:

jclass c=env->FindClass("android/graphics/Bitmap");

gBitmap_class =(jclass)env->NewGlobalRef(c);

//gBitmap_constructorMethodID是Bitmap的构造方法(方法名用”<init>”)的jmethodID:

gBitmap_constructorMethodID=env->GetMethodID(gBitmap_class, "<init>",  "(IZ[BI)V");

总结一下,c中如何访问Java对象的属性:

1)        通过JNIEnv::FindClass()找到对应的jclass;

2)        通过JNIEnv::GetMethodID()找到类的构造方法的jfieldID;

3)        通过JNIEnv::AllocObject创建该类的对象;

4)        通过JNIEnv::CallVoidMethod()调用Java对象的构造方法。

 

编程实现Android远程控制PC

[Android的系统移植与平台开发]Sensor HAL

[Android的系统移植与平台开发]Sensor HAL

用ViewPager实现高仿图片左右滑动自动切换

详解android Content Provider[6]