即使我解码图像时OOM错误

时间:2016-09-29 07:01:19

标签: android android-imageview

我在将图像加载到某些设备的屏幕上时遇到问题。

所有屏幕都有一张图片,但图片相当高。

以下是图片尺寸

人像:

  • mdpi:360x1074
  • hdpi:540x1611
  • xhdpi:720x2148
  • xxhdpi:1080x3142

风景:

  • mdpi:640x705
  • hdpi:960x1058
  • xhdpi:1280x1410
  • xxhdpi:1920x1035

我已关注:https://developer.android.com/training/displaying-bitmaps/load-bitmap.htmlhttp://successmatics.com/blog/loading-large-bitmaps-in-android-ui/但它们基本相同。

如果我加载一个更小的图像,说一个其他的缩略图,它加载正常。

将图像加载到oncreate

mImageView.setImageBitmap(
   ImageUtils.decodeSampledBitmapFromResource(getResources(),
   R.drawable.image,
   ImageUtils.getScreenWidth(getApplicationContext()),
   ImageUtils.getScreenHeight(getApplicationContext())));
}

用于解码图像的方法(来自上面的2个网址)

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

  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) {
//--------i tried a sample size of 2. was hoping 3 would work---------//
        inSampleSize *= 3;
      }
    }

    return inSampleSize;
  }

  public static int getScreenWidth(Context context) {
    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
    return displayMetrics.widthPixels;
  }

  public static int getScreenHeight(Context context) {
    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
    return displayMetrics.heightPixels;
  }

有没有人有更好的解码大图像的方法,所以我没有得到OOM

我的代码有问题(来自android本身)

编辑:抱歉忘了堆栈跟踪:

E/dalvikvm-heap: Out of memory on a 10391232-byte allocation.
    I/dalvikvm: "main" prio=5 tid=1 RUNNABLE
    I/dalvikvm:   | group="main" sCount=0 dsCount=0 obj=0xb264b450 self=0xb8d604e0
    I/dalvikvm:   | sysTid=26630 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1216986048
    I/dalvikvm:   | schedstat=( 0 0 0 ) utm=117 stm=69 core=0
    I/dalvikvm:     at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    I/dalvikvm:     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:500)
    I/dalvikvm:     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:353)
    I/dalvikvm:     at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:781)
    I/dalvikvm:     at android.content.res.Resources.loadDrawable(Resources.java:1935)
    I/dalvikvm:     at android.content.res.TypedArray.getDrawable(TypedArray.java:601)
    I/dalvikvm:     at android.widget.ImageView.<init>(ImageView.java:120)
    I/dalvikvm:     at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:57)
    I/dalvikvm:     at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:53)
    I/dalvikvm:     at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:106)
    I/dalvikvm:     at android.support.v7.app.AppCompatDelegateImplV7.createView(AppCompatDelegateImplV7.java:980)
    I/dalvikvm:     at android.support.v7.app.AppCompatDelegateImplV7.onCreateView(AppCompatDelegateImplV7.java:1039)
    I/dalvikvm:     at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:44)
    I/dalvikvm:     at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:675)
    I/dalvikvm:     at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
    I/dalvikvm:     at android.view.LayoutInflater.rInflate(LayoutInflater.java:749)
    I/dalvikvm:     at android.view.LayoutInflater.rInflate(LayoutInflater.java:749)
    I/dalvikvm:     at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
    I/dalvikvm:     at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
    I/dalvikvm:     at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
    I/dalvikvm:     at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:280)
    I/dalvikvm:     at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:140)
    I/dalvikvm:     at com.myapp.app.item.protection.Protection.onCreate(Protection.java:31)
    I/dalvikvm:     at android.app.Activity.performCreate(Activity.java:5008)
    I/dalvikvm:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
    I/dalvikvm:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
    I/dalvikvm:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
    I/dalvikvm:     at android.app.ActivityThread.access$600(ActivityThread.java:130)
    I/dalvikvm:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
    I/dalvikvm:     at android.os.Handler.dispatchMessage(Handler.java:99)
    I/dalvikvm:     at android.os.Looper.loop(Looper.java:137)
    I/dalvikvm:     at android.app.ActivityThread.main(ActivityThread.java:4745)
    I/dalvikvm:     at java.lang.reflect.Method.invokeNative(Native Method)
    I/dalvikvm:     at java.lang.reflect.Method.invoke(Method.java:511)
    I/dalvikvm:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    I/dalvikvm:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    I/dalvikvm:     at dalvik.system.NativeStart.main(Native Method)

