Java.lang.OutOfMemoryError - BitmapFactory.decode(strPath) 意思是:在使用BitmapFactory.decode(strPath)方法时,可能会出现内存不足的错误。

22

每当我调用UploadActivity.java时,我都会遇到java.lang.OutOfMemoryError错误。

第 176 行代码是:

  Bitmap bm = BitmapFactory.decodeFile(strPath);

查看我的日志:

12-07 17:57:10.585: E/AndroidRuntime(16708): FATAL EXCEPTION: main
12-07 17:57:10.585: E/AndroidRuntime(16708): java.lang.OutOfMemoryError
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:650)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:389)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:449)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at com.example.camera.UploadActivity$ImageAdapter.getView(UploadActivity.java:176)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.AbsListView.obtainView(AbsListView.java:2465)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.ListView.makeAndAddView(ListView.java:1775)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.ListView.fillDown(ListView.java:678)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.ListView.fillFromTop(ListView.java:739)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.ListView.layoutChildren(ListView.java:1628)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.AbsListView.onLayout(AbsListView.java:2300)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.View.layout(View.java:14063)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.ViewGroup.layout(ViewGroup.java:4607)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.RelativeLayout.onLayout(RelativeLayout.java:948)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.View.layout(View.java:14063)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.ViewGroup.layout(ViewGroup.java:4607)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.View.layout(View.java:14063)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.ViewGroup.layout(ViewGroup.java:4607)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1655)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1513)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1426)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.View.layout(View.java:14063)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.ViewGroup.layout(ViewGroup.java:4607)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.View.layout(View.java:14063)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.ViewGroup.layout(ViewGroup.java:4607)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1996)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1817)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1114)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4520)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.Choreographer.doCallbacks(Choreographer.java:555)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.Choreographer.doFrame(Choreographer.java:525)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.os.Handler.handleCallback(Handler.java:615)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.os.Handler.dispatchMessage(Handler.java:92)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.os.Looper.loop(Looper.java:137)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at android.app.ActivityThread.main(ActivityThread.java:4921)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at java.lang.reflect.Method.invokeNative(Native Method)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at java.lang.reflect.Method.invoke(Method.java:511)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1036)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:803)
12-07 17:57:10.585: E/AndroidRuntime(16708):    at dalvik.system.NativeStart.main(Native Method)

UploadActivity.java:-

public class ImageAdapter extends BaseAdapter
        {
        private Context context;

        public ImageAdapter(Context c)
        {
        // TODO Auto-generated method stub
        context = c;
        }

        public int getCount() {
        // TODO Auto-generated method stub
        return ImageList.size();
        }

        public Object getItem(int position) {
        // TODO Auto-generated method stub
        return position;
        }

        public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
        }

        public View getView(final int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub

        LayoutInflater inflater = (LayoutInflater) context
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);      

        if (convertView == null) {
        convertView = inflater.inflate(R.layout.list_upload, null);
        }


        // ColImgName
        TextView txtName = (TextView) convertView.findViewById(R.id.ColImgName);
        strPath = ImageList.get(position).toString();

        // Get File Name
        fileName = strPath.substring( strPath.lastIndexOf('/')+1, strPath.length() );
        File file = new File(strPath);
        @SuppressWarnings("unused")
        long length = file.length();
        txtName.setText(fileName);

        // Image Resource
        ImageView imageView = (ImageView) convertView.findViewById(R.id.ColImgPath);
        Bitmap bm = BitmapFactory.decodeFile(strPath);
        imageView.setImageBitmap(bm);


        // ColStatus
        final ImageView txtStatus = (ImageView) convertView.findViewById(R.id.ColStatus);
        txtStatus.setImageResource(R.drawable.bullet_button);

        // progressBar
        final ProgressBar progress = (ProgressBar) convertView.findViewById(R.id.progressBar);
        progress.setVisibility(View.GONE);

        //btnUpload
        final ImageButton btnUpload = (ImageButton) convertView.findViewById(R.id.btnUpload);
        btnUpload.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
        // Upload
        btnUpload.setEnabled(false);

        startUpload(position);
        }
        });

        return convertView;

        }
    } 

你可以直接用文件名的字符串替换Uri参数。这段代码同样适用于BitmapFactory.decodeFile - zapl
@zapl请指引我一下。 - Sun
Santhosh的回答已经大致做到了这一点。试试看吧。StackOverflow希望您具有基本的编程理解能力,并且能够自己完成它。 - zapl
@zapl 很抱歉,那时我没有看到 Santosh 的回答。 - Sun
如果您可以使用Santhosh答案中提供的代码片段,我相信您的问题应该已经解决了。不过,在这里http://developer.android.com/training/displaying-bitmaps/load-bitmap.html有一个更详细的版本,告诉您如何使用这些代码片段。 - saiful103a
哪个解决方案对我们有效,是解决方案1还是解决方案2? - Shakeeb Ayaz
6个回答

68

你需要回收Bitmap对象。

    Bitmap bm = BitmapFactory.decodeFile(strPath);
    imageView.setImageBitmap(bm);
在您的get视图中添加以上代码后,只需添加下面写的代码即可: ///现在回收您的位图,这将在每次迭代中释放您的内存。
    if(bm!=null)
   {
     bm.recycle();
     bm=null;
    }
