当前位置: 代码迷 >> 综合 >> 在安卓开发中把Image的ByteBuffer数据通过虚拟内存工具进行复制,以节约实际内存的做法
  详细解决方案

在安卓开发中把Image的ByteBuffer数据通过虚拟内存工具进行复制,以节约实际内存的做法

热度:59   发布时间:2024-02-05 17:09:32.0

Camera API回传的Image信息,如果不是立即使用,则在JVM中长时间保存的话会大量占用JVM内存,增加OOM风险。而我通过阅读ByteBuffer和Buffer等源码发现,如果使用Direct方式保存数据,则Buffer类中的long address变量保存的是native内存块的起始地址,于是我们可以通过这个地址把文件通过MMAP工具或者文件读写函数放到外存先。

关于MMAP工具的编写可以先看看:

《安卓应用开发中通过JNI使用虚拟内存节约物理内存的一个例子》

《一种基于linux mmap特性的应用层虚拟内存工具的编写》

取数据过程的例子:

public Object getField(Object desObj, String fieldName) throws NoSuchFieldException {if (desObj == null || fieldName == null) {throw new IllegalArgumentException("parameter can not be null!");}Class<?> desClass = desObj.getClass();return getFieldStepwise(desObj, desClass, fieldName);
}private long getByteArrayInVirtualMem(Image yuvImage, int width, int height, int stride, long vMemAdd) {ByteBuffer yBuf = yuvImage.getPlanes()[0].getBuffer();ByteBuffer vuBuf = yuvImage.getPlanes()[2].getBuffer();if (yBuf.isDirect() && vuBuf.isDirect()) {Log.i("cjztest", "getByteArrayInVirtualMem进入内部文件拷贝");try {//long vMemAdd = VirtualMemoryUtil.createVirtualMemory(filePath, vuBuf.remaining() + yBuf.remaining() + 10 * 1024);long yBufferAddress = (long) getField(yBuf, "address");long vuBufferAddress = (long) getField(vuBuf, "address");int totalSize = yBuf.remaining() + vuBuf.remaining();long yuvBufAddress = VirtualMemoryUtil.virtualMemoryMalloc(vMemAdd, totalSize);VirtualMemoryUtil.copyNativeDataBlockToVMem(yuvBufAddress, yBufferAddress, yBuf.remaining());VirtualMemoryUtil.copyNativeDataBlockToVMem(yuvBufAddress + yBuf.remaining(), vuBufferAddress, vuBuf.remaining());yuvImage.close();return yuvBufAddress;} catch (NoSuchFieldException e) {e.printStackTrace();return -1;}} else {return -1;}return -1;
}

整个把image数据持久化的过程中除了image数据本身,没有增加一点JVM内存占用。

通过这样的方法,可以把Image中的数据快速转移到外存的一个文件中,并得到外存地址。在需要使用的时候可以通过我的工具类中的方法

VirtualMemoryUtil.copyVMemDataToByteArray

即可通过传入地址把数据重新从外存中写入到byte数组中,更能直接在native中操纵该地址的数据实现不占物理内存的image数据操作,例如加水印等图像处理,但受限于外存速度远远小于内存,请不要在时间要求高的场合下使用该技巧。

  相关解决方案