我目前使用的唯一图像加载库是Volley用于联网图像。这是@Drawable文件中的资源。有2个版本,肖像和风景(不要拉伸)

由于

3 个答案:

答案 0 :(得分:0)

替换此行

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

它将解决您的问题

options.inSampleSize = 8;

答案 1 :(得分:0)

这是我通常用于计算 inSampleSize

的方法
private int calculateInSampleSize(BitmapFactory.Options opts, int reqWidth, int reqHeight) {
    int outHeight = opts.outHeight;
    int outWidth = opts.outWidth;
    int inSampleSize = 1;
    while (outHeight > reqHeight || outWidth > reqWidth) {
        outHeight /= 2;
        outWidth /= 2;
        inSampleSize *= 2;
    }
    return inSampleSize;
}

如果您希望您的位图适合您的ImageView,您可以在xml中使用scaleType,如:

<ImageView
    android:scaleType="centerCrop"
    android:id="@+id/test_iv"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

如果您不想自己解码图像,可能需要使用某些第三方库,如Glide

答案 2 :(得分:0)

  

我的代码有问题(来自android   本身)

没有错(从某种意义上讲,它并没有发生故障)。它正在做的事情。

  

有没有人有更好的解码大图像的方法,所以我没有   OOM

Ammm ..我没有,但我可以告诉你在哪里可以做出改变来使这项工作。

用于计算下采样索引的条件存在问题。我会用你问题中给出的例子来解释它。

Configuration landscape

XXHDPI qhd device with dimention 960x540 hence,

    deviceWidth 960  deviceHeight 540 
    halfWidth 960    halfHeight 517  
    imgWidth 1920    imgHeight 1035

设备宽度是此处所需的宽度,

reqWidth 960  reqHeight 540     

您使用的条件是

 while ((halfHeight / inSampleSize) >= reqHeight
              && (halfWidth / inSampleSize) >= reqWidth) {
//--------i tried a sample size of 2. was hoping 3 would work---------//
        inSampleSize *= 3;
      }

如果我们只是设置参数就变成

while ((517 / 1) >= 540
              && (960 / 1) >= 960) {
//--------i tried a sample size of 2. was hoping 3 would work---------//
        inSampleSize *= 3;
      }

正如您所看到的,第一个条件不满足,因此当循环被破坏且inSampleSize保持为1时,意味着图像不会被重新采样。这可能会导致OOM。

这个想法是,当你走向具有更高纵横比的图像时,它更有可能在给定条件下得到OOM,

e.g。 , 尺寸为2160 x 1035的图像更有可能导致您的应用崩溃(注意:此图片不会因同样的原因重新采样)。

尺寸为4320 x 1035的图片肯定会让你的应用程序崩溃(注意:这不会重新采样)

1035 x 4320(肖像案例)也是如此。

那要改变什么?    应修改条件以使其执行此类操作,

Get the large side of image, down sample it until it fits adjacent device side

我会留给你改变条件,因为你看起来很合适。

UPLDATE:

我想在this回答的评论中说OP已经提到了一个真正展示你所寻找的要点的链接。通过将位图配置设置为RGB_565(每个像素使用16位)(RGB_888使用24位)来减小给定位图的大小,它也会更加努力。

 scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565);

请注意,这会降低图像质量。