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

发布于 2024-10-25 18:23:06 字数 895 浏览 1 评论 0 原文

我正在致力于将 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万条参赛作品!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风流物 2024-11-01 18:23:06

问题的一部分在于 types 映射中的内容。

据我所知,它实际上是一个 Map>,其中第二级映射实际上是非稀疏数组。如果您选择更好的数据结构,它将占用更少的空间,并且您可以更快地构建它。 (我认为你不序列化它是对的......因为它看起来在游戏过程中可能不会改变。)

HashMap 占用更多的空间>对象[]


更好的数据结构是 Map 其中 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];
        }
    }
}

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];
        }
    }
}
骄兵必败 2024-11-01 18:23:06

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

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

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?

混吃等死 2024-11-01 18:23:06
  1. 你正在编写一个游戏并试图序列化一个巨大的对象,你不能将它分成级别或其他东西并避免一直持有所有东西。
  2. 您是否查看过 Android 开发者网站上的性能设计
  3. 了解在 Android 上使用性能工具使用 Traceview 进行分析
  1. 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.
  2. Have you looked at Designing For Performance on the Android Developer site?
  3. Look into using the performance tools on Android Profiling with Traceview.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文