我有一个ListView,每行上有两个图像按钮。当用户单击列表行时,它将启动一个新活动。我不得不建立我自己的标签,因为相机布局的问题。为结果启动的活动是一个映射。如果我点击我的按钮来启动图像预览(从SD卡加载图像),应用程序从活动返回到ListView活动到结果处理程序,重新启动我的新活动,这只不过是一个图像小部件。

ListView上的图像预览是用光标和ListAdapter完成的。这使得它非常简单,但我不确定我如何可以把一个调整大小的图像(即较小的位大小而不是像素作为图像按钮的src)。我只是调整了从手机摄像头拍下来的图片的大小。

问题是,当它试图返回并重新启动第二个活动时,我得到一个OutOfMemoryError。

是否有一种方法,我可以轻松地构建列表适配器行,在那里我可以调整大小的飞行(位)?

这将是可取的,因为我还需要对每行中的小部件/元素的属性做出一些更改,因为我无法选择一行与触摸屏,因为焦点问题。(我会用滚轮。)

我知道我可以做带外调整并保存我的图像,但这不是我真正想做的,但一些示例代码会很好。

当我在ListView上禁用图像时,它又可以正常工作了。

供你参考:我是这样做的:

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME, DBHelper.KEY_ADDRESS,
    DBHelper.KEY_CITY, DBHelper.KEY_GPSLONG, DBHelper.KEY_GPSLAT,
    DBHelper.KEY_IMAGEFILENAME  + ""};
int[] to = new int[] { R.id.businessname, R.id.address, R.id.city, R.id.gpslong,
    R.id.gpslat, R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);

其中R.id.imagefilename是一个ButtonImage。

这是我的LogCat:

01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed 

我也有一个新的错误时显示的图像:

22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed

当前回答

这似乎是一个长期存在的问题,有很多不同的解释。我采纳了这里给出的两个最常见答案的建议,但它们都没有解决我的VM声称它无法负担字节来执行过程的解码部分的问题。经过一番深入研究,我了解到这里的真正问题是解码过程从NATIVE堆中取出。

BitmapFactory OOM把我逼疯了

这将我引向另一个讨论线程,在那里我找到了这个问题的更多解决方案。一个是调用system .gc();显示图像后手动操作。但这实际上会使你的应用程序使用更多的内存,以减少本机堆。对于2.0版本(Donut)来说,更好的解决方案是使用BitmapFactory选项“inPurgeable”。所以我添加了o2.inPurgeable=true;就在o2.inSampleSize=scale;之后。

关于这个主题的更多信息:内存堆的限制只有6M吗?

现在,说了这么多,我对Java和Android也是个十足的笨蛋。所以如果你认为这是一个糟糕的解决问题的方法,你可能是对的。;-)但这已经为我工作的奇迹,我发现它不可能运行VM的堆缓存现在。唯一的缺点我能找到的是,你是垃圾缓存绘制的图像。这意味着如果你直接回到那个图像,你每次都要重新绘制它。就我的应用程序的工作方式而言,这并不是真正的问题。你的里程可能会有所不同。

其他回答

我有iOS经验,我很沮丧地发现加载和显示图像这样基本的问题。毕竟,每个人都有这个问题,试图显示合理大小的图像。不管怎样,这里有两个改变解决了我的问题(并使我的应用程序非常灵敏)。

1)每次执行BitmapFactory. decodexyz()时,确保传入一个BitmapFactory。选项inPurgeable设置为true(最好inInputShareable也设置为true)。

2)绝对不要使用位图。createBitmap(width, height, Config.ARGB_8888)。我的意思是绝不!我从来没有过这个东西在几次传递后不产生内存错误。没有多少recycle(), System.gc(),任何有用的东西。它总是抛出异常。另一种实际工作的方法是在你的绘图中有一个虚拟图像(或另一个你使用上述步骤1解码的位图),将其重新缩放为你想要的任何东西,然后操纵结果位图(例如将它传递到画布上以获得更多乐趣)。所以,你应该使用的是:位图。createScaledBitmap(srcBitmap, width, height, false)。如果出于某种原因你必须使用暴力创建方法,那么至少要传递Config.ARGB_4444。

这几乎可以保证节省你几个小时,如果不是几天。所有关于缩放图像等的讨论都是行不通的(除非你认为得到错误的大小或降级的图像是一个解决方案)。

