我目前正在制作修改图像某些字节的Android应用程序。为此,我写了这段代码:
Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(path));
ByteBuffer buffer = ByteBuffer.allocate(bmp.getWidth()*bmp.getHeight());
bmp.copyPixelsToBuffer(buffer);
return buffer.array();
问题是这种方式使用过多的堆内存,并抛出OutOfMemoryException
。
我知道我可以让App的堆内存更大,但它似乎不是一个好的设计选择。
是否有更加内存友好的方式来更改图像的字节?
答案 0 :(得分:1)
看起来托管堆上有两个像素数据副本:
Bitmap
ByteBuffer
将数据保留在位图中并使用getPixel()
/ setPixel()
(或者可能使用“批量”变体一次编辑一行),内存要求可以减半,但这会增加一些开销。
根据图像的性质,您可以使用不太精确的格式(例如RGB 565而不是8888),将内存要求减半。
如其中一条评论中所述,您可以将数据解压缩到文件中,使用java.nio.channels.FileChannel#map()
对其进行内存映射,然后通过MappedByteBuffer
访问它。这会给加载和保存带来相当大的开销,并且可能很烦人,因为您必须使用ByteBuffer
而不是byte[]
。
另一个选择是使用android:largeHeap
(documented here)扩展堆,尽管在某些方面你只是推迟了不可避免的:你可能会被要求编辑一个太大而不能用于“大“堆。此外,“大”堆的容量因设备而异,就像“正常大小”堆一样。这是否有意义部分取决于您加载的图像有多大。
在你做任何这些之前,我建议你使用堆分析工具(参见例如this blog post)来查看你的记忆力。另外,查看内存不足异常之上的logcat;它应该确定失败的分配大小。确保它看起来“合理”,即你不会无意中分配比你想象的更多的东西。