【发布时间】:2016-01-05 19:01:41
【问题描述】:
我想在安卓设备上制作一个虚拟宠物,但我在加载所有精灵表时遇到了问题(这些是 .png 文件)。我可以加载一定数量的纸张而不会崩溃,但不是全部。当然这是因为内存不足。这些图像每个都小于 650 kB(每个图像的分辨率大约为 200x6300),所以我想我做错了什么。我的问题:“如何加载所有这些图像而不会因内存不足异常而崩溃?”
我在单例类中加载图像。此类负责应用程序所需的所有资源。这是我用来加载图像的代码。
public final class ResourceManager {
public static ResourceManager INSTANCE;
private int screenWidth, screenHeight;
private float dpscreenWidth, dpscreenHeight;
private DisplayMetrics metrics;
public Bitmap testSheet;
public Bitmap flapdogBackground;
public Bitmap flapdog_head;
public SpriteSheet dogBarking, dogHappy, dogPlayfull, dogSad;
public ResourceManager(Activity a){
DisplayMetrics metrics = new DisplayMetrics();
a.getWindowManager().getDefaultDisplay().getMetrics(metrics);
this.metrics = metrics;
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
dpscreenWidth = screenWidth/metrics.density;
dpscreenHeight = screenHeight/metrics.density;
R.drawable.dead_normal);
initBitmaps(a.getResources());
flapdogBackground = getResizedBitmap(BitmapFactory.decodeResource(a.getResources(), R.drawable.flapdog_bg), screenWidth, screenHeight);
int flapdog_width = (int)getPercentageLength(10, true);
flapdog_head = getResizedBitmap(BitmapFactory.decodeResource(a.getResources(), R.drawable.flapdog_head), flapdog_width, flapdog_width);
log();
INSTANCE = this;
}
private void initBitmaps(Resources r) {
Bitmap b;
double height = getPercentageLength(50, true);
Log.e("heihgt", "Height is: "+height);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inDither = true;
b = BitmapFactory.decodeResource(r, R.drawable.dog_happy_f30, options);
dogHappy = new SpriteSheet(getResizedBitmap(b, (int)(b.getWidth()*(height*30)/b.getHeight()), (int) (height*30)), 30, false);
b.recycle();
b = BitmapFactory.decodeResource(r, R.drawable.dog_barking_f30, options);
dogBarking = new SpriteSheet(getResizedBitmap(b, (int)(b.getWidth()*(height*30)/b.getHeight()), (int) (height*30)), 30, false);
b.recycle();
b = BitmapFactory.decodeResource(r, R.drawable.dog_playfull_f30, options);
dogPlayfull = new SpriteSheet(getResizedBitmap(b, (int)(b.getWidth()*(height*30)/b.getHeight()), (int) (height*30)), 30, false);
b.recycle();
b = BitmapFactory.decodeResource(r, R.drawable.dog_sad_f30, options);
dogSad = new SpriteSheet(getResizedBitmap(b, (int)(b.getWidth()*(height*30)/b.getHeight()), (int) (height*30)), 30, false);
}
public float convertDpToPixel(float dp){
return dp * (metrics.densityDpi / 160f);
}
public float convertPixelsToDp(float px){
return px / (metrics.densityDpi / 160f);
}
public double getPercentageLength(double percentage, boolean height){
if(height){
return (double)screenHeight*(percentage/100);
}else{
return (double)screenWidth*(percentage/100);
}
}
private void log(){
String s = "Recoursemanager initialized. \nscreenwidth: "+screenWidth + "\nscreenheight: "
+screenHeight+"\ndpscreenwidth: "+dpscreenWidth+"\ndpscreenheight: "+dpscreenHeight;
Log.d("RecourceManager", s);
}
public static Bitmap getResizedBitmap(Bitmap bm, int newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
bm.recycle();
return resizedBitmap;
}
public int getScreenWidth() {
return screenWidth;
}
public int getScreenHeight(){
return screenHeight;
}
}
(这似乎是一种调整图像大小的愚蠢方法,因为 android 系统本身提供了从 mdpi hpdi 等文件夹加载适当大小的图像的功能。不知何故,我无法让它工作,这就是我尝试这种方法的原因。)
这是错误信息:
01-05 19:49:32.363 25711-25711/example.com.virtualpet I/art: Alloc partial concurrent mark sweep GC freed 18(704B) AllocSpace objects, 0(0B) LOS objects, 3% free, 421MB/437MB, paused 366us total 6.317ms
01-05 19:49:32.383 25711-25711/example.com.virtualpet I/art: Alloc concurrent mark sweep GC freed 13(12KB) AllocSpace objects, 0(0B) LOS objects, 3% free, 421MB/437MB, paused 396us total 18.615ms
01-05 19:49:32.383 25711-25711/example.com.virtualpet I/art: Forcing collection of SoftReferences for 140MB allocation
01-05 19:49:32.403 25711-25711/example.com.virtualpet I/art: Alloc concurrent mark sweep GC freed 11(344B) AllocSpace objects, 0(0B) LOS objects, 3% free, 421MB/437MB, paused 396us total 18.066ms
01-05 19:49:32.423 25711-25711/example.com.virtualpet E/art: Throwing OutOfMemoryError "Failed to allocate a 147456012 byte allocation with 16777120 free bytes and 90MB until OOM"
01-05 19:49:32.423 25711-25711/example.com.virtualpet D/AndroidRuntime: Shutting down VM
01-05 19:49:32.443 25711-25711/example.com.virtualpet E/AndroidRuntime: FATAL EXCEPTION: main
Process: example.com.virtualpet, PID: 25711
java.lang.OutOfMemoryError: Failed to allocate a 147456012 byte allocation with 16777120 free bytes and 90MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:939)
at android.graphics.Bitmap.createBitmap(Bitmap.java:912)
at android.graphics.Bitmap.createBitmap(Bitmap.java:843)
at example.com.virtualpet.Util.ResourceManager.getResizedBitmap(ResourceManager.java:111)
at example.com.virtualpet.Util.ResourceManager.initBitmaps(ResourceManager.java:68)
at example.com.virtualpet.Util.ResourceManager.<init>(ResourceManager.java:41)
at example.com.virtualpet.MainActivity.onCreate(MainActivity.java:31)
at android.app.Activity.performCreate(Activity.java:6289)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2655)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2767)
at android.app.ActivityThread.access$900(ActivityThread.java:177)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1449)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5951)
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:1388)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183)
Activity 类:
public class MainActivity extends Activity {
private DogView view;
private boolean inGame = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new ResourceManager(this);
}
}
我读过关于软链接的文章,但也读到这不是解决这个问题的正确方法。就我而言,bitmap.recycle() 似乎也无济于事,因为我需要所有位图。
附言。抱歉英语不好,希望你能理解我的问题。
编辑:
忘了提到我已经在 android 清单文件中添加了 android:largeheap=true。
编辑2: 答案中提到并非同时需要所有图像。这是正确的,所以更好的问题是如何在实际使用图像之前快速高效地加载这些图像。
回答: 我开始在脑海中对您的 cmets 进行更多思考,并提出了一个下降(满足我的需要)解决方案:不要同时加载所有图像,而仅在我需要时加载。这种方法的缺点是图像不会立即改变,而是在不到一秒钟之后改变。感谢您的宝贵时间。
【问题讨论】:
-
Failed to allocate a 147456012 byte allocation...嗯,147+ MB 确实是一大堆内存
标签: java android bitmap out-of-memory