简体   繁体   English

当存储的数据和数据格式在将来的版本中可能发生变化时,存储应用程序数据的最佳方法?

[英]Best way to store application data when data stored and data format could change in future versions?

I'm making an Android Java app game (although this question applies to all languages really) and hope to release the first version soon. 我正在制作一个Android Java应用程序游戏(尽管这个问题确实适用于所有语言),希望很快发布第一个版本。 I'm nervous about how I save data in my game. 我很担心如何在游戏中保存数据。 My problem is that, if in a later update, I decide to store more data or store the same data in a different way, I need to be careful I don't lose or corrupt data for users that upgrade (ie I want users to be able to use data created by an old version in the new version, like their high scores from before). 我的问题是,如果在以后的更新中,我决定存储更多数据或以其他方式存储相同数据,则需要注意,不要为升级用户丢失或破坏数据(即,我希望用户能够能够使用新版本中旧版本创建的数据,例如以前的高分)。

For example, say I want to save high scores in version 1 and I use a class like: 例如,假设我想在版本1中保存高分,并且使用类似以下的类:

class Score { String name; int score; }

I then store the scores in an ArrayList and then, to save/load the scores, I serialize the ArrayList object. 然后,将分数存储在ArrayList中,然后,为了保存/加载分数,我将ArrayList对象序列化。

In version 1.1, maybe I decide I want to store the date with each score. 在1.1版中,也许我决定要与每个乐谱一起存储日期。 I could then update the Score object to include a date field and use default values for date if an old object does not include a date. 然后,我可以更新“分数”对象以包括日期字段,如果旧对象不包括日期,则可以使用默认值作为日期。

In version 1.2, maybe I decide that I want to store scores in an TreeSet instead and in version 1.3 perhaps I want to load/store scores online as well. 在1.2版中,也许我决定将分数存储在TreeSet中,而在1.3版中,我也想在线加载/存储分数。

Anyway, my point is, are there any general tips for making my life easy here? 无论如何,我的意思是,在这里是否有一些使我的生活变得轻松的一般技巧? I'm particularly concerned about situations from the above, where one person upgrades from version 1.1 to 1.2 and one person upgrades from 1.0 to 1.2. 我特别关注上述情况,一个人从1.1版升级到1.2,一个人从1.0版升级到1.2。 Testing all scenarios for data corruption sounds like a headache. 测试所有方案中的数据损坏听起来很头疼。

Is it really just a case of thinking really hard to pick something sensible and scalable to start with? 难道真的只是在思考很难选择明智且可扩展的东西吗?

I'm thinking it might be easy to use a HashMap from String->Object as a general purpose storage object eg storage.put("HighScoreName1", "Bob"); 我认为将String-> Object中的HashMap用作通用存储对象可能很容易,例如storage.put(“ HighScoreName1”,“ Bob”); storage.put("HighScorePoints1", 15);. storage.put(“ HighScorePoints1”,15);。 Getting the information out is a little messier than if I'd have used a custom class, but it seems easy to add extra fields and so on without much work. 与我使用自定义类相比,获取信息稍微有些混乱,但是添加额外的字段似乎很容易,而无需做很多工作。 Iterating over lists stored in this way isn't great though. 遍历以这种方式存储的列表并不是很好。

If you are using SQLiteOpenHelper, have a look at the onUpgrade method. 如果您使用的是SQLiteOpenHelper,请查看onUpgrade方法。

Called when the database needs to be upgraded. 在需要升级数据库时调用。 The implementation should use this method to drop tables, add tables, or do anything else it needs to upgrade to the new schema version. 实现应使用此方法删除表,添加表或执行其他任何升级到新架构版本所需的操作。

Write a version number to your output stream before you write the serialized content. 在编写序列化内容之前,请在输出流中写入版本号。 I also suggest writing a magic string sequence to the front of the stream (just something that you can say is yours - maybe 5 or 6 characters). 我还建议在流的前面编写一个魔术字符串序列(只是您可以说的是您的内容-可能是5或6个字符)。 So you'll write MAGIC, then you'll write your version number. 因此,您将写MAGIC,然后您将写下版本号。 Then you'll write your serialized content. 然后,您将编写序列化的内容。

Reading from disk is fairly straightforward - read and confirm magic. 从磁盘读取非常简单-读取并确认魔术。 Read version. 阅读版本。 Deserialize, then pass the deserialized content on to a handler based on magic. 反序列化,然后将反序列化的内容传递给基于魔术的处理程序。

A couple of words of caution here, though: If possible, keep the classes you use in the serialized stream simple (String, Integer, etc...). 不过,请注意以下几点:如果可能,请使序列化流中使用的类保持简单(字符串,整数等)。 If you use your own classes, you must be insanely careful that you never have to refactor in a way that would change the package or classname. 如果您使用自己的类,则必须非常小心,以免您不必以改变包或类名的方式进行重构。

As tempting as it may be to just write your business objects to storage using serialization, over the long run, this is almost always a mistake. 从长远来看,很诱人的只是使用序列化将您的业务对象写入存储,这几乎总是一个错误。 Instead, I strongly recommend developing a Configuration object. 相反,我强烈建议开发一个Configuration对象。 Your app initializes itself form the Configuration object. 您的应用程序通过Configuration对象初始化自身。 When it's time to save, the app constructs a Configuration object. 当需要保存时,应用程序将构造一个Configuration对象。 Then you never, ever, ever change a Configuration class once it ships. 这样一来,您就永远不会更改Configuration类,因为它一经交付就可以使用。 Just create a new one (sub-classing is fine). 只需创建一个新的子类即可。

By separating the reading and writing of configuration objects, you can do the following pretty easily: 通过将配置对象的读取和写入分开,您可以轻松地执行以下操作:

  1. Read old configuration 阅读旧的配置
  2. Initialize app with old config Later... 稍后使用旧配置初始化应用程序...
  3. App creates new configuration 应用创建新配置
  4. Write new config to file 将新配置写入文件

presto - instant file format update. presto-即时文件格式更新。

Obviously, this sort of thing is tough (a lot of work, anyway) to do with big, complex data graphs - but this is Android you are talking about, so your persistent state is probably not that complex. 显然,这类事情要处理大型,复杂的数据图非常困难(无论如何,很多工作)-但这是您正在谈论的Android,因此持久状态可能并不那么复杂。

Note that saving as XML, or even to a SQL database is just a different form of the above strategy. 请注意,另存为XML甚至保存到SQL数据库只是上述策略的另一种形式。

There is no definite answer, but the term you are looking for is versioning. 没有确切的答案,但是您要查找的术语是版本控制。 Best serialization implementations (and file formats) can have that working both ways (old versions of the application can read files of new versions, and vice-versa). 最佳的序列化实现(和文件格式)可以使双向运行(应用程序的旧版本可以读取新版本的文件,反之亦然)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM