【问题标题】:How can I deserialize a very large class on Android?如何在 Android 上反序列化一个非常大的类?
【发布时间】:2011-03-25 05:43:19
【问题描述】:

我正在努力将 Roguelike 地牢冒险游戏移植到 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 万个条目!

【问题讨论】:

    标签: java android serialization out-of-memory


    【解决方案1】:

    问题的一部分在于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];
            }
        }
    }
    

    【讨论】:

    • 什么是更好的数据结构?此地图可让您获取某个级别的事物列表。如果我想创建一个 5 级怪物,我会得到“Monster”键的 Map 值。然后我得到键 5 的列表。这是一个示例条目> IsMonster: {1=[田鼠, 小老鼠, 小蜘蛛, 草蛇, 昆虫, 小黄虫], 2=[杂草妖精, 老鼠跨度>
    【解决方案2】:

    我认为你的部分问题是你的条目太胖了。查看您的代码,您为每个事物存储了一个字符串键控属性的 HashMap?那可能会杀死你。首先要做的是切换到基于枚举的 HashMap,看看这是否会产生性能差异 [并且 sum 适用于所有地方的所有字符串 - 用枚举替换它们,如果它们有将它们转换为字符串的实用程序需要显示给用户]

    现在,为什么你有 300 万个条目?!?!那是你的整个世界吗?如果是这样,你能把它分成 100 个棋盘格,写成 100 个不同的文件,然后开始只加载你的英雄所在的格子,然后在后台加载其余的格子吗?

    【讨论】:

      【解决方案3】:
      1. 您正在编写游戏并尝试序列化一个巨大的对象,难道您不能将其分解为关卡或其他东西并避免一直持有所有内容。
      2. 您查看过 Android 开发者网站上的Designing For Performance 吗?
      3. 研究在 Android Profiling with Traceview 上使用性能工具。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-12-24
        • 1970-01-01
        • 1970-01-01
        • 2014-10-16
        • 1970-01-01
        • 1970-01-01
        • 2021-12-13
        相关资源
        最近更新 更多