包括用于Android应用程序的Java实现的C / C ++标头

时间:2011-01-20 16:02:13

标签: android arrays java-native-interface android-ndk

“我如何在Java中使用大型静态C或C ++数组?”

我正在使用Android NDK和JNI来获取一些使用Java工作的本机C(或C ++)代码。这很好,我的JNI生成的接口和我的C(或C ++)实现正在工作。

但是,我的应用程序需要一个非常大的数组。在C / C ++和最初的Objective-C中,这肯定是连续的,而在Java中显然不是这种情况。即使Java数组类似于C或C ++数组,我也无法通过引用从Java传递给我的C实现。我无法在我的实现中将Java数组复制到C或C ++数组,因为这会使内存成本翻倍。每次调用我的C或C ++实现时,我都无法从头开始创建它。

因此,我希望将此数组保留在Java标头和C或C ++标头之外,并在应用加载的那一刻始终在C或C ++世界中准备就绪。这是该应用程序的关键组件。

我该怎么办?

提前感谢你。

//////////////////////////////////

使用JNI的Get<PrimitiveType>ArrayElements是一个很长的解决方案,唯一的担心是内存分配。毕竟可能是复制了数组。

但是它在Android模拟器中运行并且结果令人印象深刻:在C中执行时,模拟器上的Java需要花费13秒才能在模拟器上执行。

由于Android模拟器相当慢,这可能是一个真实的数字,唯一可以确定的方法是在设备上进行测试。

1 个答案:

答案 0 :(得分:1)

查看此JNI Tips常见问题解答条目。它可能会回答您的问题,或者最不能让您了解可用的选项。

ADDENDUM :( SK9)(评论框太大,抱歉)JNI提示的相关部分如下。按照此处的指导,如果由于内存不足而复制数组(肯定会是),应用程序将崩溃。

///////////////////

原始数组

JNI提供了访问数组对象内容的函数。虽然一次只能访问一个条目对象数组,但可以直接读取和写入基元数组,就好像它们是用C语句声明一样。

为了使接口尽可能高效而不约束VM实现,GetArrayElements调用系列允许VM返回指向实际元素的指针,或者分配一些内存并进行复制。无论哪种方式,返回的原始指针都保证有效,直到发出相应的Release调用(这意味着,如果数据未被复制,则数组对象将被固定,并且不能作为压缩的一部分重新定位堆)。您必须释放您获得的每个阵列。此外,如果Get调用失败,则必须确保您的代码稍后不会尝试释放NULL指针。

您可以通过传入isCopy参数的非NULL指针来确定是否复制了数据。这很少有用。

Release调用采用一个mode参数,该参数可以包含三个值之一。 VM执行的操作取决于它是否返回指向实际数据的指针或其副本:

0 实际:数组对象未固定。 复制:复制数据。释放带有副本的缓冲区。 JNI_COMMIT 实际:什么都不做。 复制:复制数据。没有释放带有副本的缓冲区。 JNI_ABORT 实际:数组对象未固定。早期的写入不会中止。 复制:释放带有副本的缓冲区;对它的任何改变都会丢失。 检查isCopy标志的一个原因是知道在更改数组后是否需要使用JNI_COMMIT调用Release - 如果您在进行更改和执行使用数组内容的代码之间交替,则可以跳过无操作提交。检查标志的另一个可能原因是有效处理JNI_ABORT。例如,您可能希望获取一个数组,将其修改到位,将片段传递给其他函数,然后丢弃更改。如果您知道JNI正在为您制作新副本,则无需创建另一个“可编辑”副本。如果JNI通过你的原件,你需要自己制作副本。

有些人声称如果* isCopy为false,您可以跳过Release调用。不是这种情况。如果没有分配复制缓冲区,则必须固定原始内存,垃圾收集器不能移动它。

另请注意,JNI_COMMIT标志不会释放数组,您最终需要使用不同的标志再次调用Release。“