在写JAVAME程序的时候,我们经常需要保存一些数据到手机里面,也经常希 望能把对象也保存到手机里面,但是JAVAME里面没有反射机制,也没有 java.io.Serializable接口,所以没有序列化的机制,要保存对象的话,就得自 己动手了。
在JAVAME中,程序的数据保存的地方,无外乎两种,一种是把数据保存在RMS 里面,这是所有的JAVAME的手机都支持的,还有一种就是把数据保存在手机的文 件系统里面,这个不是所有手机都能支持的,只有支持JSR075的手机,才支持把 数据保存在文件系统里面,并且如果你的程序没有经过签名的话,你每次保存或 者读取,手机都会弹出恼人的提示,是否允许程序访问文件系统。所在我一般都 是把数据存在RMS里面,因为读写RMS是安全的,并且也是不需要手机提示的。因 为我们的RMS数据是存在一个特殊的地方。但是JAVAME的RMS功能非常底层,为了 保存一些数据,我们必须和byte[]打交道,所以我就产生了,在此之前封装一层 自己的程序的想法,这样封装好以后,使用起来就非常方便了。只要实现了相关 接口,就可以享受到比较易用的方法了。
此框架总共包括了四个类,分别如下:
Serializable类,它是一个接口,类似于JAVASE里面的Serializable接口, 唯一不同的就是,JAVASE里面的接口是一个空接口,只做标记用的,而这里的这 个接口是有方法需要实现的。
Lazy类,它也是一个接口,它定义了一些方法,如果你的对象比较大,需要 惰性加载的时候,可以实现此接口,并且此接口是Serializable接口的子类,也 就是说实现了Lazy接口,你就相当于实现了Serializable接口。
RMSUtil类,此类是一个工具类,用于统一进行RMS的相关操作,也是此框架 的核心类。
RecordFetcher类,也是一个接口,它继承了RecordComparaTor, RecordFilter接口,在取数据的时候,需要用到它。
好了,下面我们就开始看代码吧。
1 /* 2 * To change this template, choose Tools | Templates 3 * and open the template in the ediTor. 4 */ 5 package com.hadeslee.mobile.rms; 6 7 import java.io.IOException; 8 9 /**10 * 一个可自己串行化的类所要实现的接口11 * @author hadeslee12 */13 public interface Serializable {1415 /**16 * 把自己编码成字节数组的格式17 * @return 字节数组18 */19 public byte[] serialize() throws IOException;2021 /**22 * 把一个对象用此字节数组进行重装23 * @param data 字节数组24 */25 public void unSerialize(byte[] data) throws IOException;2627 /**28 * 设置此对象序列化后对应的存储对象的ID29 * @param id ID30 */31 public void setId(int id);3233 /**34 * 得到此对象序列化后的ID35 * 此方法唯有在反序列化后的对象上调用才有效36 * 如果一个对象是没有序列化的,那么它的ID是-1;37 * @return ID38 */39 public int getId();40 }41 1 /* 2 * To change this template, choose Tools | Templates 3 * and open the template in the ediTor. 4 */ 5 package com.hadeslee.mobile.rms; 6 7 import java.io.IOException; 8 9 /**10 * 可以延迟加载的对象必须要实现的接口11 * @author binfeng.li12 */13 public interface Lazy extends Serializable {1415 /**16 * 实现此接口的类要实现的方法17 * 可以用于延迟加载某些属性。比如18 * get("ImgData"),get("fullImage")..等等19 * 由于J2ME不支持注释也不支持反射,所以只能以20 * 此种方法来进行模拟了21 * 此方法是RMSUtil要存对象的时候调用的,这样就可以把22 * 一个对象的不同部份存到不同的RMS里面去了23 * @param key 要得到的某性的键24 * @return 其对应的值25 * @throws IOException26 */27 public byte[] getAttach(Object key)throws IOException;2829 /**30 * 当把某个附属的对象保存进去以后,所要调用的31 * 方法,此方法告诉主体,它的那个附件被保存后32 * 在RMS里面对应的ID是多少33 * @param key34 * @param id35 */36 public void savedAttach(Object key, int id);3738 /**39 * 得到此对象所支持的所有的key的数组40 * @return KEY的数组,不能为NULL41 */42 public Object[] getAttachKeys();4344 /**45 * 此对象的附属对象所存的RMS的名字46 * @return RMS的名字47 */48 public String getNameOfAttachRMS();49 }50
1 /* 2 * To change this template, choose Tools | Templates 3 * and open the template in the ediTor. 4 */ 5 package com.hadeslee.mobile.rms; 6 7 import javax.microedition.rms.RecordComparaTor; 8 import javax.microedition.rms.RecordFilter; 910 /**11 * 此类是一个继承了两个接口的接口,并且添加了自己12 * 的方法,自己的方法是用于通知数量以及开始取的位置13 * 只是为了方便于传递参数以及以后扩展14 * @author binfeng.li15 */16 public interface RecordFetcher extends RecordComparaTor, RecordFilter {1718 /**19 * 从哪个下标开始取20 * @return 下标21 */22 public int getFromIndex();2324 /**25 * 最多取多少条记录26 * @return 记录27 */28 public int getMaxRecordSize();29 }30 1 /* 2 * To change this template, choose Tools | Templates 3 * and open the template in the ediTor. 4 */ 5 package com.hadeslee.mobile.rms; 6 7 import com.hadeslee.mobile.log.LogManager; 8 import java.util.Enumeration; 9 import java.util.Hashtable; 10 import java.util.VecTor; 11 import javax.microedition.rms.RecordEnumeration; 12 import javax.microedition.rms.RecordSTore; 13 import javax.microedition.rms.RecordSToreException; 14 15 /** 16 * 一个专门用来操作RMS的工具类,通过这个类 17 * 可以把RMS封装起来,上层调用就更方便了 18 * @author binfeng.li 19 */ 20 public class RMSUtil { 21 22 /** 23 * 用于缓存生命周期之内的所有的RecordSTore的表,当 MIDlet要退出的 24 * 时候,调用此类的关闭方法,使RMS正确地被关闭 25 */ 26 private static Hashtable rmsCache = new Hashtable (); 27 28 private RMSUtil() { 29 } 30 31 /** 32 * 插入一个对象到一个RMS的数据库里面,如果此数据库不 存在 33 * 则自动创建一个对于MIDlet私有的数据库。如果存在,则 直接 34 * 插在此数据库的最后面 35 * @param ser 要插入的数据,必须是实现了Serializable接 口的类 36 * @return 是否插入成功 37 */ 38 public static boolean insertObject(Serializable ser) { 39 RecordSTore rs = null; 40 try { 41 rs = getRecordSTore(ser.getClass ().getName()); 42 if (ser instanceof Lazy) { 43 Lazy lazy = (Lazy) ser; 44 insertAttachDatas(lazy); 45 } 46 byte[] data = ser.serialize(); 47 int id = rs.addRecord(data, 0, data.length); 48 ser.setId(id); 49 return true; 50 } catch (Exception exe) { 51 exe.printStackTrace(); 52 LogManager.error ("RMSUtil.insertObject(),ser = " + ser + ",exe = " + exe); 53 return false; 54 } 55 } 56 57 /** 58 * 更新某个对象到RMS里面去, 59 * @param ser 要更新的对象 60 * @return 是否成功 61 */ 62 public static boolean updateObject(Serializable ser) { 63 RecordSTore rs = null; 64 try { 65 rs = getRecordSTore(ser.getClass ().getName()); 66 byte[] data = ser.serialize(); 67 rs.setRecord(ser.getId(), data, 0, data.length); 68 return true; 69 } catch (Exception exe) { 70 exe.printStackTrace(); 71 LogManager.error ("RMSUtil.updateObject(),ser = " + ser + ",exe = " + exe); 72 return false; 73 } 74 } 75 76 /** 77 * 从RMS里面删除某个对象 78 * @param ser 要删除的对象 79 * @return 是否成功 80 */ 81 public static boolean deleteObject(Serializable ser) { 82 if (ser.getId() == -1) { 83 return false; 84 } 85 RecordSTore rs = null; 86 try { 87 rs = getRecordSTore(ser.getClass ().getName()); 88 int id = ser.getId(); 89 rs.deleteRecord(id); 90 ser.setId(-1); 91 return true; 92 } catch (Exception exe) { 93 exe.printStackTrace(); 94 LogManager.error ("RMSUtil.deleteObject(),ser = " + ser + ",exe = " + exe); 95 return false; 96 } 97 } 98 99 /**100 * 从某个数据库里面读取某个对象101 * @param id 此对象的ID102 * @param clz 对应的类103 * @return 此对象,如果发生任何异常,则返回null104 */105 public static Serializable readObject(int id, Class clz) {106 RecordSTore rs = null;107 try {108 rs = getRecordSTore(clz.getName());109 byte[] data = rs.getRecord(id);110 Serializable ser = (Serializable) clz.newInstance();111 ser.unSerialize(data);112 ser.setId(id);113 return ser;114 } catch (Exception exe) {115 //如果读取对象失败,则可能是有东西被删了 或者版本不一样,此时就应该删掉116 exe.printStackTrace();117 LogManager.error("RMSUtil.readObject (),id = " + id + ",Class = " + clz + ",exe= " + exe);118 if (rs != null) {119 try {120 rs.deleteRecord(id);121 } catch (Exception ex) {122 ex.printStackTrace ();123 LogManager.error ("RMSUtil.readObject$rs.deleteRecord(id),id = " + id + ",exe = " + ex);124 }125 }126 return null;127 }128 }129130 /**131 * 得到某个类存在RMS里面的总数,这样便于分段取132 * @param cls 类名133 * @return 有效记录总数134 */135 public static int getSToreSize(Class cls) {136 try {137 RecordSTore rs = getRecordSTore (cls.getName());138 return rs.getNumRecords();139 } catch (Exception exe) {140 exe.printStackTrace();141 LogManager.error("RMSUtil.getSToreSize (),Class = " + cls + ",exe = " + exe);142 return -1;143 }144 }145146 /**147 * 列出某个类的对象的集合,最多取多少个对象148 * @param cls 类名149 * @param from 从第几个开始取150 * @param maxSize 最多取多少个对象151 * @return 取到的列表152 */153 public static VecTor listObjects(Class cls, int from, int maxSize) {154 System.out.println("class="+cls);155 if (from < 0 || maxSize = from) {190 if (size < maxSize) {191 Serializable ser = readObject(id, cls);192 if (ser != null) {193 v.addElement (ser);194 size++;195 }196 } else {197 break;198 }199 }200 index++;201 }202 }203204 /**205 * 列出某个类的对象,并用一种过滤以及排序的方法来进行过 滤或者排序206 * @param cls 类名207 * @param fetcher 取记录的方法208 * @return 记录列表209 */210 public static VecTor listObjects(Class cls, RecordFetcher fetcher) {211 System.out.println("fetcher class="+cls);212 int from = fetcher.getFromIndex();213 int maxSize = fetcher.getMaxRecordSize();214 if (from < 0 || maxSize < 1) {215 throw new IllegalArgumentException("from can not less than 0 and maxSize must greater than 0");216 }217 VecTor v = new VecTor();218 RecordEnumeration ren = null;219 try {220 RecordSTore rs = getRecordSTore (cls.getName());221 ren = rs.enumerateRecords(fetcher, fetcher, false);222 fetchRecord(v, cls, ren, from, maxSize);223 } catch (Exception exe) {224 LogManager.error("RMSUtil.listObjects (),Class = " + cls + ",exe = " + exe);225 exe.printStackTrace();226 } finally {227 ren.destroy();228 }229 return v;230 }231232 /**233 * 插入某个可延迟加载的对象的所有附件到数据库里面去234 * 插入完成后,此lazy对象将变得很完整,因为此时它的235 * 附件对象的ID都已经设置好了236 * @param lazy 要插入附件的主对象237 * @return 是否插入成功238 */239 private static boolean insertAttachDatas(Lazy lazy) {240 try {241 Object[] attachKeys = lazy.getAttachKeys();242 RecordSTore rs = getRecordSTore (lazy.getNameOfAttachRMS());243 for (int i = 0; i 32) {367 name = name.substring(name.length()-32, name.length());368 }369 if (rmsCache.containsKey(name)) {370 return (RecordSTore) rmsCache.get (name);371 } else {372 RecordSTore rs = RecordSTore.openRecordSTore(name, true);373 rmsCache.put(name, rs);374 return rs;375 }376 }377 }378
相信看完代码以后,大家应该知道如何使用它吧。如果有需要持久化的类, 那么就需要实现Serializable接口,然后只要调用RMSUtil.insertObject()就可 以了,同理,查找也是一样的,你可以查找同一个类的一系列对象,也可以自己 定义记录查询器,在里面设置查询条件。
目前JAVAME的持久化框架,也有用其它的方法实现的,比如动态插入代码的 方法,也就是你在写好了JAVAME的代码以后,在编译的过程中,它自动帮你加上 相应的方法,我看了一个他们的源码,其实也就是它们自己帮你实现了一个相当 于Serializable接口,我觉得这样不好的地方在于,它会为你的类添加方法,万 一你的类里面原来就有那个方法的时候,那就会出现不可意料的情况了,还有, 我觉得自己的数据还是自己一个一个把它写出来,这样心里更踏实一些。我一直 都认为,封装得有一个度,不能过度的封装,过度封装表面上看是编码更方便了 ,但是写的时候,自己心里也更没底了,因为你不知道别人的代码都做了一些什 么。因为别人的代码做的事情太多了。呵呵,纯属个人意见。
大家如果有什么自己的看法,欢迎留言。
还有,此代码用到了我的另外一个通用框架,那就是LOG框架,所以如果直接 下载的话,可能会编译不过了,只要注释掉LogManager的调用就可以了。LOG框 架的说明点击这里,这个LOG框架现在正在改动中,以使它更好用,更没有侵入 性。
本文配套源码
快乐不是因为拥有的多而是计较的少