设置视图背景会导致OutOfMemoryError

时间:2015-09-27 21:55:33

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

我只是想改变应用程序的背景图像,它还没有任何内容,我只是想将主要活动的背景更改为图像。 它在预览中正确显示,但是当我在模拟器或物理设备上运行时,我收到此错误。

  

抛出OutOfMemoryError"无法分配604786188字节   分配4194208个空闲字节和230MB直到OOM"

图像小于1.5MB,所以我不知道为什么会耗尽内存或试图分配那么多。

我所做的只是打开一个空白项目并更改相对布局的背景。

奇怪的是,如果我在nexus 4模拟器上运行应用程序它工作正常,但即使在Android上的相同版本上的nexus 5也会引发此错误。

修改

不再出现上述错误,而是在启动应用时发生以下错误,无论使用何种模拟器。

FATAL EXCEPTION: main
    Process: com.jacksteel.comp4, PID: 2164
    java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.jacksteel.comp4/com.jacksteel.comp4.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.Window.findViewById(int)' on a null object reference
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2236)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2390)
            at android.app.ActivityThread.access$800(ActivityThread.java:151)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5257)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.Window.findViewById(int)' on a null object reference
            at android.app.Activity.findViewById(Activity.java:2072)
            at com.jacksteel.comp4.MainActivity.<init>(MainActivity.java:20)
            at java.lang.reflect.Constructor.newInstance(Native Method)
            at java.lang.Class.newInstance(Class.java:1606)
            at android.app.Instrumentation.newActivity(Instrumentation.java:1066)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2226)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2390)
            at android.app.ActivityThread.access$800(ActivityThread.java:151)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5257)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

编辑2 源代码

 package com.jacksteel.comp4;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.RelativeLayout;


 public class MainActivity extends AppCompatActivity {
     private RelativeLayout Bg = (RelativeLayout) findViewById(R.id.MainBg);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Bitmap bitmap = decodeSampledBitmapFromResource(getResources(),R.drawable.redboxes,Bg.getWidth(), Bg.getHeight());
        Resources res = getResources();
        BitmapDrawable backgroundDrawable = new BitmapDrawable(res, bitmap);
        Bg.setBackgroundDrawable(backgroundDrawable);
    }


     @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

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

编辑3 更新

更新了源代码

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final RelativeLayout Bg = (RelativeLayout) findViewById(R.id.MainBg);

    Bg.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            Bg.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.redboxes, Bg.getWidth(), Bg.getHeight());
            Resources res = getResources();
            BitmapDrawable backgroundDrawable = new BitmapDrawable(res, bitmap);
            Bg.setBackgroundDrawable(backgroundDrawable);
        }
    });

}

更新错误

FATAL EXCEPTION: main
    Process: com.jacksteel.comp4, PID: 1872
    java.lang.OutOfMemoryError: Failed to allocate a 107347980 byte allocation with 1048576 free bytes and 63MB until OOM
            at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
            at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
            at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
            at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
            at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:467)
            at com.jacksteel.comp4.MainActivity.decodeSampledBitmapFromResource(MainActivity.java:93)
            at com.jacksteel.comp4.MainActivity$1.onGlobalLayout(MainActivity.java:27)
            at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:912)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1881)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1061)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5885)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
            at android.view.Choreographer.doCallbacks(Choreographer.java:580)
            at android.view.Choreographer.doFrame(Choreographer.java:550)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5257)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

1 个答案:

答案 0 :(得分:4)

<强>更新

您应该将要加载BitmapFactory.decodeResource()的图像放到drawable-nodpi目录中,以避免将drawable-<density>目录名中使用的密度缩放到设备屏幕密度。

在您的情况下,您将图片放在drawable-hdpi目录中,并且您的设备有xxhdpi屏幕,因此BitmapFactory.decodeResource() inSampleSize=2图片的图片放大了2倍(来自hdpi } xxhdpi)和2x缩小(按inSampleSize),保持原始大小并导致应用程序崩溃OutOfMemoryError

原始回答

1.5MB是压缩图像的大小。将其加载到ImageView或其他视图的背景时,会隐式创建Bitmap。在Bitmap中,图像的每个像素都需要4个字节。因此,如果你的图像分辨率为4096x4096像素,那么内存需要64兆,尽管图像可以用单色填充,只需要几千字节压缩到png或jpeg。

您应该确定视图的实际大小并加载缩放图像:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            Bitmap bitmap = ImageUtils.decodeSampledBitmapFromFile(backgroundImageFile, view.getWidth(), view.getHeight());
            view.setBackground(new BitmapDrawable(getResources(), bitmap));
        }
    });

我的实用工具方法是从Bitmap加载缩放File

public class ImageUtils {
    public static Bitmap decodeSampledBitmapFromFile(File file, int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(file.getAbsolutePath(), options);

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

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(file.getAbsolutePath(), 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.floor((float) height / (float) reqHeight);
            final int widthRatio = Math.floor((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;
    }
}

您可以使用其他BitmapFactory方法从流,资源和字节数组中解码Bitmap

相关问题