高分辨率图像 - 内存溢出错误

15

我正在为Galaxy S4开发一个应用程序。 应用程序的要求之一是具有启动屏幕,其中包含一个1920x1080像素的图像。这是一张高质量的.jpeg图片,其大小约为2兆字节

问题在于,只要我启动应用程序,就会出现OutOfMemoryError错误。我感到非常震惊,因为这个图像仅有2兆字节的大小?我该如何解决这个问题并显示该图像?

更改图像的尺寸或大小不是一个选项。

SplashScreen.java

public class Splashscreen extends Activity {

private static final int SPLASH_DURATION = 2000;
private boolean isBackPressed = false; 

@Override
protected void onCreate(Bundle savedInstanceState) {

    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(LayoutParams.FLAG_FULLSCREEN, LayoutParams.FLAG_FULLSCREEN);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.splashscreen);

    Handler h = new Handler();

    h.postDelayed(new Runnable() {

        @Override
        public void run() {

            // check if the backbutton has been pressed within the splash_duration
            if(!isBackPressed) { 

                Intent i = new Intent(Splashscreen.this, MainActivity.class);
                i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                Splashscreen.this.startActivity(i);
                overridePendingTransition(R.anim.short_fade_in, R.anim.short_fade_out);
            }       

            finish();
        }
    }, SPLASH_DURATION);
}

@Override
public void onBackPressed() {

    isBackPressed = true;
    super.onBackPressed();
}
}

还有splashscreen.xml

    <ImageView 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/ivSplashScreenImage"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:scaleType="fitXY"
        android:src="@drawable/high_res_splashscreen_image"/>

额外信息:

有时候,(当设备内存很充足)应用程序能够通过闪屏界面,但是之后,应用程序的内存消耗非常高(约100兆字节)即使我关闭了 SplashScreen 活动并且使用 finish() 结束它,似乎仍然 有一个对 ImageView/图像的引用保存在内存中。

如何减少大量的内存消耗?

如果我不显示闪屏界面,我的应用程序只消耗约35MB的内存。但是使用闪屏图片后就达到了约100MB。


请参考以下链接:https://dev59.com/puo6XIcBkEYKwwoYTzNK - Ritesh Gune
你的res文件夹里有很多图片文件吗? - Arun C
是的,我有大约40个图像文件。但它们并不是很大(大多数只是图标),大小在10到50kb之间。 - Philipp Jahoda
2个回答

15

以下三个提示可能会有所帮助:

  1. 使用这个方法来加载您的图像,参见 Android大位图加载文档

    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) {
    
            // 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;
        }
    
        return inSampleSize;
    }
    
  2. 确保您的Bitmap只有一个实例在内存中。在显示后,调用recycle()并将引用设置为null。您可以使用Memory Allocation Tracker查看分配情况。您也可以读取HPROF文件(如评论中所建议)。

  3. 默认情况下使用ARGB_8888像素格式,这意味着每个像素有4个字节。非常好的文章:Bitmap quality, banding and dithering。您的图像是JPEG格式,因此它没有透明度,因此每个像素上的alpha通道浪费了1个字节。可能不太可能,但是也许您可以使用更经济的格式来获得可接受的质量。看看它们。例如,也许可以使用RGB_565格式。它每个像素需要2个字节,因此图像会轻50%。您可以启用抖动以改善RGB_565的质量。


谢谢,我会尝试的。请查看我的更新答案和“附加信息”。 - Philipp Jahoda
1
我在.xml文件中将图像资源设置为"@null",现在像这样加载图像:"((ImageView) findViewById(R.id.ivSplashScreenImage)).setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.drawable.highres, 1920, 1080));" 我现在能够跳过启动画面,但我的应用程序内存消耗仍然约为90MB。(没有启动画面,它是35-40mb) - Philipp Jahoda
1
理论上,大小为1920x1080的位图应该只有大约8MB。您能否查看HPROF转储以确保您只分配了其中一个位图?现在,您手动加载图像后,应确保在闪屏关闭后回收它并将其引用设置为null。Android应用程序的内存分析 - Dane White
抱歉,改变图像以压缩它不是一个可行的选择 :( 然而,回收利用完成了任务。非常感谢您的帮助! - Philipp Jahoda

7
我曾遇到类似的问题,解决方法很简单。将你的高分辨率1920x1080像素图片放在drawable-nodpi目录中。不论当前屏幕密度如何,系统不会缩放带有该限定符标记的资源,这会导致OutOfMemoryError,所以问题应该就可以消失了。
请查看Android文档:http://developer.android.com/intl/es/guide/practices/screens_support.html 希望能对你有所帮助。

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