Android 从Listview创建位图时发生OutOfMemoryException

Android 从Listview创建位图时发生OutOfMemoryException,android,listview,bitmap,out-of-memory,screenshot,Android,Listview,Bitmap,Out Of Memory,Screenshot,我的应用程序在从Listview创建位图时抛出OutOfMemoryException。此错误是新错误,但未对方法进行任何更改 如果有人对这个问题有想法就好了。以下是stacktrace: java.lang.OutOfMemoryError: at dalvik.system.VMRuntime.newNonMovableArray (Native Method) at android.graphics.Bitmap.nativeCreate (Native Method) at

我的应用程序在从Listview创建位图时抛出OutOfMemoryException。此错误是新错误,但未对方法进行任何更改

如果有人对这个问题有想法就好了。以下是stacktrace:

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

这段代码与使用ListView的整个想法背道而驰。listView的观点是,您永远不会获得所有子对象的视图-您只创建(并测量和绘制)实际在屏幕上的子对象。这样做的原因是为了节省一个大列表占用的大量内存

基本上,如果您曾经直接调用getView,那么您就错了。如果您曾经在循环中调用getView,那么您不仅做错了,而且还抵消了listview,并且比刚创建一个大视图时做得更糟

此外,如果你的列表足够大,可以占据超过一个全屏的空间,那么它将是巨大的。位图占用4*width*height字节的内存(加上一些开销)。如果在现代密度设备上有两个屏幕长,则很容易占用10秒或100秒的兆欧表。你就是没有那么多的记忆力


这段代码永远不会工作,如果大小超过一个小数目,它也永远不会工作。我首先要问,为什么您认为需要视图中每个项目的位图(而不仅仅是屏幕上的项目),因为这不太可能是您真正想要的,或者根本无法实现。

图片只有3mb。Listview中的项目不超过40个。应用程序具有共享整个listview的功能。我没有这个错误,也没有其他人从我的朋友,所以我无法复制。有没有其他更好的方法或想法呢?首先,图片不可能只有3MB。它需要4个*宽度*高度字节。可能是3MB压缩,但位图对象没有压缩。第二,40个项目太多了。这将至少有5个屏幕。在星系S8上,这将是2960x1440x5x4,或大约90MB。如果您没有做任何其他可能导致堆栈崩溃的事情,请添加其余代码,几乎肯定会出错。这基本上不是你应该做的事情。
bigbitmap = Bitmap.createBitmap(listview.getMeasuredWidth(), allitemsheight, Bitmap.Config.ARGB_8888);