安卓系统:在没有OutOfMemoryError的情况下处理位图



首先是重要的代码行:

Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), getDrawableForLvl(drawable));
int []pixels = new int[bitmap.getWidth()*bitmap.getHeight()];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
//..
pixels = null; // please gc remove this huge data
System.gc();

所以我正在开发一款具有多个级别的Android应用程序(游戏)。每个级别都是我在级别开始时加载为位图的图像。然后我必须分析每个像素(第3行)。为此,我创建了一个数组(第2行)。图像为1280x800,因此阵列的大小超过一百万int。这里没有问题,因为它是一个局部变量,所以应该在方法返回时销毁它。但它是java,所以它不是-。-根据设备的不同,垃圾收集器的运行速度是否足够快。因此,当用户快速启动和关闭级别时,它会在第2行生成java.lang.OutOfMemoryError。我想是因为旧的数组还没有被删除,现在我有多个数组填充了内存。

我可以使数组成为静态成员。因此,它只创建一次,并且始终可用。然后不可能有多个实例。但我认为这是一种糟糕的编码风格,因为它在不需要的时候也可用(4MB)。

我不需要同时使用所有像素。我将图像分割成一百多个矩形,这样我就可以使用一个更小的阵列,并用像素一个接一个地填充它。但我在理解方法参数方面有问题,这里有人能帮我吗?还有一种方法可以在x,y位置只得到一个像素,但我想一百万次函数调用相当慢。

有人有更好的主意吗?在java中没有办法将对象从内存中挤出,是吗?


更新1:正如vzoha建议只得到第一季度:

int []pixels = new int[bitmap.getWidth()/2*bitmap.getHeight()/2];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth()/2, bitmap.getHeight()/2);

给出ArrayIndexOutOfBound。我认为函数调用只是获取第一季度的像素,但仍然希望完整大小的数组和其他字段保持不变。

Update2:我想我可以逐行完成(第一季度半行半行):

int []pixels = new int[bitmap.getWidth()/2*bitmap.getHeight()/2];
for(int row = 0; row < bitmap.getHeight()/2; ++row)
    bitmap.getPixels(pixels, bitmap.getWidth()/2, bitmap.getWidth(), 0, row, bitmap.getWidth()/2, 1);

但当我对20x10个零件这样做时,这并不比单独获得每个像素好多少。好吧,这要好得多,但该方法仍然应该能够通过一次调用做到这一点,不是吗?我只是不知道这个"步幅"参数:"行之间要跳过的项数(像素[])(必须是>=位图的宽度)。可以是负数。"当>=宽度时,它怎么会是负数?

像素大小并不能直接转化为图像在内存中所占的内存量。众所周知,安卓系统中的位图(在使用ART之前)很难大量使用,同时避免OOM异常,因此有一个专门介绍如何有效使用位图的页面。问题通常是实际上有足够的内存可用,但它已经变得碎片化,并且没有一个的单个连续块达到您需要的可用大小。

我的第一个建议(为了快速获胜)是用特定的配置解码位图,通过切换到使用ALPHA_8进行位图配置,您应该只能占用之前使用的内存量的1/4。这将使用每个像素1个字节,而不是4个(默认值为ARGB_8888)

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ALPHA_8
bitmap = BitmapFactory.decodeResource(getResources(), getDrawableForLvl(drawable), options);

我的下一个建议是先缩放位图,然后将合适的位图放在hdpi、xhdpi、xxhdpi文件夹中。

事实上,只获取特定区域的像素非常简单。但是文档中关于步幅参数的内容是错误的。上面写着:

public void getPixels(int[]像素,int偏移量,int步长,int x,int y,int宽度,int高度)

跨步:行之间要跳过的项数(以像素[]为单位)(必须为>=位图的宽度)。可以是负数。

但事实是,它可以(而且在大多数情况下必须)小于位图的宽度,它只需要大于或等于传递的宽度(getPixels倒数第二个参数),这意味着我想要像素的区域的宽度。

因此,要获得位图第一个四分之一的像素:

int []pixels = new int[bitmap.getWidth()>>1 * bitmap.getHeight()>>1]; // quarter of the bitmap size
bitmap.getPixels(pixels, 0, bitmap.getWidth()>>1, 0, 0, bitmap.getWidth()>>1, bitmap.getHeight()>>1);

或者获取一个具有x,y(左上角)和宽度,高度的特定矩形:

int []pixels = new int[width*height];
bitmap.getPixels(pixels, 0, width, x, y, width, height);

这么简单。只是错误的文件让我的大脑发生了变化。

最新更新