在写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 hadeslee
12 */
13 public interface Serializable {
14
15 /**
16 * 把自己编码成字节数组的格式
17 * @return 字节数组
18 */
19 public byte[] serialize() throws IOException;
20
21 /**
22 * 把一个对象用此字节数组进行重装
23 * @param data 字节数组
24 */
25 public void unSerialize(byte[] data) throws IOException;
26
27 /**
28 * 设置此对象序列化后对应的存储对象的ID
29 * @param id ID
30 */
31 public void setId(int id);
32
33 /**
34 * 得到此对象序列化后的ID
35 * 此方法唯有在反序列化后的对象上调用才有效
36 * 如果一个对象是没有序列化的,那么它的ID是-1;
37 * @return ID
38 */
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.li
12 */
13 public interface Lazy extends Serializable {
14
15 /**
16 * 实现此接口的类要实现的方法
17 * 可以用于延迟加载某些属性。比如
18 * get("ImgData"),get("fullImage")..等等
19 * 由于J2ME不支持注释也不支持反射,所以只能以
20 * 此种方法来进行模拟了
21 * 此方法是RMSUtil要存对象的时候调用的,这样就可以把
22 * 一个对象的不同部份存到不同的RMS里面去了
23 * @param key 要得到的某性的键
24 * @return 其对应的值
25 * @throws IOException
26 */
27 public byte[] getAttach(Object key)throws IOException;
28
29 /**
30 * 当把某个附属的对象保存进去以后,所要调用的
31 * 方法,此方法告诉主体,它的那个附件被保存后
32 * 在RMS里面对应的ID是多少
33 * @param key
34 * @param id
35 */
36 public void savedAttach(Object key, int id);
37
38 /**
39 * 得到此对象所支持的所有的key的数组
40 * @return KEY的数组,不能为NULL
41 */
42 public Object[] getAttachKeys();
43
44 /**
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;
9
10 /**
11 * 此类是一个继承了两个接口的接口,并且添加了自己
12 * 的方法,自己的方法是用于通知数量以及开始取的位置
13 * 只是为了方便于传递参数以及以后扩展
14 * @author binfeng.li
15 */
16 public interface RecordFetcher extends RecordComparator, RecordFilter {
17
18 /**
19 * 从哪个下标开始取
20 * @return 下标
21 */
22 public int getFromIndex();
23
24 /**
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 此对象的ID
102 * @param clz 对应的类
103 * @return 此对象,如果发生任何异常,则返回null
104 */
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 }
129
130 /**
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 }
145
146 /**
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 < 1) {
156 throw new IllegalArgumentException("from can not less than 0 and maxSize must greater than 0");
157 }
158 Vector v = new Vector();
159 RecordEnumeration ren = null;
160 try {
161 RecordStore rs = getRecordStore(cls.getName());
162 ren = rs.enumerateRecords(null, null, false);
163 fetchRecord(v, cls, ren, from, maxSize);
164 } catch (Exception exe) {
165 LogManager.error("RMSUtil.listObjects(),Class = " + cls + ",from = " + from + ",maxSize = " + maxSize + ",exe = " + exe);
166 exe.printStackTrace();
167 } finally {
168 ren.destroy();
169 }
170 return v;
171 }
172
173 /**
174 * 用于前面一个方法和后面一个方法的共用方法,
175 * 它用来从特定的记录枚举里面去取特定的记录,
176 * 并放到特定的地方
177 * @param v 要保存的地方
178 * @param cls 要实例化的类
179 * @param ren 记录的枚举
180 * @param from 从哪里开始取
181 * @param maxSize 要取多少条记录
182 * @throws java.lang.Exception 可能会抛出的异常
183 */
184 private static void fetchRecord(Vector v, Class cls, RecordEnumeration ren, int from, int maxSize) throws Exception {
185 int index = 0;
186 int size = 0;
187 while (ren.hasNextElement()) {
188 int id = ren.nextRecordId();
189 if (index >= 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 }
203
204 /**
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 }
231
232 /**
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 < attachKeys.length; i++) {
244 Object key = attachKeys[i];
245 byte[] data = lazy.getAttach(key);
246 int id = rs.addRecord(data, 0, data.length);
247 lazy.savedAttach(key, id);
248 }
249 return true;
250 } catch (Exception exe) {
251 exe.printStackTrace();
252 LogManager.error("RMSUtil.insertAttachDatas(),Lazy = " + lazy + ",exe = " + exe);
253 return false;
254 }
255 }
256
257 /**
258 * 得到某个可以延迟加载的对象的某个附件的字节数组内容
259 * TODO 如果能把此方法变成私有,那就更好了
260 * 此方法是专门供lazy对象调用的,这样的话,实体类里面就出现了
261 * 读取数据的方法,但是由于J2ME不支持反射,只能这样实现了
262 * @param lazy 可以延迟加载的对象
263 * @param id 附件的ID
264 * @return 对应的数组
265 */
266 public static byte[] getAttachData(Lazy lazy, int id) {
267 try {
268 return getRecordStore(lazy.getNameOfAttachRMS()).getRecord(id);
269 } catch (Exception exe) {
270 exe.printStackTrace();
271 LogManager.error("RMSUtil.getAttachData(),Lazy = " + lazy + ",id = " + id + ",exe = " + exe);
272 return null;
273 }
274 }
275
276 /**
277 * 更新某个对象的附件
278 * TODO 如果能把此方法变成私有就更好了
279 * @param lazy 可延迟加载的对象
280 * @param id 附件的ID
281 * @param data 附件的内容
282 * @return 是否成功
283 */
284 public static boolean updateAttachData(Lazy lazy, int id, byte[] data) {
285 try {
286 RecordStore rs = getRecordStore(lazy.getNameOfAttachRMS());
287 rs.setRecord(id, data, 0, data.length);
288 return true;
289 } catch (Exception exe) {
290 exe.printStackTrace();
291 LogManager.error("RMSUtil.updateAttachData(),Lazy = " + lazy + ",exe = " + exe);
292 return false;
293 }
294 }
295
296 /**
297 * 从附件数据库中删除某个附件
298 * @param lazy 主对象
299 * @param id 附件的ID
300 * @return 是否删除成功
301 */
302 public static boolean deleteAttachData(Lazy lazy, int id) {
303 try {
304 RecordStore rs = getRecordStore(lazy.getNameOfAttachRMS());
305 rs.deleteRecord(id);
306 return true;
307 } catch (Exception exe) {
308 exe.printStackTrace();
309 LogManager.error("RMSUtil.deleteAttachData(),Lazy = " + lazy + ",id = " + id + ",exe = " + exe);
310 return false;
311 }
312 }
313
314 /**
315 * 关闭所有的RMS
316 */
317 public static void closeAllRMS() {
318 Enumeration en = rmsCache.elements();
319 while (en.hasMoreElements()) {
320 RecordStore rs = (RecordStore) en.nextElement();
321 closeRecordStore(rs);
322 }
323 rmsCache.clear();
324 }
325
326 public static void deleteRecord(Class cls, int id) {
327 deleteRecord(cls.getName(), id);
328 }
329
330 /**
331 * 删除某个仓库里面的某条记录
332 * @param rsName 仓库的名字
333 * @param id 记录的ID
334 */
335 public static void deleteRecord(String rsName, int id) {
336 try {
337 RecordStore rs = RecordStore.openRecordStore(rsName, false);
338 if (rs != null) {
339 rs.deleteRecord(id);
340 }
341 rs.closeRecordStore();
342 } catch (Exception exe) {
343 }
344 }
345
346 /**
347 * 一个简单的方法用于关闭RecordStore
348 * @param rs 要关闭的RecordStore
349 */
350 private static void closeRecordStore(RecordStore rs) {
351 try {
352 rs.closeRecordStore();
353 } catch (Exception exe) {
354 LogManager.error("RMSUtil.closeRecordStore(),rs = " + rs + ",exe = " + exe);
355 exe.printStackTrace();
356 }
357 }
358
359 /**
360 * 得到某个RMS的存储数据库,先从缓存里面去找,如果没有找到则生成
361 * 一个并把它放到缓存里面,还有,因为RMS的名字,最长只支持32位
362 * @param name 数据库的名字
363 * @return 找到的RMS数据库
364 */
365 private synchronized static RecordStore getRecordStore(String name) throws RecordStoreException {
366 if (name.length() > 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框架现在正在改动中,以使它更好用,更没有侵入性。
Netbeans项目工程打包下载,
请点击这里。此工程中还有LOG框架在里面。
尽管千里冰封
依然拥有晴空
你我共同品味JAVA的浓香.
posted on 2009-03-01 10:13
千里冰封 阅读(5045)
评论(3) 编辑 收藏 所属分类:
JAVAME