如果你还遇到相同的错误,请执行以下操作:
替换下面的代码。
    Bitmap bm = BitmapFactory.decodeFile(strPath);
    imageView.setImageBitmap(bm);

随着

 final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;

Bitmap bm = BitmapFactory.decodeFile(strPath,options);
imageView.setImageBitmap(bm);

使用inSampleSize来将位图缩放加载到内存中。使用2的幂次方作为inSampleSize值对于解码器来说更快、更高效。但是,如果计划将调整大小的版本缓存到内存或磁盘上,通常仍然值得解码为最合适的图像尺寸以节省空间。

更多信息请参见高效加载大位图


5
我可能错了,但如果在setImageBitmap之后立即回收位图,它不应该会抛出“Canvas:trying to use a recycled bitmap”错误吗? - saiful103a
1
不,但我之前遇到过这个问题,现在正在尝试理解它如何帮助解决内存不足的问题。据我所知,在设置位图后,如果您调用位图上的recycle()方法,只要系统不使View无效,就不会出现任何问题。正如您建议的那样,在设置位图后我们应该立即调用bm.recycle();。在这种情况下,如果用户滚动列表/网格视图,系统将使UI中的所有内容无效,并且在无效期间,系统将找不到imageview的像素数据。这就是用户会收到“Canvas: trying to use a recycled bitmap”错误的地方? - saiful103a
@saiful103a,你有没有记录过getView(),以了解它的工作原理或在调用ListView时它被调用的次数?还有一件事,你没有使用ViewHolder设计,这也可能会引起内存问题,这里有一个ViewHolder的教程https://www.youtube.com/watch?v=OTEiRiMaQ7M。我是新手,你比我资深,如果我的话太苛刻,请原谅我,如果我错了,我准备自我纠正。看完这个视频,你对我的答案会有所了解。 - Shakeeb Ayaz
@ShakeebAyaz 我遇到了“Canvas: trying to use a recycled bitmap”错误... 你有什么想法吗? - Thomas
@Thomas 在再次使用回收的位图之前,请重新初始化该位图。 - Shakeeb Ayaz
显示剩余4条评论

13

在应用程序标签中的清单文件中使用此代码

android:largeHeap="true"

7

在调用Bitmap bm=BitmapFactory.decodeFile(strPath)之前,先调用以下方法:Bitmap bm =decodeSampledBitmapFromResource(strPath,reqWidth,reqHeight);

如果你再次遇到java.lang.OutOfMemoryError,请告诉我。

public static int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;    
        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and
            // keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

public static Bitmap decodeSampledBitmapFromResource(String strPath,int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(strPath, options);
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options,reqWidth,
                reqHeight);
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(strPath, options);;
}

我按照你的指示操作了,但在这行代码上出现了多个标记:
  • reqHeight无法解析为变量
  • "int"存在语法错误,请删除此标记
  • reqWidth无法解析为变量
  • "strPath"存在语法错误,请删除此标记
  • 无法将String解析为变量
  • "int"存在语法错误,请删除此标记
代码如下:Bitmap bm = decodeSampledBitmapFromResource(String strPath, int reqWidth, int reqHeight);
- Sun
strPath = sdcard 中图像的绝对路径 尝试使用宽度和高度为 100 x 100 Bitmap bm = decodeSampledBitmapFromResource(strPath, reqWidth, reqHeight) - Santhosh

4

这是关于应用程序优化的问题。当您使用 BitmapFactory.decodeFile(strPath) 时,Android 将尝试为该位图分配内存,因此您会收到 OutOfMemoryError 错误。在您的情况下,系统找不到足够的可用空间进行分配,这就是为什么会出现此错误。

现在根据您的代码,我可以看到您正在尝试使用 ImageAdapter 显示图像列表。在这种情况下,您的 ImageView 必须具有比实际图像更小的宽度和高度。

这里是更一般的说明:

  • ImageView 宽度 * 高度 = 100dp * 100dp
  • 图像宽度 * 高度 = 800px * 800px。

在这种情况下,尽管我们的 ImageView 宽度和高度为 100*100,但我们正在尝试将 800*800 的图像设置为其背景。这绝对不是有效的方式,因为系统将为 800*800 像素图像分配内存,而100*100 就足够了。

这就是为什么在解码位图之前,您应该对位图进行采样,以便只分配 100*100 的内存。

您可以在这里找到更详细的说明。


3
我遇到了同样的问题。我尝试了上面提到的所有建议,但仍然遇到了java.lang.outOfMemoryError的问题。经过多次调整后,最终有效的方法是缩小图像尺寸(和大小)。 原来,我的图像尺寸为800 x 1000左右。我将它们缩小到大约60 x 80(因为这就是我需要的),然后问题解决了! 所以,除了按照stackoverflow和其他网站关于此问题的所有建议之外,还要对您的图像大小和尺寸进行基本检查。这将节省您很多麻烦。

你如何在Java中实现这个? - learner
你不能在Java中缩小图像,至少我不知道有什么方法可以做到。你可以在MS Paint中完成或是使用MS Photoviewer中的图像压缩功能。 - Rudra

0

在清单文件中使用android:largeHeap="true",并在创建位图后调用bitmap.recycle()


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