【问题标题】:Strange OutOfMemory issue while loading an image to a Bitmap object将图像加载到位图对象时出现奇怪的 OutOfMemory 问题
【发布时间】:2010-10-03 10:00:25
【问题描述】:

我有一个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.imagefilenameButtonImage

这是我的 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

【问题讨论】:

  • 我通过避免使用 Bitmap.decodeStream 或 decodeFile 并使用 BitmapFactory.decodeFileDescriptor 方法解决了这个问题。
  • 几周前我也遇到了类似的问题,我通过将图像缩小到最佳点来解决它。我已经在我的博客codingjunkiesforum.wordpress.com/2014/06/12/… 中编写了完整的方法,并在 https://github.com/shailendra123/BitmapHandlingDemo 上传了带有 OOM 倾向代码与 OOM 证明代码的完整示例项目
  • 关于这个问题的公认答案正在meta上讨论
  • 这是由于糟糕的 android 架构造成的。它应该像 ios 一样调整图像本身的大小,而 UWP 会这样做。我不必自己做这些事情。 Android 开发人员已经习惯了这种地狱,并认为它应该按应有的方式工作。

标签: android image bitmap out-of-memory android-bitmap


【解决方案1】:

Android Training 类“Displaying Bitmaps Efficiently”为理解和处理异常“java.lang.OutOfMemoryError:加载位图时位图大小超出 VM 预算”提供了一些重要信息。


读取位图尺寸和类型

BitmapFactory 类提供了几种解码方法(decodeByteArray()decodeFile()decodeResource() 等),用于从各种来源创建 Bitmap。根据您的图像数据源选择最合适的解码方法。这些方法尝试为构造的位图分配内存,因此很容易导致OutOfMemory 异常。每种类型的解码方法都有额外的签名,让您可以通过BitmapFactory.Options 类指定解码选项。在解码时将inJustDecodeBounds 属性设置为true 可避免内存分配,为位图对象返回null,但设置outWidthoutHeightoutMimeType。此技术允许您在构建位图(和内存分配)之前读取图像数据的尺寸和类型。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

为避免出现java.lang.OutOfMemory 异常,请在解码之前检查位图的尺寸,除非您绝对相信来源能够为您提供大小可预测的图像数据,这些数据可以轻松容纳在可用内存中。


将缩小版本加载到内存中

现在图像尺寸已知,它们可用于决定是否应将完整图像加载到内存中,或者是否应加载子采样版本。以下是一些需要考虑的因素:

  • 在内存中加载完整图像的估计内存使用量。
  • 考虑到您的应用程序的任何其他内存要求,您愿意承诺用于加载此图像的内存量。
  • 要加载图像的目标 ImageView 或 UI 组件的尺寸。
  • 当前设备的屏幕尺寸和密度。

例如,如果一个 1024x768 像素的图像最终会显示在 ImageView 中的 128x96 像素缩略图中,则不值得将其加载到内存中。

要告诉解码器对图像进行二次采样,将较小的版本加载到内存中,请在 BitmapFactory.Options 对象中将 inSampleSize 设置为 true。例如,分辨率为 2048x1536 的图像使用 4 的inSampleSize 进行解码会生成大约 512x384 的位图。将其加载到内存中使用 0.75MB 而不是 12MB 的完整图像(假设位图配置为ARGB_8888)。这是一种根据目标宽度和高度计算样本大小值的方法,该值是 2 的幂:

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

注意:由于解码器使用了 通过四舍五入到最接近的 2 次幂,根据 inSampleSize 文档。

