Android上的C/C++调用Java问题(转载)

Android上的C/C++调用Java问题

http://www.h3-embedded.com/thread-262-1-1.html


基本上来说,在androidC/C++调用Java,和在其他平台上调用是一致的。网上一收,一大箩筐。我在这里没什么好多说明的。这里,我想谈一下,在android上,多线程C/C++调用Java要遵守的规则。
最近,我在android上使用Curl库。网络通讯,我想没有同学会想使用single thread去等待网络另一端,而hung住用户的操作吧。所以多线程不失为一个方法。同时,我想让user知道当前网络通讯的状态,比如下载速度,那么自然而然会想到用回调函数去update这些信息。那么问题来了,在Android上,典型的一个应用程序,首先由上层Java code开启Thread开始一个下载,那么这个Thread就会调用JNI层中真正下载的函数,姑且叫做downloadFuncdownloadFunc 使用Curl to download, 同时会注册回调函数叫做downloadProgress。那么这个回调函数就是运行在另一个线程中的。Okay,我们先来看一段:
8.1.4 Obtaining a JNIEnv Pointer in Arbitrary Contexts

We explained earlier that a JNIEnv pointer is only valid in its associated thread. This is generally not a problem for native methods because they receive the JNIEnv pointer from the virtualmachine as the first argument. Occasionally, however, it may be necessary for a piece of native code not called directly from the virtual machine to obtain the JNIEnv interface pointer thatbelongs to the current thread. For example, the piece of native code may belong to a "callback" function called by the operating system, in which case the JNIEnv pointer will probably notbe available as an argument.

You can obtain the JNIEnv pointer for the current thread by calling the AttachCurrentThread function of the invocation interface:


JavaVM *jvm/* already set */

f()
{
     JNIEnv *env;
     (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
     ... /* use env */
}
When the current thread is already attached to the virtual machine, Attach-Current-Thread returns the JNIEnv interface pointer that belongs to the current thread.

There are many ways to obtain the JavaVM pointer: by recording it when the virtual machine is created, by querying for the created virtual machines using JNI_GetCreatedJavaVMs, bycalling the JNI function GetJavaVM inside a regular native method, or by defining a JNI_OnLoad handler. Unlike the JNIEnv pointer, the JavaVM pointer remains valid across multiple threadsso it can be cached in a global variable.



//-----------------------------------------------------------------------分割线--------------------------------------------------------------------------------------
  大概的用中文说明一下:通常Java call JNI中的函数,那么这个函数的第一参数:是JNIEnv *指针;所以假如我们要使用JNIEnv* 指针,那么我们直接使用就好了。但是,有些情况下,比如系统的回调函数,就不会有JNIEnv *指针作为第一个传入参数。这个时候,我们能使用  (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL)来创建一个在回调函数中的JNIEnv*指针。而jvm是进程内的共享对象,所以,只要我们在JNI_OnLoad中保存jvm对象就好了。那么我们就可以跨线程使用JNIEnv *指针了。



此外,我使用curl progress callback function的时候,出现了如下错误:
01 W/dalvikvm(16890): ReferenceTable overflow (max=512)
02 W/dalvikvm(16890): Last 10 entries in JNI local reference table:
03 W/dalvikvm(16890):   502: 0x44a9e110 cls=Ljava/lang/String; (28 bytes)
04 W/dalvikvm(16890):   503: 0x44a9e908 cls=Ljava/lang/String; (28 bytes)
05 W/dalvikvm(16890):   504: 0x44a9e908 cls=Ljava/lang/String; (28 bytes)
06 W/dalvikvm(16890):   505: 0x44a9f358 cls=Ljava/lang/String; (28 bytes)
07 W/dalvikvm(16890):   506: 0x44a9f358 cls=Ljava/lang/String; (28 bytes)
08 W/dalvikvm(16890):   507: 0x44a9fca0 cls=Ljava/lang/String; (36 bytes)
09 W/dalvikvm(16890):   508: 0x44a9fca0 cls=Ljava/lang/String; (36 bytes)
10 W/dalvikvm(16890):   509: 0x44b94180 cls=Ljava/lang/String; (28 bytes)
11 W/dalvikvm(16890):   510: 0x44b94180 cls=Ljava/lang/String; (28 bytes)
12 W/dalvikvm(16890):   511: 0x449a6768 cls=Ljava/lang/String; (28 bytes)
13 W/dalvikvm(16890):   JNI local reference table summary (512 entries):
14 W/dalvikvm(16890):   1 of Ljava/lang/Class164B
15 W/dalvikvm(16890):   507 of Ljava/lang/String28B (432 unique)
16 W/dalvikvm(16890):   3 of Ljava/lang/String36B (2 unique)
17 W/dalvikvm(16890):   Memory held directly by tracked refs is 12344 bytes
18 E/dalvikvm(16890):    Failed adding to JNI local ref table (has 512 entries)
19 I/dalvikvm(16890):    "DownloadThread" prio=5 tid=7 RUNNABLE
20 I/dalvikvm(16890):   | group="main" sCount=0 dsCount=0 s=obj=0x449ff6a0
21 self=0x26f4d0
22 I/dalvikvm(16890):   | sysTid=16898 nice=0 sched=0/0 cgrp=default
23 handle=2543832



  网上一搜,发现也有老兄碰到过类似的情况。说是资源没有释放。资源?我几乎没有申请什么资源啊。只是传回去一个jstringjint。难道?JNIEnv->NewUTFString()中的资源要释放?结果我用google了一把,发现不用我们来手动去release。由JavaVM来管理。这个回调函数大概每秒调用一次,难道JavaVM没时间去释放?看起来,这个回调函数被调用的太频繁了,所以ReferenceTable overflow。这个table好想只能容纳512slot.超过512,就不能 Failed adding to JNI local ref table. 想想reference table是在heap上,我干脆只传jintJava layer好啦。那么jint这个是在stack上的,函数一旦执行完成的时候,stack就会自动被释放。果然,修改后,就不再出现这个错误了。

你可能感兴趣的:(java,jvm,thread,多线程,android,jni)