I am working on porting a Roguelike dungeon adventure game to the Android. http://tyrant.sourceforge.net
When I start my program, I initialize the world and all it's objects first. I have a class that contains 3 million(!) HashMap entries. When I try to initialize that class, it takes 5+ minutes to run on my Android, which is a very unacceptable length of time. Once the class is is initialized, the game runs great and is quite fun.
So I thought I could serialize this object and reload it at runtime. I serialize it, and pack it in the .APK 开发者_如何学编程file, it's over 5MB in size. Android just chokes and gives me an OutOfMemory error.
I can deserialize part of the class(800KB), and then dynamically create the rest of entries. That still takes quite a few minutes to finish too, it still has to create 3 million entries from the serialized data. But I do know that my serialization code works fine.
How can I store this very large class file on the Android and load it later? What are some options I can explore? Making my user wait 5 minutes for the app to start is not very good at all.
Here is the troubling class in the original source code: http://tyrant.cvs.sourceforge.net/viewvc/tyrant/tyrant/mikera/engine/Lib.java?view=markup
and the troubling field is:
private transient Map types;
(I'm aware it's transient)
it ends up storing over 3 million entries!
Well part of the problem is what is in the types
map.
From what I can make out, it is effectively a Map<String, Map<Integer, List>>
, where the 2nd level maps are really non-sparse arrays. If you chose a better data structure it would occupy less space and you could build it much faster. (I think you are right not to serialize it ... since it looks like it might not be changed during game play.)
A HashMap<Integer, Object>
takes a LOT more space than an Object[]
.
A better data structure would be Map<String, LevelMap>
where LevelMap
is something like this:
public class LevelMap {
private static final ArrayList<String> EMPTY = Collections.emptyList();
private ArrayList<String>[] levels;
private int firstLevel;
public LevelMap(int firstLevel, int lastLevel) {
this.firstLevel = firstLevel;
levels = new ArrayList<String>[lastLevel - firstLevel + 1];
}
public static void addToTypeMap(String key, String value,
int firstLevel, int lastLevel) {
LevelMap l = types.get(key);
if (l == null) {
l = new LevelMap(firstLevel, lastLevel);
types.put(key, l);
}
l.add(value, firstLevel, lastLevel);
}
public void add(String value, int firstLevel, int lastLevel) {
ensure(firstLevel, lastLevel);
for (int i = firstLevel; i <= lastLevel; i++) {
j = i - this.firstLevel;
if (levels[j] == null) {
levels[j] == new ArrayList<String>();
}
levels[j].add(value);
}
}
private void ensure(int firstLevel, int lastLevel) {
// Make sure that these levels are in the levelmap
// If necessary, reallocate this.levels and adjust this.firstLevel
}
public String get(int level) {
if (level < this.firstLevel ||
level >= this.firstLevel + levels.length ||
levels[level - this.firstLevel] == null) {
return EMPTY;
} else {
return levels[level - this.firstLevel];
}
}
}
I think part of your problem is that your entries are too fat. Looking over your code, you store a HashMap of String-keyed properties for every Thing? That's probably killing you right there. First thing to do is switch to enum-based HashMap, and see if that makes a performance difference [and sum applies to all your Strings all over the place - replace them with enums everywhere, and have on utility that converts them to Strings if they need be displayed to users]
Now, why exactly do you have 3 million entries?!?! is that your entire world? If so, can you break it up into 100 squares ala chessboard, write out into 100 different files, and start off only loading the square your heroes are in, while loading the rest in the background later?
- You are writing a game and trying to serialize a huge object, can't you break it into levels or something and avoid holding everything all the time.
- Have you looked at Designing For Performance on the Android Developer site?
- Look into using the performance tools on Android Profiling with Traceview.
精彩评论