j2me - 如何使用 RMS 存储自定义对象

Posted

技术标签:

【中文标题】j2me - 如何使用 RMS 存储自定义对象【英文标题】:j2me - How to store custom objects using RMS 【发布时间】:2014-06-22 19:10:33 【问题描述】:

在我正在开发的应用程序中,我需要存储客户、产品及其价格的数据。 为了持久化我使用 RMS 的数据,但知道 RMS 不支持直接对象序列化,并且由于我读取的数据已经采用 json 格式,我将每个 JSONObject 存储为其字符串版本,如下所示:

        rs = RecordStore.openRecordStore(mRecordStoreName, true);

        JSONArray jsArray = new JSONArray(data);                        

        for (int i = 0; i < jsArray.length(); i++) 

            JSONObject jsObj = jsArray.getJSONObject(i);
            stringJSON = jsObj.toString();                    
            addRecord(stringJSON, rs);                  

        

addRecord方法

public int addRecord(String stringJSON, RecordStore rs) throws JSONException,RecordStoreException 
    int id = -1;           
    byte[] raw = stringJSON.getBytes();
    id= rs.addRecord(raw, 0, raw.length);           
    return id;

所以我有三个 RecordStores(客户、产品和它们的价格),我对它们中的每一个都进行了如上所示的保存以保存它们的相应数据。

我知道这可能是一个可行的解决方案,但我确信一定有更好的实现。更重要的是,考虑到这三个“表”,我将执行搜索、排序等。

在这些情况下,在继续搜索或排序之前必须反序列化似乎不是一个好主意。

这就是我想问你们的原因。根据您的经验,如何将自定义对象存储在 RMS 中以便以后使用?

非常感谢您的所有 cmets 和建议。

编辑

当您为每个字段定义一个固定的最大长度时,似乎更容易处理记录。所以这就是我尝试过的:

1) 首先,这是我用来从记录存储中检索值的类:

 public class Customer    
        public int idCust;
        public String name;
        public String IDNumber;
        public String address;
    

2) 这是我用来将每个 jsonObject 保存到记录存储的代码:

        RecordStore rs = null;        
        try 
            rs = RecordStore.openRecordStore(mRecordStoreName, true);

            JSONArray js = new JSONArray(data);                           

            for (int i = 0; i < js.length(); i++) 

                JSONObject jsObj = js.getJSONObject(i);                 
                byte[] record = packRecord(jsObj);
                rs.addRecord(record, 0, record.length);

            


         finally 
            if (rs != null) 
                rs.closeRecordStore();
            
        

packRecord 方法:

       private byte[] packRecord(JSONObject jsonObj) throws IOException, JSONException 
            ByteArrayOutputStream raw = new ByteArrayOutputStream();
            DataOutputStream out = new DataOutputStream(raw);
            out.writeInt(jsonObj.getInt("idCust"));
            out.writeUTF(jsonObj.getString("name"));
            out.writeUTF(jsonObj.getString("IDNumber"));
            out.writeUTF(jsonObj.getString("address"));
            return raw.toByteArray();
        

3) 这就是我从记录存储中提取所有记录的方式:

        RecordStore rs = null;
        RecordEnumeration re = null;

        try 
            rs = RecordStore.openRecordStore(mRecordStoreName, true);
            re = rs.enumerateRecords(null, null, false);           
            while (re.hasNextElement()) 
                Customer c;
                int idRecord = re.nextRecordId();
                byte[] record = rs.getRecord(idRecord);
                c = parseRecord(record);
                //Do something with the parsed object (Customer)
            
         finally 
            if (re != null) 
                re.destroy();
            
            if (rs != null) 
                rs.closeRecordStore();
            
        

parseRecord方法:

   private Customer parseRecord(byte[] record) throws IOException 
        Customer cust = new Customer();
        ByteArrayInputStream raw = new ByteArrayInputStream(record);
        DataInputStream in = new DataInputStream(raw);
        cust.idCust = in.readInt();
        cust.name = in.readUTF();
        cust.IDNumber = in.readUTF();
        cust.address = in.readUTF();       
        return cust;
    

这就是我实施史密斯先生建议的方式(希望这是他的想法)。但是,我仍然不太确定如何实现搜索。

我差点忘了提到,在我对代码进行这些更改之前,我的 RecordStore 的大小是 229048 字节,现在只有 158872 字节 :)

【问题讨论】:

【参考方案1】:

RMS 不是那种数据库。您必须将其视为一个记录集,其中每条记录都是一个字节数组。

因此,当您为记录中的每个字段定义固定的最大长度时,使用它会更容易。例如,记录可以是有关游戏中玩家的一些信息(达到的***别、得分、玩家姓名等)。您可以将级别字段定义为 4 字节长 (int),然后定义一个 8 字节长的分数字段,然后将名称定义为 100 字节字段(字符串)。这很棘手,因为字符串通常是可变长度的,但您可能希望该字段具有固定的最大长度,如果某个字符串比该长度短,您将使用字符串终止符 char 来分隔它。 (这个例子实际上很糟糕,因为字符串是最后一个字段,所以让它保持可变长度会更容易。想象一下你有几个连续的字符串类型的字段。)

为了帮助您进行序列化/反序列化,您可以使用DataOutputstream 和DataInputStream。使用这些类,您可以读取/写入 UTF 格式的字符串,它们会为您插入字符串分隔符。但这意味着当你需要一个字段时,由于你不知道它的确切位置,你必须先读取数组到那个位置。

固定长度的优点是您以后可以使用RecordFilter,如果您想检索得分超过 10000 的玩家的记录,您可以查看完全相同位置的“积分”字段(从字节数组的开头偏移 4 个字节)。

所以这是一个权衡。固定长度意味着更快地访问字段(更快的搜索),但可能会浪费空间。可变长度意味着最小的存储空间但较慢的搜索。最适合您的情况取决于记录的数量和您需要的搜索类型。

您在网上收集了大量教程。仅举几例:

http://developer.samsung.com/java/technical-docs/Java-ME-Record-Management-Systemhttp://developer.nokia.com/community/wiki/Persistent_Data_in_Java_ME

【讨论】:

首先,非常感谢您抽出宝贵的时间来回答,非常感谢。现在关于我的应用程序,我最担心的唱片店是存储“产品”的唱片店,因为它最多可以有 1670 个项目。 (根据RecordStore.getSize() 为229048 字节)在这个rec​​ordStore 上,我将不得不执行两种搜索:一种按ProductName,另一种按ProductID。在这种情况下,您会推荐固定长度还是可变长度? 不,只有 ProductName 是一个字符串。 ID 是一个整数。 好。这样,您可以定义记录结构,其中 ID 字段位于第一个位置,然后是可变长度字符串字段。诀窍是将字符串放在每个固定长度字段之后的固定位置。按 ID 搜索会很快,因为它是一个固定长度的字段,并且是第一个字段。您甚至可以使用DataInputStream.readInt 读取它而不会损失性能。字符串字段的搜索也会很快,因为您确切知道它的开始位置,尽管它的长度是可变的。调用DataInputStream.skipBytes(4)后可以使用DataInputStream.readUtf 我刚刚在我的问题中添加了一些代码,以展示我如何实现您的建议。请看一下。 我已阅读您的更新。我认为序列化和反序列化是正确的,但是如果您打算在第 3 点中使用 sn-p 来实现搜索,那么优化记录结构没有任何优势,因为您总是在提取所有字段。您应该使用RecordFilter 搜索记录,并且只反序列化您需要的字段。正是在这种反序列化中,首先使用 ID 可以稍微加快搜索速度。

以上是关于j2me - 如何使用 RMS 存储自定义对象的主要内容,如果未能解决你的问题,请参考以下文章

J2ME RMS - 打开/关闭记录存储的最佳实践?

在 J2ME 中存储数据的文件

J2ME 中的数据库

为啥我不能将UTF写入RMS?

sql server 中如何查看自定义函数的源代码?

无法让自定义对话框与 DirtyForms 一起使用