我想在Android设备上制作虚拟宠物,但是我在加载所有spritesheets(这些是.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)
活动类:
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:largeheap=true
添加到了android清单文件中。
EDIT2: 在答案中提到,并非所有图像都需要在同一时刻。这是正确的,所以一个更好的问题是如何在实际使用图像之前有效和快速地加载这些图像。
解答: 我开始更多地考虑你在脑海中的评论,并提出了一个下降(根据我的需要)解决方案:不是同时加载所有图像,而是仅在我需要它时。这种方法的缺点是图像不会立即改变,但是在不到一秒钟之后。谢谢你的时间。
答案 0 :(得分:1)
首先在清单文件中添加此应用内标android:largeheap=true
,然后使用universal image loader
或picasso
获取图片。不用担心,这些库管理您的图像不会导致崩溃。 picasso和Universal Image Loader。
答案 1 :(得分:0)
您应该尝试inJustDecodeBounds。这样可以帮助您在不将内容加载到内存中的情况下获取图像的大小而不进行缩放。
如果设置为true,解码器将返回null(无位图),但仍将设置out ...字段,允许调用者查询位图而无需为其像素分配内存。
答案 2 :(得分:0)
狗在同一时间吠叫,悲伤,快乐和好玩吗? 您需要尝试不同的方法来尝试做什么。如果你甚至不使用它们,你绝对不需要内存中的每一个精灵。
答案 3 :(得分:0)
你考虑过使用LibGDX吗?
它有SpriteSheets(TextureAtlas)的实现,也是构建精灵的工具。这样您就不必花时间编写自己的spritesheet渲染器/控制器。
LibGDX Tutorial for TextureAtlas and Texture Packing
LibGDX是一个构建游戏的框架,像你这样的虚拟宠物项目听起来像是一个很好的候选者。