1

我正在使用thquinn 的 DraggableGridView并将大约 60 个图像加载到其中。这一切都很好。我拥有资产中所需的所有图像,但想在运行时创建它们,因为首先只有图像上的文本会发生变化,这似乎是多余的,我可以减少应用程序大小,第二个原因是我有时需要更改图标添加不会成为问题但无法从资产中删除的空气,并且会占用不必要的空间。在这里简要解释一下我的动机。

所以我使用这篇文章中的方法在我的资产 png 上绘制文本,然后将其转换为位图,以便能够在 LRUcache 中使用它们。这适用于一些图像,但一旦我尝试显示所有需要的图像,我就会收到 OutOfMemory 错误。基本图像是 200x200 像素,我认为也应该根据屏幕尺寸和密度缩放到需要的尺寸。
首先,这种方法似乎效率不高,因为我创建了一个位图画布,然后制作了一个 LayerdDrawable,我再次将其制成位图(用于缓存)。不确定,但感觉就像我正在创建很多临时图像,这会使内存变得混乱。然后我使用了一个折旧的 BitmapDrawable。如果没有 BitmapDrawable,这个方法会怎样?

一般来说,我是否以正确的方式进行此操作,如何有效地使用此方法,以免出现 OOM 错误?!?

顺便提一句。当我不使用 LRUcache 并且只为 GridView 返回 LayerdDrawable 时,图像加载正常,但是在几个方向更改后我得到了异常!这是我在atm拥有的方法:

private Bitmap createIcon(Drawable backgroundImage, String text,
                          int width, int height) {

    String key = text.toLowerCase();
    Bitmap cachedBitmap = getBitmapFromMemCache(key);

    if (cachedBitmap != null){
        Log.d("TRACE", "is cached");
        return cachedBitmap;
    }
    else{

        Bitmap canvasBitmap = Bitmap.createBitmap(width, height,
                Bitmap.Config.ARGB_8888);
        Canvas imageCanvas = new Canvas(canvasBitmap);

        Typeface font = Typeface.createFromAsset(getActivity().getAssets(), "myriadpro.ttf");

        Paint imagePaint = new Paint();
        imagePaint.setTextAlign(Paint.Align.CENTER);
        imagePaint.setTextSize(26);//
        imagePaint.setTypeface(font);
        imagePaint.setAntiAlias(true);
        imagePaint.setColor(Color.parseColor("#562b12"));
        backgroundImage.draw(imageCanvas);

        imageCanvas.drawText(text, (width / 2)+4, (height / 2)-8, imagePaint);

        LayerDrawable layerDrawable = new LayerDrawable(
                new Drawable[]{backgroundImage, new BitmapDrawable(canvasBitmap)});
        int w = layerDrawable.getIntrinsicWidth();
        int h = layerDrawable.getIntrinsicHeight();

        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        layerDrawable.draw(canvas);

        addBitmapToMemoryCache(key,bitmap);
        return bitmap;
    }
}

更新: 我现在尝试了另一种方法,这似乎更好,因为它没有使用 BitmapDrawable。但我仍然收到 OOM 错误。此外,它通常似乎并没有真正使用缓存的图像,当我改变方向时,只有 1 或 2 个图像来自缓存。

在 this 在 Fragment 中之前,我也没有提及。不确定是否重要。但是在纵向模式下,我只有这个片段,而在横向模式下,如果宽度允许,可以有另一个片段。

public Bitmap drawTextToBitmap(Context mContext,  int resourceId,  String mText) {
    try {

        int memory = (int) (Runtime.getRuntime().freeMemory() / 1024);
        Log.d("TRACE", "memory " + memory);
        Log.d("TRACE", mText);

        String key = mText.toLowerCase();
        Bitmap cachedBitmap = getBitmapFromMemCache(key);

        if (cachedBitmap != null){
            Log.d("TRACE", "is cached");
            return cachedBitmap;
        }
        else{
        Resources resources = mContext.getResources();
        float scale = resources.getDisplayMetrics().density;
        Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId);

        android.graphics.Bitmap.Config bitmapConfig =   bitmap.getConfig();
        // set default bitmap config if none
        if(bitmapConfig == null) {
            bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
        }
         bitmap = bitmap.copy(bitmapConfig, true); // OOE error happens here

        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.rgb(110,110, 110));
        paint.setTextSize((int) (25 * scale));

        Rect bounds = new Rect();
        paint.getTextBounds(mText, 0, mText.length(), bounds);
        int x = (bitmap.getWidth() - bounds.width())/6;
        int y = (bitmap.getHeight() + bounds.height())/5;

        canvas.drawText(mText, x * scale, y * scale, paint);

        addBitmapToMemoryCache(key,bitmap);
        return bitmap;
        }
    } catch (Exception e) {
        // TODO: handle exception
        return null;
    }

}
4

1 回答 1

0

我开发了一个应用程序,该应用程序需要在内存中不断保存 3-4 个非常大的图像,并且我正在努力解决与您的问题非常相似的问题。我从一个字节数组中加载了一个位图,然后我需要复制它以使其可变,以便我可以在上面绘图。此副本会导致内存中的位图过多,然后导致 OOM 崩溃。

最终我找到了一种方法来加载它,同时让它可变:

Options options = new Options();
options.inMutable = true;
BitmapFactory.decodeResource(getResources(), id, options);

使用它,而不是复制位图以使其可变。我不确定你是否真的需要 ARGB_8888 配置,但如果你不需要,这至少应该会提高你的内存效率。

注意:这仅适用于 Android 3.0 及更高版本。对于需要在 2.x 及以上版本上运行的版本,您可以使用一点反射魔法来查看运行时是否存在“inMutable”字段。虽然这对 3.0 之前的设备没有帮助,但它仍然会为大多数设备提供很好的改进(我还注意到运行 2.x 的设备往往具有更大的内存灵活性)。

这是代码:

Options options = new Options();
options.inPurgeable = true;
options.inInputShareable = true;

Bitmap mutableBitmap = null;

try
{
    Options.class.getField("inMutable").set(options, Boolean.TRUE);
    Bitmap decodedBitmap = BitmapFactory.decodeResource(getResources(), id, options);                       
    mutableBitmap = decodedBytes;

}
catch (NoSuchFieldException noFieldException)                       
{
    Bitmap decodedBitmap  = BitmapFactory.decodeResource(getResources(), id, options);                              
    mutableBitmap = decodedBitmap .copy(decodedBitmap .getConfig(), true);
    decodedBitmap .recycle();                                                       
}
于 2013-09-25T14:12:11.093 回答