如何在Android上反序列化一个非常大的类?

时间:2011-03-25 05:43:19

标签: java android serialization out-of-memory

我正在努力将Roguelike Dungeon冒险游戏移植到Android。 http://tyrant.sourceforge.net

当我开始我的程序时,我首先初始化世界及其所有对象。我有一个包含300万(!)HashMap条目的类。当我尝试初始化该类时,在Android上运行需要5分钟以上,这是一段非常难以接受的时间。一旦课程初始化,游戏运行良好,非常有趣。

所以我想我可以序列化这个对象并在运行时重新加载它。我序列化它,并将其打包在.APK文件中,它的大小超过5MB。 Android只是窒息并给我一个OutOfMemory错误。

我可以反序列化类的一部分(800KB),然后动态创建其余的条目。这仍然需要花费几分钟才能完成,它仍然需要从序列化数据中创建300万个条目。但我知道我的序列化代码工作正常。

如何在Android上存储这个非常大的类文件并在以后加载?我可以探索哪些选项?让我的用户等待5分钟让应用程序启动并不是很好。

这是原始源代码中令人不安的类: http://tyrant.cvs.sourceforge.net/viewvc/tyrant/tyrant/mikera/engine/Lib.java?view=markup

令人不安的是:

private transient Map types;

(我知道这是短暂的)

最终存储超过300万个条目!

3 个答案:

答案 0 :(得分:0)

我认为你的部分问题是你的参赛作品太胖了。查看代码,为每个Thing存储一个字符串键控属性的HashMap?那可能就是在那里杀了你。首先要做的是切换到基于枚举的HashMap,看看是否会产生性能差异[并且总和适用于所有的所有字符串 - 用枚举替换它们到处都有,并且具有将它们转换为字符串的实用程序需要显示给用户]

现在,为什么你有300万条款?!?!是你的整个世界?如果是这样的话,你可以把它分成100个方格棋盘,写出100个不同的文件,然后开始只加载你的英雄所在的方块,而后来在后台加载其余的?

答案 1 :(得分:0)

问题的一部分是types地图中的内容。

从我可以看出,它实际上是Map<String, Map<Integer, List>>,其中第二级映射实际上是非稀疏数组。如果您选择了更好的数据结构,它将占用更少的空间,您可以更快地构建它。 (我认为你不应该将它序列化......因为在游戏过程中看起来似乎没有改变。)

HashMap<Integer, Object>占用的空间比Object[]多。


更好的数据结构是Map<String, LevelMap>,其中LevelMap是这样的:

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];
        }
    }
}

答案 2 :(得分:0)

  1. 你正在编写一个游戏并尝试序列化一个巨大的物体,你不能把它分成几个级别或者其他东西,并且不要一直抓住所有东西。
  2. 您是否在Android开发者网站上查看了Designing For Performance
  3. 使用Android Profiling with Traceview上的效果工具。