【问题标题】:Max size image to avoid OutOfMemoryException on Android在 Android 上避免 OutOfMemoryException 的最大尺寸图像
【发布时间】:2014-12-05 14:44:09
【问题描述】:

我发现很难处理 OutOfMemoryExceptions 在 Android 中很难管理。 目前,我总是先在 PhotoShop 中调整图像大小,然后检查不同的设备和模拟器,看看我能突破多远。

我当前应用程序的目标是输入图像,在其上映射文本和图像并将其保存为 PDF。 现在我正在处理一个大小约为 620x842 和 250kb 的图像,并且输出不够好。 原件是 2480x3368 和 482kb。

毫无疑问,使用原始版本会导致 OutOfMemoryExceptions。 但我想知道如何关闭我才能回到原来的位置。

对此有何想法或提示?

【问题讨论】:

标签: android bitmap out-of-memory


【解决方案1】:

原件是 2480x3368 和 482kb

您混淆了图像大小和文件大小。 482KB为文件大小,为压缩图片。

2480x3368 在 32 位 ARGB 中实际上是 33MB。 (对于内存中的单个对象来说,一次分配很多)

文件大小与图片占用内存大小无关。

注意:这有点离题,可能应该是评论,但对于评论格式来说太长了。

【讨论】:

    【解决方案2】:

    您是否根据google guide 解码文件?这为我解决了 outOfMemoryException 的问题。现在我可以轻松解码文件,例如 2676x3326 在银河 SII 上

    【讨论】:

    • 这应该是一条评论
    【解决方案3】:

    您可以在调用 BitmapFactory.decodeFile(String,Options) 时使用 Bitmap.Options

    通过设置 inSampleSize=2,你可以得到维度除以 2 的结果位图。

    【讨论】:

      【解决方案4】:

      你可以试试这个:

       <activity
                  android:name="..."
                  android:configChanges="orientation|keyboardHidden"
                  android:hardwareAccelerated="true"
                  android:label="@string/app_name"
                  android:largeHeap="true" >
              </activity>
      

      有时有效,有时无效。请记住,此尺寸是特定于设备的,因此如果您希望您的应用程序公开,您应该将图像调整为所有需要的尺寸。我最近看到的最低的是三星银河成名。它的 heep 不会加载大于 2048x2048 的图像。

      如果您想加载大量图像并且内存不足,您可以尝试使用第三方库,例如:http://square.github.io/picasso/。它对我来说效果很好,可以处理 android 原生的所有位图问题。

      真正的问题是图片的大小。即使图片大小为 300kb 或 400kb,当您将其放大以适应屏幕尺寸时,它也可能会变成令人难以置信的大位图,例如 20 或 30 mb,这将导致此线程的堆溢出。

      【讨论】:

        【解决方案5】:

        您可以使用 BitmapFactory 来获取图像的边界,然后加载适合您当前内存的较小版本的图像:

             /**
             * Decodes the given inputstream into a Bitmap, scales the bitmap to the
             * required height and width if image dimensions are bigger. Note: the
             * decoder will try to fulfill this request, but the resulting bitmap may
             * have different dimensions that precisely what has been requested.
             *
             * @param _inputStreamJustDecode The input stream for bound decoding
             * @param _inputStreamData The input stream for image decoding
             * @param _preview true to generate a preview image, false to generate a high quality image
             * @return
             */
            private Bitmap decodeSampledBitmapFromResource(InputStream _inputStreamJustDecode,InputStream _inputStreamData) {
        
                // First decode with inJustDecodeBounds=true to check dimensions
                final BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(_inputStreamJustDecode, null, options);
                float imageSize = options.outWidth * options.outHeight * 4;
                float scaleFactor = 1;
                long availableMem = getAvailableMemory(mContext);
        
                //new image should not use more than 66% of the available memory
                availableMem*=0.66f;
                if(imageSize > availableMem){
                    scaleFactor = (float)availableMem / imageSize;
                }
        
                float maxDimen = options.outWidth > options.outHeight ? options.outWidth : options.outHeight;
        
                //Don't let the image get to big.
                if(maxDimen * scaleFactor > Constant.MAX_TEXTURE_SIZE ){
                    scaleFactor = Constant.MAX_TEXTURE_SIZE/maxDimen;
                }
        
                // Calculate inSampleSize
                options.inSampleSize = calculateInSampleSize(options, options.outWidth*scaleFactor,
                        options.outHeight*scaleFactor);
        
                // Decode bitmap with inSampleSize set
                options.inJustDecodeBounds = false;
                // set other options
                options.inPurgeable = true;
                options.inPreferredConfig = Bitmap.Config.RGB_565;
                return BitmapFactory.decodeStream(_inputStreamData,null,options);
            }
        
             /**
             * Returns the currently available memory (ram) in bytes.
             * @param _context The context.
             * @return The available memory in bytes.
             */
            public long getAvailableMemory(Context _context){
                ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
                ActivityManager activityManager = (ActivityManager) _context.getSystemService(Activity.ACTIVITY_SERVICE);
                activityManager.getMemoryInfo(mi);
                long availableMegs = mi.availMem;
                return availableMegs;
            }
        

        注意:两个输入流都来自同一个资源,但由于在解码边界后无法回退流,因此需要一个新流。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-10-14
          • 1970-01-01
          • 2012-03-19
          • 2017-01-02
          • 2013-01-17
          • 2011-05-02
          • 1970-01-01
          相关资源
          最近更新 更多