OutOfMemoryException при создании BitMap из списка просмотра - PullRequest
0 голосов
/ 02 мая 2018

мое приложение генерирует исключение OutOfMemoryException при создании BitMap из списка. Эта ошибка является новой, но не внесла никаких изменений в метод.

Было бы неплохо, если бы у кого-нибудь было представление об этой проблеме. Вот трассировка стека:

java.lang.OutOfMemoryError: 
  at dalvik.system.VMRuntime.newNonMovableArray (Native Method)
  at android.graphics.Bitmap.nativeCreate (Native Method)
  at android.graphics.Bitmap.createBitmap (Bitmap.java:977)
  at android.graphics.Bitmap.createBitmap (Bitmap.java:948)
  at android.graphics.Bitmap.createBitmap (Bitmap.java:915)
  at com.beerapp.givemebeerfree.utils.Helper.getWholeListViewItemsToBitmap (Helper.java:300)
  at com.beerapp.givemebeerfree.fragments.ListViewFragment$4.onClick (ListViewFragment.java:196)
  at android.support.v7.app.AlertController$ButtonHandler.handleMessage (AlertController.java:162)
  at android.os.Handler.dispatchMessage (Handler.java:102)
  at android.os.Looper.loop (Looper.java:154)
  at android.app.ActivityThread.main (ActivityThread.java:6776)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1518)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1408)

А вот и метод:

    public static Bitmap getWholeListViewItemsToBitmap(SearchableAdapter adapter, ListView listview, int singleOffer){
    Bitmap bigbitmap;
    try{
        int itemscount       = adapter.getCount();
        int allitemsheight   = 0;
        List<Bitmap> bmps    = new ArrayList<>();
        // If screenshot from single offer, than the item with the shop info has to be added too
        OfferItem singleOfferItem = adapter.getItem(singleOffer);
        OfferItem item;
        for (int i = 0; i < itemscount; i++) {
            item = adapter.getItem(i);
            if (((singleOffer == 0) || (i == singleOffer)) ||
                    ((singleOffer != 0) && (item.getShopID() == singleOfferItem.getShopID()) && item.getShopLogoID() != 0)){
                View childView = adapter.getView(i, null, listview);
                childView.measure(View.MeasureSpec.makeMeasureSpec(listview.getWidth(), View.MeasureSpec.EXACTLY),
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

                childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
                childView.setDrawingCacheEnabled(true);
                childView.destroyDrawingCache();
                childView.buildDrawingCache();
                bmps.add(childView.getDrawingCache(true));
                allitemsheight += childView.getMeasuredHeight();
            }
        }
        bigbitmap = Bitmap.createBitmap(listview.getMeasuredWidth(), allitemsheight, Bitmap.Config.ARGB_8888);
        Canvas bigcanvas    = new Canvas(bigbitmap);

        Paint paint = new Paint();
        int iHeight = 0;

        for (int i = 0; i < bmps.size(); i++) {
            Bitmap bmp = bmps.get(i);
            bigcanvas.drawBitmap(bmp, 0, iHeight, paint);
            iHeight+=bmp.getHeight();

            bmp.recycle();
            bmp=null;
        }
        for (int i = 0; i < itemscount; i++) {

            View childView = adapter.getView(i, null, listview);
            childView.measure(View.MeasureSpec.makeMeasureSpec(listview.getWidth(), View.MeasureSpec.EXACTLY),
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

            childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
            childView.setDrawingCacheEnabled(true);
            childView.destroyDrawingCache();
        }
    }catch (Exception e){
        bigbitmap = null;
    }
    return bigbitmap;
}

Строка 300:

bigbitmap = Bitmap.createBitmap(listview.getMeasuredWidth(), allitemsheight, Bitmap.Config.ARGB_8888);

1 Ответ

0 голосов
/ 02 мая 2018

Этот код противоречит всей идее использования ListView. Смысл listView в том, что вы никогда не получаете представления для всех детей - вы только создаете (и измеряете, и рисуете) детей, которые действительно находятся на экране. Это делается для того, чтобы сохранить большие объемы памяти, занимаемые большим списком.

В основном, если вы когда-либо вызываете getView напрямую, вы делаете это неправильно. Если вы когда-либо вызываете getView в цикле - вы не только делаете это неправильно, но вы противодействуете просмотру списка и делаете это хуже, чем если бы вы только что создали гигантское представление для начала.

Кроме того - если ваш список достаточно велик, чтобы занимать больше, чем полный экран, он будет ОГРОМНЫМ. Растровое изображение занимает 4 * ширины * высоты байтов памяти (плюс некоторые накладные расходы). Если у вас есть два экрана длиной на современном устройстве плотности, вы можете легко взять 10 или 100 мегабайт. У тебя просто не так много памяти.

Этот код никогда не будет работать и никогда не сможет работать, если его размер превышает тривиальную величину. Я бы начал с вопроса, почему вы думаете, что вам нужно растровое изображение каждого элемента в представлении (а не только то, что на экране), поскольку это вряд ли будет тем, что вы на самом деле хотите, или вообще достижимо.

...