上面的答案都不适合我,但我确实想出了一个可怕的解决问题的方法。我将一个非常小的1x1像素的图像作为资源添加到我的项目中,并在调用垃圾收集之前将其加载到我的ImageView中。我认为这可能是ImageView没有释放位图,所以GC从未拾取它。虽然很难看,但目前看来还是有效的。

if (bitmap != null)
{
  bitmap.recycle();
  bitmap = null;
}
if (imageView != null)
{
  imageView.setImageResource(R.drawable.tiny); // This is my 1x1 png.
}
System.gc();

imageView.setImageBitmap(...); // Do whatever you need to do to load the image you want.

这里的所有解决方案都需要设置IMAGE_MAX_SIZE。这限制了硬件更强大的设备,如果图像尺寸太小,在高清屏幕上看起来会很难看。

我想出了一个解决方案,它适用于我的三星Galaxy S3和其他几台设备(包括功能不太强大的设备),当使用功能更强大的设备时,图像质量会更好。

它的要点是计算在特定设备上分配给应用程序的最大内存,然后将规模设置为尽可能低的,而不超过这个内存。代码如下:

public static Bitmap decodeFile(File f)
{
    Bitmap b = null;
    try
    {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        FileInputStream fis = new FileInputStream(f);
        try
        {
            BitmapFactory.decodeStream(fis, null, o);
        }
        finally
        {
            fis.close();
        }

        // In Samsung Galaxy S3, typically max memory is 64mb
        // Camera max resolution is 3264 x 2448, times 4 to get Bitmap memory of 30.5mb for one bitmap
        // If we use scale of 2, resolution will be halved, 1632 x 1224 and x 4 to get Bitmap memory of 7.62mb
        // We try use 25% memory which equals to 16mb maximum for one bitmap
        long maxMemory = Runtime.getRuntime().maxMemory();
        int maxMemoryForImage = (int) (maxMemory / 100 * 25);

        // Refer to
        // http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
        // A full screen GridView filled with images on a device with
        // 800x480 resolution would use around 1.5MB (800*480*4 bytes)
        // When bitmap option's inSampleSize doubled, pixel height and
        // weight both reduce in half
        int scale = 1;
        while ((o.outWidth / scale) * (o.outHeight / scale) * 4 > maxMemoryForImage)
        scale *= 2;

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = new FileInputStream(f);
        try
        {
            b = BitmapFactory.decodeStream(fis, null, o2);
        }
        finally
        {
            fis.close();
        }
    }
    catch (IOException e)
    {
    }
    return b;
}

我将此位图使用的最大内存设置为最大已分配内存的25%,您可能需要根据需要进行调整,并确保此位图被清理,并且当您使用完它时,它不会停留在内存中。通常我使用这段代码来执行图像旋转(源和目标位图),所以我的应用程序需要同时在内存中加载2个位图,25%给了我一个很好的缓冲区,而不会在执行图像旋转时耗尽内存。

希望这能帮助到一些人。

不幸的是,如果以上都不起作用,那么将此添加到您的清单文件。应用程序内部标签

 <application
         android:largeHeap="true"

要修复OutOfMemory,你应该这样做,请尝试这段代码

public Bitmap loadBitmap(String URL, BitmapFactory.Options options) {
                Bitmap bitmap = null;
                InputStream in = null;
                options.inSampleSize=4;
                try {
                    in = OpenHttpConnection(URL);
                    Log.e("In====>", in+"");
                    bitmap = BitmapFactory.decodeStream(in, null, options);
                    Log.e("URL====>", bitmap+"");

                    in.close();
                } catch (IOException e1) {
                }
                return bitmap;
            }

and

try {
                    BitmapFactory.Options bmOptions;
                    bmOptions = new BitmapFactory.Options();
                    bmOptions.inSampleSize = 1;
                    if(studentImage != null){
                        galleryThumbnail= loadBitmap(IMAGE_URL+studentImage, bmOptions);    
                    }

                    galleryThumbnail=getResizedBitmap(galleryThumbnail, imgEditStudentPhoto.getHeight(), imgEditStudentPhoto.getWidth());
                    Log.e("Image_Url==>",IMAGE_URL+studentImage+"");

                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }