JAVA認(rèn)證:JavaME的RMS通用持久化框架

字號(hào):

在寫(xiě)JAVAME程序的時(shí)候,我們經(jīng)常需要保存一些數(shù)據(jù)到手機(jī)里面,也經(jīng)常希望能把對(duì)象也保存到手機(jī)里面,但是JAVAME里面沒(méi)有反射機(jī)制,也沒(méi)有java.io.Serializable接口,所以沒(méi)有序列化的機(jī)制,要保存對(duì)象的話,就得自己動(dòng)手了。
    在JAVAME中,程序的數(shù)據(jù)保存的地方,無(wú)外乎兩種,一種是把數(shù)據(jù)保存在RMS里面,這是所有的JAVAME的手機(jī)都支持的,還有一種就是把數(shù)據(jù)保存在手機(jī)的文件系統(tǒng)里面,這個(gè)不是所有手機(jī)都能支持的,只有支持JSR075的手機(jī),才支持把數(shù)據(jù)保存在文件系統(tǒng)里面,并且如果你的程序沒(méi)有經(jīng)過(guò)簽名的話,你每次保存或者讀取,手機(jī)都會(huì)彈出惱人的提示,是否允許程序訪問(wèn)文件系統(tǒng)。所在我一般都是把數(shù)據(jù)存在RMS里面,因?yàn)樽x寫(xiě)RMS是安全的,并且也是不需要手機(jī)提示的。因?yàn)槲覀兊腞MS數(shù)據(jù)是存在一個(gè)特殊的地方。但是JAVAME的RMS功能非常底層,為了保存一些數(shù)據(jù),我們必須和 byte[]打交道,所以我就產(chǎn)生了,在此之前封裝一層自己的程序的想法,這樣封裝好以后,使用起來(lái)就非常方便了。只要實(shí)現(xiàn)了相關(guān)接口,就可以享受到比較易用的方法了。
    此框架總共包括了四個(gè)類(lèi),分別如下:Serializable類(lèi),它是一個(gè)接口,類(lèi)似于JAVASE里面的Serializable接口,不同的就是,JAVASE里面的接口是一個(gè)空接口,只做標(biāo)記用的,而這里的這個(gè)接口是有方法需要實(shí)現(xiàn)的。
    Lazy類(lèi),它也是一個(gè)接口,它定義了一些方法,如果你的對(duì)象比較大,需要惰性加載的時(shí)候,可以實(shí)現(xiàn)此接口,并且此接口是Serializable接口的子類(lèi),也就是說(shuō)實(shí)現(xiàn)了Lazy接口,你就相當(dāng)于實(shí)現(xiàn)了Serializable接口。
    RMSUtil類(lèi),此類(lèi)是一個(gè)工具類(lèi),用于統(tǒng)一進(jìn)行RMS的相關(guān)操作,也是此框架的核心類(lèi)。
    RecordFetcher類(lèi),也是一個(gè)接口,它繼承了RecordComparator, RecordFilter接口,在取數(shù)據(jù)的時(shí)候,需要用到它。
    好了,下面我們就開(kāi)始看代碼吧。
    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 * 一個(gè)可自己串行化的類(lèi)所要實(shí)現(xiàn)的接口
    11 * @author hadeslee
    12 */
    13 public interface Serializable {
    14
    15 /**
    16 * 把自己編碼成字節(jié)數(shù)組的格式
    17 * @return 字節(jié)數(shù)組
    18 */
    19 public byte[] serialize() throws IOException;
    20
    21 /**
    22 * 把一個(gè)對(duì)象用此字節(jié)數(shù)組進(jìn)行重裝
    23 * @param data 字節(jié)數(shù)組
    24 */
    25 public void unSerialize(byte[] data) throws IOException;
    26
    27 /**
    28 * 設(shè)置此對(duì)象序列化后對(duì)應(yīng)的存儲(chǔ)對(duì)象的ID
    29 * @param id ID
    30 */
    31 public void setId(int id);
    32
    33 /**
    34 * 得到此對(duì)象序列化后的ID
    35 * 此方法唯有在反序列化后的對(duì)象上調(diào)用才有效
    36 * 如果一個(gè)對(duì)象是沒(méi)有序列化的,那么它的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 * 可以延遲加載的對(duì)象必須要實(shí)現(xiàn)的接口
    11 * @author binfeng.li
    12 */
    13 public interface Lazy extends Serializable {
    14
    15 /**
    16 * 實(shí)現(xiàn)此接口的類(lèi)要實(shí)現(xiàn)的方法
    17 * 可以用于延遲加載某些屬性。比如
    18 * get("ImgData"),get("fullImage")..等等
    19 * 由于J2ME不支持注釋也不支持反射,所以只能以
    20 * 此種方法來(lái)進(jìn)行模擬了
    21 * 此方法是RMSUtil要存對(duì)象的時(shí)候調(diào)用的,這樣就可以把
    22 * 一個(gè)對(duì)象的不同部份存到不同的RMS里面去了
    23 * @param key 要得到的某性的鍵
    24 * @return 其對(duì)應(yīng)的值
    25 * @throws IOException
    26 */
    27 public byte[] getAttach(Object key)throws IOException;
    28
    29 /**
    30 * 當(dāng)把某個(gè)附屬的對(duì)象保存進(jìn)去以后,所要調(diào)用的
    31 * 方法,此方法告訴主體,它的那個(gè)附件被保存后
    32 * 在RMS里面對(duì)應(yīng)的ID是多少
    33 * @param key
    34 * @param id
    35 */
    36 public void savedAttach(Object key, int id);
    37
    38 /**
    39 * 得到此對(duì)象所支持的所有的key的數(shù)組
    40 * @return KEY的數(shù)組,不能為NULL
    41 */
    42 public Object[] getAttachKeys();
    43
    44 /**
    45 * 此對(duì)象的附屬對(duì)象所存的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 * 此類(lèi)是一個(gè)繼承了兩個(gè)接口的接口,并且添加了自己
    12 * 的方法,自己的方法是用于通知數(shù)量以及開(kāi)始取的位置
    13 * 只是為了方便于傳遞參數(shù)以及以后擴(kuò)展
    14 * @author binfeng.li
    15 */
    16 public interface RecordFetcher extends RecordComparator, RecordFilter {
    17
    18 /**
    19 * 從哪個(gè)下標(biāo)開(kāi)始取
    20 * @return 下標(biāo)
    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 * 一個(gè)專(zhuān)門(mén)用來(lái)操作RMS的工具類(lèi),通過(guò)這個(gè)類(lèi)
    17 * 可以把RMS封裝起來(lái),上層調(diào)用就更方便了
    18 * @author binfeng.li
    19 */20 public class RMSUtil {
    21
    22 /**
    23 * 用于緩存生命周期之內(nèi)的所有的RecordStore的表,當(dāng)MIDlet要退出的
    24 * 時(shí)候,調(diào)用此類(lèi)的關(guān)閉方法,使RMS正確地被關(guān)閉
    25 */
    26 private static Hashtable rmsCache = new Hashtable();
    27
    28 private RMSUtil() {
    29 }
    30
    31 /**
    32 * 插入一個(gè)對(duì)象到一個(gè)RMS的數(shù)據(jù)庫(kù)里面,如果此數(shù)據(jù)庫(kù)不存在
    33 * 則自動(dòng)創(chuàng)建一個(gè)對(duì)于MIDlet私有的數(shù)據(jù)庫(kù)。如果存在,則直接
    34 * 插在此數(shù)據(jù)庫(kù)的最后面
    35 * @param ser 要插入的數(shù)據(jù),必須是實(shí)現(xiàn)了Serializable接口的類(lèi)
    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 * 更新某個(gè)對(duì)象到RMS里面去,
    59 * @param ser 要更新的對(duì)象
    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里面刪除某個(gè)對(duì)象
    78 * @param ser 要?jiǎng)h除的對(duì)象
    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 * 從某個(gè)數(shù)據(jù)庫(kù)里面讀取某個(gè)對(duì)象
    101 * @param id 此對(duì)象的ID
    102 * @param clz 對(duì)應(yīng)的類(lèi)
    103 * @return 此對(duì)象,如果發(fā)生任何異常,則返回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 //如果讀取對(duì)象失敗,則可能是有東西被刪了或者版本不一樣,此時(shí)就應(yīng)該刪掉
    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 * 得到某個(gè)類(lèi)存在RMS里面的總數(shù),這樣便于分段取
    132 * @param cls 類(lèi)名
    133 * @return 有效記錄總數(shù)
    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 * 列出某個(gè)類(lèi)的對(duì)象的集合,最多取多少個(gè)對(duì)象
    148 * @param cls 類(lèi)名
    149 * @param from 從第幾個(gè)開(kāi)始取
    150 * @param maxSize 最多取多少個(gè)對(duì)象
    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 * 用于前面一個(gè)方法和后面一個(gè)方法的共用方法,
    175 * 它用來(lái)從特定的記錄枚舉里面去取特定的記錄,
    176 * 并放到特定的地方
    177 * @param v 要保存的地方
    178 * @param cls 要實(shí)例化的類(lèi)
    179 * @param ren 記錄的枚舉
    180 * @param from 從哪里開(kāi)始取
    181 * @param maxSize 要取多少條記錄
    182 * @throws java.lang.Exception 可能會(huì)拋出的異常
    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 * 列出某個(gè)類(lèi)的對(duì)象,并用一種過(guò)濾以及排序的方法來(lái)進(jìn)行過(guò)濾或者排序
    206 * @param cls 類(lèi)名
    207 * @param fetcher 取記錄的方法
    208 * @return 記錄列表
    209 */
    210 public static Vector listObjects(Class cls, RecordFetcher fetcher) {
    211 System.out.println("fetcherfrom 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 * 插入某個(gè)可延遲加載的對(duì)象的所有附件到數(shù)據(jù)庫(kù)里面去
    234 * 插入完成后,此lazy對(duì)象將變得很完整,因?yàn)榇藭r(shí)它的
    235 * 附件對(duì)象的ID都已經(jīng)設(shè)置好了
    236 * @param lazy 要插入附件的主對(duì)象
    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 * 得到某個(gè)可以延遲加載的對(duì)象的某個(gè)附件的字節(jié)數(shù)組內(nèi)容
    259 * TODO 如果能把此方法變成私有,那就更好了
    260 * 此方法是專(zhuān)門(mén)供lazy對(duì)象調(diào)用的,這樣的話,實(shí)體類(lèi)里面就出現(xiàn)了
    261 * 讀取數(shù)據(jù)的方法,但是由于J2ME不支持反射,只能這樣實(shí)現(xiàn)了
    262 * @param lazy 可以延遲加載的對(duì)象
    263 * @param id 附件的ID
    264 * @return 對(duì)應(yīng)的數(shù)組
    265 */