ViewFlipper:OutOfMemoryError:位图大小超出VM预算

3

我在我的安卓应用中使用了ViewFlipper。在调用startflipping()后,如果我让应用运行一段时间,很快就会出现以下错误:

11-22 15:36:34.354: ERROR/dalvikvm-heap(428): 307200-byte external allocation too large for this process.
11-22 15:36:34.372: ERROR/(428): VM won't let us allocate 307200 bytes
11-22 15:36:34.375: ERROR/AndroidRuntime(428): Uncaught handler: thread main exiting due to uncaught exception
11-22 15:36:34.384: ERROR/AndroidRuntime(428): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-22 15:36:34.384: ERROR/AndroidRuntime(428):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
11-22 15:36:34.384: ERROR/AndroidRuntime(428):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459)
11-22 15:36:34.384: ERROR/AndroidRuntime(428):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:271)
11-22 15:36:34.384: ERROR/AndroidRuntime(428):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:296)
11-22 15:36:34.384: ERROR/AndroidRuntime(428):     at com.PlayerOrange.ViewPlaylist.populate(ViewPlaylist.java:115)
11-22 15:36:34.384: ERROR/AndroidRuntime(428):     at com.PlayerOrange.ViewPlaylist.access$0(ViewPlaylist.java:100)
11-22 15:36:34.384: ERROR/AndroidRuntime(428):     at com.PlayerOrange.ViewPlaylist$ProgressTask$1$1.run(ViewPlaylist.java:383)

每30秒我会得到如下关于内存的一些信息:

ActivityManager activityManager = (ActivityManager) getBaseContext().getSystemService(ACTIVITY_SERVICE);
                android.app.ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
                activityManager.getMemoryInfo(memoryInfo);

                Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
                Log.i(TAG, " memoryInfo.lowMemory " + memoryInfo.lowMemory + "\n" );
                Log.i(TAG, " memoryInfo.threshold " + memoryInfo.threshold + "\n" );

在我遇到“发生内存溢出错误导致强制关闭”的情况之前,我在DDMS中看到了以下内容:
11-22 15:36:10.255: INFO/(428):  memoryInfo.availMem 50470912
11-22 15:36:10.255: INFO/(428):  memoryInfo.lowMemory false
11-22 15:36:10.264: INFO/(428):  memoryInfo.threshold 16777216

我不知道如何解决这个错误。

以下是用于填充 ViewFlipper 的代码:

private void populate() {
        for (int i = 0; i < jArray.length(); i++) {
            System.out.println("lungime" + jArray.length());
            LinearLayout l = new LinearLayout(this);
            l.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
                    LayoutParams.FILL_PARENT));
            l.setBackgroundColor(0x000000);
            l.setOrientation(LinearLayout.VERTICAL);
            vf.addView(l);

            File f = new File(Environment.getExternalStorageDirectory()
                    + "/Downloads/");

            File[] files = f.listFiles();

            Bitmap bitmap = BitmapFactory.decodeFile(files[i].getPath());
            img = new ImageView(this);
            img.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
                    LayoutParams.FILL_PARENT));

            img.setImageBitmap(bitmap);

            System.out.println("target " + target[i]);
            img.setOnTouchListener(this);
            img.setId(i);

            l.addView(img);
            img = null;

        }

vf = (ViewFlipper) findViewById(R.id.details);
            vf.setFlipInterval(Integer.valueOf(timer) * 1000);
            vf.startFlipping();
            populate();

我从网上获取图片并保存到SD卡。然后我从SD卡文件夹中获取它们,并将它们添加到viewFlipper中。

欢迎提出任何想法。提前致谢。

3个回答

1
  1. 使用一些缓存机制来减少内存的使用。
  2. 使用 BitmapFactory.Options 来缩小图像 http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

这里的缓存意味着使用某种数据结构来保持对您的图像的有效引用。这只是意味着您可以通过缓存按需访问图像,并且缓存将尝试仅保留对所需图像的引用,如所需。通常使用 SoftReference: http://download.oracle.com/javase/6/docs/api/java/lang/ref/SoftReference.html 或者甚至是 WeakReference: http://download.oracle.com/javase/6/docs/api/java/lang/ref/WeakReference.html 来实现。

以下是一个示例:https://github.com/thest1/LazyList


@Gabrielle,我在上面的答案中添加了一些解释。 - LuxuryMode
即使我使用SoftReference,5分钟后仍然会出现Force Close :| - Gabrielle

1

你只是使用了太多太大的位图图像。

如果一次性在内存中有太多的图像,它会导致OutOfMemoryException,因为位图实际上只是巨大的二维数组。

如其他答案所述,您可以做两件不同的事情:减小图像的大小或减少一次性在内存中的图像数量。

您可以使用缓存或弱引用,可以动态缩小位图。这些是相对高级的技术。如果可能的话,最好的选择是使用较小的位图作为源。

如果不能,这里有一个简单的解决方案:

        img.setImageBitmap(bitmap);
        bitmap.recycle();
        System.out.println("target " + target[i]);

这应该更快地清除位图对象。有关更多信息,请参见以下内容:recycle


嗨,Graeme,当我按照你在这里说的那样尝试时,我得到了这个错误:Canvas: trying to use a recycled bitmap android.graphics.Bitmap。你能给出任何建议吗.....? - Kalpesh
一旦您回收了位图,它将被垃圾回收器销毁和清理,并且其内存将被释放以供重用。一旦回收,它将不再可用。 - Graeme
你说得没错,但是我的代码是这样的:File folder = new File(GlobalPhotoPrivacy.savedImagesFolderPath + "/" + rowId);File[] files = folder.listFiles();for (int i = 0; i < files.length; i++) {ImageView imageView = (ImageView) pager.getChildAt(value).findViewById(value); Bitmap bitmap = BitmapFactory.decodeFile(files[value].getAbsolutePath()); imageView.setImageBitmap(bitmap); bitmap.recycle(); bitmap = null; imageView = null; System.gc();} - Kalpesh
在将位图加载到页面后,您应该对其进行回收利用。 - Graeme
我首先将所有的ImageView加载到pager中,然后在后台线程中使用for循环设置位图,并在设置后进行回收...我们犯了什么错误? - Kalpesh
你只是在设置对位图的引用,没有进行内存复制。如果你说“位图在位置4”,然后在生命周期后面删除了位图,当他们检查位置4时将找不到位图。 - Graeme

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接