Android 设置视图背景会导致OutOfMemory错误
我只是想更改应用程序的背景图像,它还没有任何内容,我只是想将主要活动的背景更改为图像。 它在预览中正确显示,但当我在模拟器或物理设备上运行它时,会出现此错误 抛出OutOfMemoryError“无法分配604786188字节 分配4194208个可用字节和230MB,直到OOM“ 该图像小于1.5MB,因此我不知道它为什么会耗尽内存或试图分配这么多 我所做的只是打开一个空白项目并更改相对布局的背景 奇怪的是,如果我在Nexus4模拟器上运行该应用程序,它可以正常工作,但即使在android上的同一版本上运行Nexus5,也会抛出此错误 编辑 不再获取上述错误,而是在应用程序启动时发生以下错误,无论使用的是什么模拟器Android 设置视图背景会导致OutOfMemory错误,android,image,android-studio,out-of-memory,Android,Image,Android Studio,Out Of Memory,我只是想更改应用程序的背景图像,它还没有任何内容,我只是想将主要活动的背景更改为图像。 它在预览中正确显示,但当我在模拟器或物理设备上运行它时,会出现此错误 抛出OutOfMemoryError“无法分配604786188字节 分配4194208个可用字节和230MB,直到OOM“ 该图像小于1.5MB,因此我不知道它为什么会耗尽内存或试图分配这么多 我所做的只是打开一个空白项目并更改相对布局的背景 奇怪的是,如果我在Nexus4模拟器上运行该应用程序,它可以正常工作,但即使在android上的
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)
编辑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)
更新 您应该将要加载的图像与
BitmapFactory.decodeResource()
一起放入drawable nodpi
目录,以避免从drawable-
目录名中使用的密度缩放到设备屏幕密度
在您的情况下,您将图像放在可绘制hdpi
目录中,并且您的设备具有xxhdpi
屏幕,因此在BitmapFactory.decodeResource()
中inSampleSize=2
图像放大了2倍(从hdpi
到xxhdpi
),缩小了2倍(通过inSampleSize
)保持原始大小,并通过OutOfMemoryError
导致应用程序崩溃
原始答案
1.5MB是压缩图像的大小。当您将其加载到ImageView
或某个其他视图的背景时,将隐式创建位图
。在位图中
图像的每个像素占用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));
}
});
我的实用方法是从文件
加载缩放的位图
:
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;
}
}
公共类ImageUtils{
公共静态位图解码SampledBitMapFromFile(文件文件文件,int-reqWidth,int-reqHeight){
//使用INJUSTDECBOUNDS首次解码=true检查尺寸
final BitmapFactory.Options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(file.getAbsolutePath(),选项);
//计算样本大小
options.inSampleSize=计算样本大小(options、reqWidth、reqHeight);
//使用inSampleSize集合解码位图
options.inJustDecodeBounds=false;
返回BitmapFactory.decodeFile(file.getAbsolutePath(),options);
}
公共静态int-calculateInSampleSize(BitmapFactory.Options选项、int-reqWidth、int-reqHeight){
//图像的原始高度和宽度
最终内部高度=options.outHeight;
最终整数宽度=options.outWidth;
int inSampleSize=1;
如果(高度>要求高度| |宽度>要求宽度){
//计算高度和宽度与所需高度和宽度的比率
最终内部高度比=数学地板((浮动)高度/(浮动)要求高度);
最终内部宽度比=数学地板((浮动)宽度/(浮动)宽度);
//选择最小比率作为采样值,这将保证
//最终图像的两个尺寸均大于或等于
//要求的高度和宽度。
inSampleSize=高度比<宽度比?高度比:宽度比;
}
返回样本大小;
}
}
您可以使用其他
位图工厂
方法从流、资源和字节数组中解码位图。更新
您应该将要加载的图像与BitmapFactory.decodeResource()
一起放入drawable nodpi
目录,以避免从drawable-
目录名中使用的密度缩放到设备屏幕密度
在您的情况下,您将图像放在可绘制hdpi
目录中,并且您的设备具有xxhdpi
屏幕,因此在BitmapFactory.decodeResource()
中inSampleSize=2
图像放大了2倍(从hdpi
到xxhdpi
),缩小了2倍(通过inSampleSize
)保持原始大小,并通过OutOfMemoryError
导致应用程序崩溃
原始答案
1.5MB是压缩图像的大小。当您将其加载到ImageView
或某个其他视图的背景时,将隐式创建位图
。在位图中
图像的每个像素占用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));
}
});
我的实用方法是从文件
加载缩放的位图
:
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;
}
}
公共类ImageUtils{
公共静态位图解码SampledBitMapFromFile(文件文件文件,int-reqWidth,int-reqHeight){
//使用INJUSTDECBOUNDS首次解码=true检查尺寸
final BitmapFactory.Options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(file.getAbsolutePath(),选项);
//计算样本大小
options.inSampleSize=计算样本大小(options、reqWidth、reqHeight);
//使用inSampleSize s解码位图