要使用这种方法,首先解码inJustDecodeBounds设置为true, pass the options through and then decode again using the new inSampleSizevalue andinJustDecodeBoundsset tofalse`:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

此方法可以轻松地将任意大尺寸的位图加载到显示 100x100 像素缩略图的ImageView 中,如以下示例代码所示:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

您可以按照类似的过程对来自其他来源的位图进行解码,方法是根据需要替换适当的BitmapFactory.decode* 方法。

【讨论】:

  • 这个答案正在meta讨论
  • 这个答案(通过链接获得的信息除外)并没有提供太多的解决方案。链接的重要部分应合并到问题中。
  • 这个答案,就像问题和其他答案一样,都是社区 Wiki,所以这是社区可以通过编辑来解决的问题,不需要版主干预。
【解决方案2】:

我需要将大尺寸图像加载到 Bitmap 中,我使用 Glide 解决了这个问题。首先使用 inJustDecodeBounds 设置为 true 检查 BitmapFactory.Options 的图像大小,然后使用 Glide 获取 Bitmap 对象。我使用分析器检查内存使用情况,但没有像使用 BitmapFactory.decodeFile() 时那样看到任何内存峰值。 当我使用 Xamarin 时,我正在用 c# 编写,所以需要一些调整才能在 Java 中使用。 Glide library documentation

private Bitmap DecodeFile(File file) {
        // Decode image size
        BitmapFactory.Options options = new BitmapFactory.Options();
        
        // setting inJustDecodeBounds to true won't load the file into memory, 
        // but gives you the actual file size.
        options.InJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(file), null, options);
        int actualWidth = options.OutWidth;
        int actualHeight = options.OutHeight;
                
        var ratio = (double)actualHeight / actualWidth;

        // Default to 800 x 600. changed the size whatever you need.
        var desiredWidth = 800;
        var desiredHeight = 600;

        if(actualHeight > actualWidth)
        {
            var ratio = (double)actualWidth / actualHeight;
            var futureTarget = Glide.With(Application.Context)
                .AsBitmap()
                .Load(file)
                .SetDiskCacheStrategy(DiskCacheStrategy.None)
                .SkipMemoryCache(true)
                .Submit((int)(desiredWidth * ratio), desiredWidth);
            bitmap = futureTarget.Get() as Bitmap;
        }
        else
        {
            var ratio = (double)actualHeight / actualWidth;
            var futureTarget = Glide.With(Application.Context)
                .AsBitmap()
                .Load(file)
                .SetDiskCacheStrategy(DiskCacheStrategy.None)
                .SkipMemoryCache(true)
                .Submit(desiredWidth, (int)(desiredWidth * ratio));
            bitmap = futureTarget.Get() as Bitmap;
        }return bitmap;}

【讨论】:

    【解决方案3】:

    如果你和我一样懒,可以开始使用Picasso library加载图片。

    Picasso.with(context).load(R.drawable.landing_screen).into(imageView1);
    Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2);
    Picasso.with(context).load(new File(...)).into(imageView3);
    

    【讨论】:

      【解决方案4】:

      将以下行添加到您的 manifest.xml 文件中:

      <application
      
          android:hardwareAccelerated="false"
          android:largeHeap="true">
      
          <activity>
          </activity>
      
      </application>
      

      【讨论】:

      • 请正确格式化您的代码,这看起来也不是有效的 xml
      【解决方案5】:

      避免内存泄漏或位图 OOM 的最佳做法

      1. 不要将位图引用长期保存在上下文/活动中。
      2. 如果您在应用程序中使用大位图作为背景或其他内容,则不要将完整图像拉入主内存。您可以使用位图的 insample size 属性来调整屏幕所需的大小。
      3. 一旦不再使用,请清理位图参考。

      【讨论】:

        【解决方案6】:

        这将得到一个合适的位图并减少内存消耗

        JAVA

        Bitmap bm = null;
        
        BitmapFactory.Options bmpOption = new BitmapFactory.Options();
        bmpOption.inJustDecodeBounds = true;
        
        FileInputStream fis = new FileInputStream(file);
        BitmapFactory.decodeStream(fis, null, bmpOption);
        fis.close();
        
        int scale = 1;
        
        if (bmpOption.outHeight > IMAGE_MAX_SIZE || bmpOption.outWidth > IMAGE_MAX_SIZE) {
            scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
               (double) Math.max(bmpOption.outHeight, bmpOption.outWidth)) / Math.log(0.5)));
        }
        
        BitmapFactory.Options bmpOption2 = new BitmapFactory.Options();
        bmpOption2.inSampleSize = scale;
        fis = new FileInputStream(file);
        bm = BitmapFactory.decodeStream(fis, null, bmpOption2);
        fis.close();
        

        科特林

        val bm:Bitmap = null
        val bmpOption = BitmapFactory.Options()
        bmpOption.inJustDecodeBounds = true
        val fis = FileInputStream(file)
        BitmapFactory.decodeStream(fis, null, bmpOption)
        fis.close()
        val scale = 1
        if (bmpOption.outHeight > IMAGE_MAX_SIZE || bmpOption.outWidth > IMAGE_MAX_SIZE)
        {
          scale = Math.pow(2.0, Math.ceil((Math.log((IMAGE_MAX_SIZE / Math.max(bmpOption.outHeight, bmpOption.outWidth) as Double)) / Math.log(0.5))).toInt().toDouble()).toInt()
        }
        val bmpOption2 = BitmapFactory.Options()
        bmpOption2.inSampleSize = scale
        fis = FileInputStream(file)
        bm = BitmapFactory.decodeStream(fis, null, bmpOption2)
        fis.close()
        

        【讨论】:

          【解决方案7】:

          我遇到了同样的问题,并通过避免使用 BitmapFactory.decodeStream 或 decodeFile 函数来解决它,而是使用BitmapFactory.decodeFileDescriptor

          decodeFileDescriptor 看起来它调用的本机方法与 decodeStream/decodeFile 不同。

          不管怎样,这是有效的(请注意,我添加了一些选项,就像上面的一些选项一样,但这并不是造成差异的原因。关键是调用 BitmapFactory.decodeFileDescriptor 而不是 decodeStreamdecodeFile):

          private void showImage(String path)   {
          
              Log.i("showImage","loading:"+path);
              BitmapFactory.Options bfOptions=new BitmapFactory.Options();
              bfOptions.inDither=false;                     //Disable Dithering mode
              bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
              bfOptions.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
              bfOptions.inTempStorage=new byte[32 * 1024]; 
          
              File file=new File(path);
              FileInputStream fs=null;
              try {
                  fs = new FileInputStream(file);
              } catch (FileNotFoundException e) {
                  //TODO do something intelligent
                  e.printStackTrace();
              }
          
              try {
                  if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
              } catch (IOException e) {
                  //TODO do something intelligent
                  e.printStackTrace();
              } finally{ 
                  if(fs!=null) {
                      try {
                          fs.close();
                      } catch (IOException e) {
                          // TODO Auto-generated catch block
                          e.printStackTrace();
                      }
                  }
              }
              //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
          
              im.setImageBitmap(bm);
              //bm.recycle();
              bm=null;                        
          }
          

          我认为decodeStream/decodeFile中使用的native函数有问题。我已经确认使用 decodeFileDescriptor 时调用了不同的本机方法。另外我读到的是“图像(位图)不是以标准 Java 方式分配的,而是通过本机调用分配的;分配是在虚拟堆之外完成的,但是 算了!"

          【讨论】:

          • 内存溢出的结果相同,实际上,您使用哪种方法并不重要,这取决于您为读取内存不足的数据而持有的字节数。
          【解决方案8】:

          此代码将有助于从 drawable 加载大位图

          public class BitmapUtilsTask extends AsyncTask<Object, Void, Bitmap> {
          
              Context context;
          
              public BitmapUtilsTask(Context context) {
                  this.context = context;
              }
          
              /**
               * Loads a bitmap from the specified url.
               * 
               * @param url The location of the bitmap asset
               * @return The bitmap, or null if it could not be loaded
               * @throws IOException
               * @throws MalformedURLException
               */
              public Bitmap getBitmap() throws MalformedURLException, IOException {       
          
                  // Get the source image's dimensions
                  int desiredWidth = 1000;
                  BitmapFactory.Options options = new BitmapFactory.Options();
                  options.inJustDecodeBounds = true;
          
                  BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);
          
                  int srcWidth = options.outWidth;
                  int srcHeight = options.outHeight;
          
                  // Only scale if the source is big enough. This code is just trying
                  // to fit a image into a certain width.
                  if (desiredWidth > srcWidth)
                      desiredWidth = srcWidth;
          
                  // Calculate the correct inSampleSize/scale value. This helps reduce
                  // memory use. It should be a power of 2
                  int inSampleSize = 1;
                  while (srcWidth / 2 > desiredWidth) {
                      srcWidth /= 2;
                      srcHeight /= 2;
                      inSampleSize *= 2;
                  }
                  // Decode with inSampleSize
                  options.inJustDecodeBounds = false;
                  options.inDither = false;
                  options.inSampleSize = inSampleSize;
                  options.inScaled = false;
                  options.inPreferredConfig = Bitmap.Config.ARGB_8888;
                  options.inPurgeable = true;
                  Bitmap sampledSrcBitmap;
          
                  sampledSrcBitmap =  BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);
          
                  return sampledSrcBitmap;
              }
          
              /**
               * The system calls this to perform work in a worker thread and delivers
               * it the parameters given to AsyncTask.execute()
               */
              @Override
              protected Bitmap doInBackground(Object... item) {
                  try { 
                    return getBitmap();
                  } catch (MalformedURLException e) {
                      e.printStackTrace();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
                  return null;
              }
          }
          

          【讨论】:

            【解决方案9】:

            一般 android 设备堆大小只有 16MB(因​​设备/操作系统而异,请参阅帖子 Heap Sizes),如果您正在加载图像并且超过 16MB 的大小,它将抛出内存不足异常,而不是使用位图,从 SD 卡或资源甚至网络加载图像尝试使用 getImageUri ,加载位图需要更多内存,或者如果您使用该位图完成工作,您可以将位图设置为 null。

            【讨论】:

            【解决方案10】:

            查看所有答案后,我惊讶地发现没有人提到用于处理图像的 Glide API。很棒的库,并抽象出位图管理的所有复杂性。您可以使用此库和一行代码快速加载和调整图像大小。

                 Glide.with(this).load(yourImageResource).into(imageview);
            

            您可以在此处获取存储库:https://github.com/bumptech/glide

            【讨论】:

            • 它不能处理所有场景。 Glide 不是一站式解决方案,我们正在使用 Glide 但仍然面临许多 OOM 崩溃
            • 我并不是说这是一站式解决方案。我将它添加到工具箱中,供那些没有听说过的人使用。
            【解决方案11】:

            我使用了对我有用的解码文件描述符:

             FileInputStream  fileInputStream = null;
                    try {
                        fileInputStream  = new FileInputStream(file);
                         FileDescriptor fd = fileInputStream.getFD();
                        Bitmap imageBitmap = decodeSampledBitmapFromDescriptor(fd , 612,
                                816);
                        imageView.setImageBitmap(imageBitmap);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }finally {
                        if(fileInputStream != null){
                            try {
                                fileInputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            

            从文件描述符中解码采样位图的代码:

             /**
                 * Decode and sample down a bitmap from a file input stream to the requested width and height.
                 *
                 * @param fileDescriptor The file descriptor to read from
                 * @param reqWidth       The requested width of the resulting bitmap
                 * @param reqHeight      The requested height of the resulting bitmap
                 * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
                 * that are equal to or greater than the requested width and height
                 */
                public static Bitmap decodeSampledBitmapFromDescriptor(
                        FileDescriptor fileDescriptor, int reqWidth, int reqHeight) {
            
                    // First decode with inJustDecodeBounds=true to check dimensions
                    final BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inJustDecodeBounds = true;
                    BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
            
                    // Calculate inSampleSize
                    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
            
                    // Decode bitmap with inSampleSize set
                    options.inJustDecodeBounds = false;
                    return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
                }
            
                /**
                 * Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options} object when decoding
                 * bitmaps using the decode* methods from {@link android.graphics.BitmapFactory}. This implementation calculates
                 * the closest inSampleSize that will result in the final decoded bitmap having a width and
                 * height equal to or larger than the requested width and height. This implementation does not
                 * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
                 * results in a larger bitmap which isn't as useful for caching purposes.
                 *
                 * @param options   An options object with out* params already populated (run through a decode*
                 *                  method with inJustDecodeBounds==true
                 * @param reqWidth  The requested width of the resulting bitmap
                 * @param reqHeight The requested height of the resulting bitmap
                 * @return The value to be used for inSampleSize
                 */
                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) {
            
                        // Calculate ratios of height and width to requested height and width
                        final int heightRatio = Math.round((float) height / (float) reqHeight);
                        final int widthRatio = Math.round((float) width / (float) reqWidth);
            
                        // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
                        // with both dimensions larger than or equal to the requested height and width.
                        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
            
                        // This offers some additional logic in case the image has a strange
                        // aspect ratio. For example, a panorama may have a much larger
                        // width than height. In these cases the total pixels might still
                        // end up being too large to fit comfortably in memory, so we should
                        // be more aggressive with sample down the image (=larger inSampleSize).
            
                        final float totalPixels = width * height;
            
                        // Anything more than 2x the requested pixels we'll sample down further
                        final float totalReqPixelsCap = reqWidth * reqHeight * 2;
            
                        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
                            inSampleSize++;
                        }
                    }
                    return inSampleSize;
                }
            

            【讨论】:

              【解决方案12】:

              此问题仅发生在 Android 模拟器中。我在模拟器中也遇到了这个问题,但是当我签入设备时它工作正常。

              所以请检查设备。它可以在设备中运行。

              【讨论】:

                【解决方案13】:

                这样的OutofMemoryException不能通过调用System.gc()等来完全解决。

                参考Activity Life Cycle

                活动状态由操作系统本身决定,取决于每个进程的内存使用情况和每个进程的优先级。

                您可以考虑使用的每个位图图片的大小和分辨率。我建议缩小尺寸,重新采样到更低的分辨率,参考画廊的设计(一张小图PNG,一张原图。)

                【讨论】:

                  【解决方案14】:

                  我来自 iOS 经验,我很沮丧地发现加载和显示图像这样基本的问题。毕竟,遇到此问题的每个人都在尝试显示尺寸合理的图像。无论如何,这里有两个更改解决了我的问题(并使我的应用响应速度非常快)。

                  1) 每次执行BitmapFactory.decodeXYZ() 时,请确保传入BitmapFactory.Options,并将inPurgeable 设置为true(最好将inInputShareable 也设置为true)。

                  2) 永远不要使用Bitmap.createBitmap(width, height, Config.ARGB_8888)。我的意思是从不!几次通过后,我从来没有遇到过不会引发内存错误的事情。没有多少recycle()System.gc(),不管有什么帮助。它总是引发异常。实际可行的另一种方法是在您的可绘制对象(或您使用上面的步骤 1 解码的另一个位图)中有一个虚拟图像,将其重新缩放为您想要的任何内容,然后操作生成的位图(例如将其传递给 Canvas为了更多的乐趣)。所以,你应该改用:Bitmap.createScaledBitmap(srcBitmap, width, height, false)。如果出于某种原因您必须使用蛮力创建方法,那么至少通过Config.ARGB_4444

                  这几乎可以保证为您节省数小时甚至数天。所有关于缩放图像等的讨论并没有真正起作用(除非您认为尺寸错误或图像质量下降是一种解决方案)。

                  【讨论】:

                  • BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true;Bitmap.createScaledBitmap(srcBitmap, width, height, false); 解决了我在 android 4.0.0 上出现内存不足异常的问题。谢谢老哥!
                  • 在 Bitmap.createScaledBitmap() 调用中,您可能应该使用 true 作为标志参数。否则放大时图像质量将不平滑。检查此线程stackoverflow.com/questions/2895065/…
                  • 这真是绝妙的建议。希望我能给你一个额外的+1,让谷歌为这个令人惊讶的 rinky dink bug 执行任务。我的意思是......如果这不是一个错误,那么文档真的需要有一些严重闪烁的霓虹灯标志,上面写着“这就是你处理照片的方式”,因为我已经为此苦苦挣扎了 2 年,现在才找到这篇文章。很棒的发现。
                  • 从 Lollipop 开始,BitmapFactory.Options.inPurgeableBitmapFactory.Options.inInputShareable 已弃用 developer.android.com/reference/android/graphics/…
                  【解决方案15】:

                  很遗憾如果以上方法均无效,请将其添加到您的 Manifest 文件中。 应用程序标签内

                   <application
                           android:largeHeap="true"
                  

                  【讨论】:

                  • 你能解释一下这实际上是做什么的吗?简单地告诉人们添加它并没有帮助。
                  • 这是一个非常糟糕的解决方案。基本上你不是在试图解决问题。而是要求 android 系统为您的应用程序分配更多的堆空间。这将对您的应用产生非常糟糕的影响,例如您的应用会消耗大量电池电量,因为 GC 必须通过大堆空间来清理内存,而且您的应用性能也会变慢。
                  • 那么为什么 android 允许我们在清单中添加这个 android:largeHeap="true" 呢?现在你正在挑战 Android。
                  • @HimanshuMori 您可能需要重新考虑使用 android:largeHeap="true" 的决定。请参阅此答案stackoverflow.com/a/30930239/10158117 或该线程中的任何其他答案。它可能会帮助您了解您做错了什么。
                  【解决方案16】:

                  要修复 OutOfMemory 错误,您应该执行以下操作:

                  BitmapFactory.Options options = new BitmapFactory.Options();
                  options.inSampleSize = 8;
                  Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);
                  

                  这个inSampleSize 选项减少了内存消耗。

                  这是一个完整的方法。首先它读取图像大小而不解码内容本身。然后找到最佳的inSampleSize值,应该是2的幂,最后解码图像。

                  // Decodes image and scales it to reduce memory consumption
                  private Bitmap decodeFile(File f) {
                      try {
                          // Decode image size
                          BitmapFactory.Options o = new BitmapFactory.Options();
                          o.inJustDecodeBounds = true;
                          BitmapFactory.decodeStream(new FileInputStream(f), null, o);
                  
                          // The new size we want to scale to
                          final int REQUIRED_SIZE=70;
                  
                          // Find the correct scale value. It should be the power of 2.
                          int scale = 1;
                          while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
                                o.outHeight / scale / 2 >= REQUIRED_SIZE) {
                              scale *= 2;
                          }
                  
                          // Decode with inSampleSize
                          BitmapFactory.Options o2 = new BitmapFactory.Options();
                          o2.inSampleSize = scale;
                          return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
                      } catch (FileNotFoundException e) {}
                      return null;
                  }
                  

                  【讨论】:

                  • 请注意,10 可能不是 inSampleSize 的最佳值,但文档建议使用 2 的幂。
                  • 我面临与 Chrispix 相同的问题,但我认为这里的解决方案并没有真正解决问题,而是回避了它。更改样本大小会减少使用的内存量(以图像质量为代价,这对于图像预览来说可能是可以的),但是如果解码了足够大的图像流,或者如果多个图像流被解码,它不会阻止异常解码。如果我找到更好的解决方案(可能没有),我会在这里发布答案。
                  • 您只需要一个合适的尺寸来匹配屏幕的像素密度,以便放大,这样您就可以以更高的密度对图像进行采样。
                  • REQUIRED_SIZE 是您要缩放到的新尺寸。
                  • 这个解决方案对我有帮助,但图像质量很差。我正在使用 viewfilpper 显示图像有什么建议吗?
                  【解决方案17】:
                  BitmapFactory.Options options = new Options();
                  options.inSampleSize = 32;
                  //img = BitmapFactory.decodeFile(imageids[position], options);
                  
                  Bitmap theImage = BitmapFactory.decodeStream(imageStream,null, options);
                  Bitmap img=theImage.copy(Bitmap.Config.RGB_565,true);
                  theImage.recycle();
                  theImage = null;
                  System.gc();
                  //ivlogdp.setImageBitmap(img);
                  Runtime.getRuntime().gc();
                  

                  【讨论】:

                    【解决方案18】:

                    对从 SdCard 中选择的每个图像或可绘制的图像使用这些代码来转换位图对象。

                    Resources res = getResources();
                    WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
                    Display display = window.getDefaultDisplay();
                    @SuppressWarnings("deprecation")
                    int width = display.getWidth();
                    @SuppressWarnings("deprecation")
                    int height = display.getHeight();
                    try {
                        if (bitmap != null) {
                            bitmap.recycle();
                            bitmap = null;
                            System.gc();
                        }
                        bitmap = Bitmap.createScaledBitmap(BitmapFactory
                            .decodeFile(ImageData_Path.get(img_pos).getPath()),
                            width, height, true);
                    } catch (OutOfMemoryError e) {
                        if (bitmap != null) {
                            bitmap.recycle();
                            bitmap = null;
                            System.gc();
                        }
                        BitmapFactory.Options options = new BitmapFactory.Options();
                        options.inPreferredConfig = Config.RGB_565;
                        options.inSampleSize = 1;
                        options.inPurgeable = true;
                        bitmapBitmap.createScaledBitmap(BitmapFactory.decodeFile(ImageData_Path.get(img_pos)
                            .getPath().toString(), options), width, height,true);
                    }
                    return bitmap;
                    

                    使用 ImageData_Path.get(img_pos).getPath() 的图像路径。

                    【讨论】:

                      【解决方案19】:

                      我已经通过以下方式解决了同样的问题。

                      Bitmap b = null;
                      Drawable d;
                      ImageView i = new ImageView(mContext);
                      try {
                          b = Bitmap.createBitmap(320,424,Bitmap.Config.RGB_565);
                          b.eraseColor(0xFFFFFFFF);
                          Rect r = new Rect(0, 0,320 , 424);
                          Canvas c = new Canvas(b);
                          Paint p = new Paint();
                          p.setColor(0xFFC0C0C0);
                          c.drawRect(r, p);
                          d = mContext.getResources().getDrawable(mImageIds[position]);
                          d.setBounds(r);
                          d.draw(c);
                      
                          /*   
                              BitmapFactory.Options o2 = new BitmapFactory.Options();
                              o2.inTempStorage = new byte[128*1024];
                              b = BitmapFactory.decodeStream(mContext.getResources().openRawResource(mImageIds[position]), null, o2);
                              o2.inSampleSize=16;
                              o2.inPurgeable = true;
                          */
                      } catch (Exception e) {
                      
                      }
                      i.setImageBitmap(b);
                      

                      【讨论】:

                        【解决方案20】:

                        这对我有用!

                        public Bitmap readAssetsBitmap(String filename) throws IOException {
                            try {
                                BitmapFactory.Options options = new BitmapFactory.Options(); 
                                options.inPurgeable = true;
                                Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
                                if(bitmap == null) {
                                    throw new IOException("File cannot be opened: It's value is null");
                                } else {
                                    return bitmap;
                                }
                            } catch (IOException e) {
                                throw new IOException("File cannot be opened: " + e.getMessage());
                            }
                        }
                        

                        【讨论】:

                          【解决方案21】:

                          我执行了以下操作来拍摄图像并即时调整其大小。希望这会有所帮助

                          Bitmap bm;
                          bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath), 100, 100, true);
                          mPicture = new ImageView(context);
                          mPicture.setImageBitmap(bm);    
                          

                          【讨论】:

                          • 这种方法缩放位图。但它并没有解决 OutOfMemory 问题,因为无论如何都要解码完整的位图。
                          • 我会看看我是否可以查看我的旧代码,但我认为它确实解决了我的内存不足问题。将仔细检查我的旧代码。
                          • 至少在这个例子中,看起来你没有保留对完整位图的引用,因此节省了内存。
                          【解决方案22】:

                          使用这个bitmap.recycle(); 这有助于没有任何图像质量问题。

                          【讨论】:

                          • 根据API,不需要调用recycle()。
                          【解决方案23】:

                          您使用的图像似乎非常大。因此,由于堆内存已满,一些旧设备会发生崩溃。在旧设备(蜂蜜梳或 ICS 或任何低端型号设备)中尝试使用android:largeHeap="true" in应用程序标签下的清单文件或使用以下代码减小位图的大小。

                          Bitmap bMap;
                          BitmapFactory.Options options = new BitmapFactory.Options(); 
                          options.InSampleSize = 8;
                          bMap= BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);
                          

                          你也可以给出 4 或 12 或 16 来减小位图大小

                          【讨论】:

                            【解决方案24】:

                            known bug,不是因为文件太大。由于 Android 会缓存 Drawable,因此在使用少量图像后会出现内存不足。但是我找到了另一种方法,跳过了android默认的缓存系统。

                            解决方案: 将图片移动到“assets”文件夹并使用以下函数获取BitmapDrawable:

                            public static Drawable getAssetImage(Context context, String filename) throws IOException {
                                AssetManager assets = context.getResources().getAssets();
                                InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
                                Bitmap bitmap = BitmapFactory.decodeStream(buffer);
                                return new BitmapDrawable(context.getResources(), bitmap);
                            }
                            

                            【讨论】:

                              【解决方案25】:

                              我最近看到很多关于 OOM 异常和缓存的问题。开发人员指南对此有a really good article,但有些人往往无法以合适的方式实现它。

                              因此,我编写了一个示例应用程序来演示 Android 环境中的缓存。此实现尚未获得 OOM。

                              查看此答案的末尾以获取指向源代码的链接。

                              要求:

                              • Android API 2.1 或更高版本(我根本无法在 API 1.6 中为应用程序获取可用内存 - 这是唯一在 API 1.6 中不起作用的代码)
                              • Android support package

                              特点:

                              • 如果方向发生变化则保留缓存,使用单例
                              • 将分配的应用程序内存的八分之一用于缓存(根据需要进行修改)
                              • 大位图被缩放(你可以定义你想要允许的最大像素)
                              • 在下载位图之前控制有可用的互联网连接
                              • 确保每行只实例化一个任务
                              • 如果你要甩掉ListView,它根本不会下载它们之间的位图

                              这不包括:

                              • 磁盘缓存。无论如何,这应该很容易实现 - 只需指向一个从磁盘中获取位图的不同任务

                              示例代码:

                              正在下载的图像是来自 Flickr 的图像 (75x75)。但是,放置您想要处理的任何图像 url,如果超过最大值,应用程序将缩小它。在这个应用程序中,url 只是在一个 String 数组中。

                              LruCache 是处理位图的好方法。然而,在这个应用程序中,我将LruCache 的一个实例放在我创建的另一个缓存类中,以便让应用程序更可行。

                              Cache.java 的关键东西(loadBitmap() 方法是最重要的):

                              public Cache(int size, int maxWidth, int maxHeight) {
                                  // Into the constructor you add the maximum pixels
                                  // that you want to allow in order to not scale images.
                                  mMaxWidth = maxWidth;
                                  mMaxHeight = maxHeight;
                              
                                  mBitmapCache = new LruCache<String, Bitmap>(size) {
                                      protected int sizeOf(String key, Bitmap b) {
                                          // Assuming that one pixel contains four bytes.
                                          return b.getHeight() * b.getWidth() * 4;
                                      }
                                  };
                              
                                  mCurrentTasks = new ArrayList<String>();    
                              }
                              
                              /**
                               * Gets a bitmap from cache. 
                               * If it is not in cache, this method will:
                               * 
                               * 1: check if the bitmap url is currently being processed in the
                               * BitmapLoaderTask and cancel if it is already in a task (a control to see
                               * if it's inside the currentTasks list).
                               * 
                               * 2: check if an internet connection is available and continue if so.
                               * 
                               * 3: download the bitmap, scale the bitmap if necessary and put it into
                               * the memory cache.
                               * 
                               * 4: Remove the bitmap url from the currentTasks list.
                               * 
                               * 5: Notify the ListAdapter.
                               * 
                               * @param mainActivity - Reference to activity object, in order to
                               * call notifyDataSetChanged() on the ListAdapter.
                               * @param imageKey - The bitmap url (will be the key).
                               * @param imageView - The ImageView that should get an
                               * available bitmap or a placeholder image.
                               * @param isScrolling - If set to true, we skip executing more tasks since
                               * the user probably has flinged away the view.
                               */
                              public void loadBitmap(MainActivity mainActivity, 
                                      String imageKey, ImageView imageView,
                                      boolean isScrolling) {
                                  final Bitmap bitmap = getBitmapFromCache(imageKey); 
                              
                                  if (bitmap != null) {
                                      imageView.setImageBitmap(bitmap);
                                  } else {
                                      imageView.setImageResource(R.drawable.ic_launcher);
                                      if (!isScrolling && !mCurrentTasks.contains(imageKey) && 
                                              mainActivity.internetIsAvailable()) {
                                          BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
                                                  mainActivity.getAdapter());
                                          task.execute();
                                      }
                                  } 
                              }
                              

                              除非您想实现磁盘缓存,否则您不需要编辑 Cache.java 文件中的任何内容。

                              MainActivity.java 的关键内容:

                              public void onScrollStateChanged(AbsListView view, int scrollState) {
                                  if (view.getId() == android.R.id.list) {
                                      // Set scrolling to true only if the user has flinged the       
                                      // ListView away, hence we skip downloading a series
                                      // of unnecessary bitmaps that the user probably
                                      // just want to skip anyways. If we scroll slowly it
                                      // will still download bitmaps - that means
                                      // that the application won't wait for the user
                                      // to lift its finger off the screen in order to
                                      // download.
                                      if (scrollState == SCROLL_STATE_FLING) {
                                          mIsScrolling = true;
                                      } else {
                                          mIsScrolling = false;
                                          mListAdapter.notifyDataSetChanged();
                                      }
                                  } 
                              }
                              
                              // Inside ListAdapter...
                              @Override
                              public View getView(final int position, View convertView, ViewGroup parent) {           
                                  View row = convertView;
                                  final ViewHolder holder;
                              
                                  if (row == null) {
                                      LayoutInflater inflater = getLayoutInflater();
                                      row = inflater.inflate(R.layout.main_listview_row, parent, false);  
                                      holder = new ViewHolder(row);
                                      row.setTag(holder);
                                  } else {
                                      holder = (ViewHolder) row.getTag();
                                  }   
                              
                                  final Row rowObject = getItem(position);
                              
                                  // Look at the loadBitmap() method description...
                                  holder.mTextView.setText(rowObject.mText);      
                                  mCache.loadBitmap(MainActivity.this,
                                          rowObject.mBitmapUrl, holder.mImageView,
                                          mIsScrolling);  
                              
                                  return row;
                              }
                              

                              getView() 经常被调用。如果我们没有实施检查以确保我们不会在每行启动无限数量的线程,那么在此处下载图像通常不是一个好主意。 Cache.java 检查rowObject.mBitmapUrl 是否已经在一个任务中,如果是,它不会启动另一个。因此,我们很可能不会超出 AsyncTask 池中的工作队列限制。

                              下载:

                              您可以从https://www.dropbox.com/s/pvr9zyl811tfeem/ListViewImageCache.zip下载源代码。


                              遗言:

                              我已经测试了几个星期了,我还没有得到一个 OOM 异常。我已经在模拟器、Nexus One 和 Nexus S 上对此进行了测试。我已经测试了包含高清质量图像的图像 URL。唯一的瓶颈是下载需要更多时间。

                              我可以想象只有一种可能的情况会出现 OOM,那就是如果我们下载许多非常大的图像,并且在它们被缩放并放入缓存之前,会同时占用更多内存并导致哎呀。但这无论如何都不是一个理想的情况,而且很可能无法以更可行的方式解决。

                              报告 cmets 中的错误! :-)

                              【讨论】:

                                【解决方案26】:

                                我对 Fedor 的代码进行了小幅改进。它基本上做同样的事情,但没有(在我看来)丑陋的 while 循环,它总是导致 2 的幂。感谢 Fedor 提出最初的解决方案,我一直被困住,直到找到他的解决方案,然后我才能做出这个 :)

                                 private Bitmap decodeFile(File f){
                                    Bitmap b = null;
                                
                                        //Decode image size
                                    BitmapFactory.Options o = new BitmapFactory.Options();
                                    o.inJustDecodeBounds = true;
                                
                                    FileInputStream fis = new FileInputStream(f);
                                    BitmapFactory.decodeStream(fis, null, o);
                                    fis.close();
                                
                                    int scale = 1;
                                    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
                                        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
                                           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
                                    }
                                
                                    //Decode with inSampleSize
                                    BitmapFactory.Options o2 = new BitmapFactory.Options();
                                    o2.inSampleSize = scale;
                                    fis = new FileInputStream(f);
                                    b = BitmapFactory.decodeStream(fis, null, o2);
                                    fis.close();
                                
                                    return b;
                                }
                                

                                【讨论】:

                                • 是的,你是对的,虽然不是那么漂亮。我只是想让大家清楚。感谢您的代码。
                                • @Thomas Vervest - 该代码有一个大问题。 ^ 不会将 2 提高到幂,而是将结果与 2 相异。你想要 Math.pow(2.0, ...)。否则,这看起来不错。
                                • 哦,那是一个非常好的!我的不好,我会立即纠正,谢谢你的回复!
                                • 您正在创建两个新的 FileInputStream,一个用于每次调用 BitmapFactory.decodeStream()。您不必保存对它们中的每一个的引用,以便它们可以在 finally 块中关闭吗?
                                • @Babibu 文档没有说明该流已为您关闭,因此我认为它仍应关闭。可以在here 找到一个有趣且相关的讨论。请注意 Adrian Smith 的评论,它与我们的辩论直接相关。
                                【解决方案27】:

                                要修复 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;
                                            }
                                

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

                                【讨论】:

                                  【解决方案28】:

                                  您好,请访问链接http://developer.android.com/training/displaying-bitmaps/index.html

                                  或者只是尝试使用给定函数检索位图

                                  private Bitmap decodeBitmapFile (File f) {
                                      Bitmap bitmap = 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 ();
                                          }
                                  
                                          int scale = 1;
                                          for (int size = Math.max (o.outHeight, o.outWidth); 
                                              (size>>(scale-1)) > IMAGE_MAX_SIZE; ++scale);
                                  
                                          // Decode with input-stram SampleSize
                                          BitmapFactory.Options o2 = new BitmapFactory.Options ();
                                          o2.inSampleSize = scale;
                                          fis = new FileInputStream (f);
                                          try {
                                              bitmap  = BitmapFactory.decodeStream (fis, null, o2);
                                          } finally {
                                              fis.close ();
                                          }
                                      } catch (IOException e) {
                                      }
                                      return bitmap ;
                                  }
                                  

                                  【讨论】:

                                    【解决方案29】:

                                    这里的所有解决方案都需要设置 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% 为我提供了一个很好的缓冲区,而不会在执行图像旋转时耗尽内存。

                                    希望这对那里的人有所帮助..

                                    【讨论】:

                                      【解决方案30】:

                                      使用这个概念会帮助你,然后在图像视图上设置图像位图

                                      public static Bitmap convertBitmap(String path)   {
                                      
                                              Bitmap bitmap=null;
                                              BitmapFactory.Options bfOptions=new BitmapFactory.Options();
                                              bfOptions.inDither=false;                     //Disable Dithering mode
                                              bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
                                              bfOptions.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
                                              bfOptions.inTempStorage=new byte[32 * 1024]; 
                                      
                                      
                                              File file=new File(path);
                                              FileInputStream fs=null;
                                              try {
                                                  fs = new FileInputStream(file);
                                              } catch (FileNotFoundException e) {
                                                  e.printStackTrace();
                                              }
                                      
                                              try {
                                                  if(fs!=null)
                                                  {
                                                      bitmap=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
                                                  }
                                                  } catch (IOException e) {
                                      
                                                  e.printStackTrace();
                                              } finally{ 
                                                  if(fs!=null) {
                                                      try {
                                                          fs.close();
                                                      } catch (IOException e) {
                                      
                                                          e.printStackTrace();
                                                      }
                                                  }
                                              }
                                      
                                              return bitmap;
                                          }
                                      

                                      如果您想从高度和宽度为 60 和 60 的大图像制作小图像并快速滚动列表视图,请使用此概念

                                      public static Bitmap decodeSampledBitmapFromPath(String path, int reqWidth,
                                                  int reqHeight) {
                                      
                                              final BitmapFactory.Options options = new BitmapFactory.Options();
                                              options.inJustDecodeBounds = true;
                                              BitmapFactory.decodeFile(path, options);
                                      
                                              options.inSampleSize = calculateInSampleSize(options, reqWidth,
                                                      reqHeight);
                                      
                                              // Decode bitmap with inSampleSize set
                                              options.inJustDecodeBounds = false;
                                              Bitmap bmp = BitmapFactory.decodeFile(path, options);
                                              return bmp;
                                              }
                                      
                                          public static int calculateInSampleSize(BitmapFactory.Options options,
                                                  int reqWidth, int reqHeight) {
                                      
                                              final int height = options.outHeight;
                                              final int width = options.outWidth;
                                              int inSampleSize = 1;
                                      
                                              if (height > reqHeight || width > reqWidth) {
                                                  if (width > height) {
                                                      inSampleSize = Math.round((float) height / (float) reqHeight);
                                                  } else {
                                                      inSampleSize = Math.round((float) width / (float) reqWidth);
                                                   }
                                               }
                                               return inSampleSize;
                                              }
                                      

                                      希望对你有很大帮助。

                                      您可以从开发者网站Here获得帮助

                                      【讨论】:

                                        猜你喜欢
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 2011-06-20
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 2019-08-11
                                        • 1970-01-01
                                        相关资源
                                        最近更新 更多