有效地绘制资产png并缓存

时间:2013-09-16 16:20:17

标签: android performance bitmap out-of-memory android-lru-cache

我正在使用thquinn’s DraggableGridView并加载~60张图片。一切正常。我拥有了我的资产中所需的所有图像,但是想要在运行时创建它们,因为只有图像上的Text更改,这似乎是多余的,我可以减少appsize,第二个原因是我需要有时更改图标添加不会成为问题的空气,但不能从资产中删除,并会使用不必要的空间。这简要地解释了我的动机。

所以我使用method from this Post在我的Asset png上绘制文本,然后将其转换为Bitmap,以便能够在LRUcache中使用它们。这适用于一些图像,但一旦我尝试显示所有需要的图像,我得到一个OutOfMemory错误。基本图像是200x200像素,我认为也应根据屏幕尺寸和密度调整到需要尺寸 首先,这个方法看起来效率不高,因为我创建了一个Bitmap画布,然后制作了一个LayerdDrawable,我再次将其制作成Bitmap(用于缓存)。不确定,但感觉就像我正在创造那些使记忆混乱的临时图像。 然后我使用折旧的BitmapDrawable。 如果没有BitmapDrawable,这个方法看起来怎么样?

我是否正确地采用了这种方法,如何有效地使用此方法,以免出现OOM错误?!

顺便说一句。当我不使用LRUcache并且只返回GridView的LayerdDrawable时,图像加载正常,但是在几次Orientation更改后我得到了Exception! 这就是我的方法:

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个图像来自缓存。

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

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;
    }

}

1 个答案:

答案 0 :(得分: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();                                                       
}