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