在寫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. 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 */ 20 21 /** 22 * 把一個對象用此位元組數組進行重裝 23 * @param data 位元組數組 24 */ 25 public void unSerialize(byte[] data) throws IOException; 26 27 /** 28 * 設置此對象序列化后對應的存儲對象的ID 29 * @param id ID 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 /* 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 * 實現此介面的類要實現的方法 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 /** 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(); 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 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 下標 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 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封裝起來,上層調用就更方便了 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 } 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 { 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) { 52 LogManager.error("RMSUtil.insertObject(),ser = " ser ",exe = " exe); 53 return false; 54 } 55 } 56 57 /** 58 * 更新某個對象到RMS裡面去, 59 * @param ser 要更新的對象 60 * @return 是否成功 61 */ 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); 73 } 74 } 75 76 /** 77 * 從RMS裡面刪除某個對象 78 * @param ser 要刪除的對象 79 * @return 是否成功 80 */ 81 public static boolean deleteObject(Serializable ser) { 82 if (ser.getId() == -1) { 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(); 95 return false; 96 } 97 } 98 99 /** 100 * 從某個資料庫裡面讀取某個對象 101 * @param id 此對象的ID 102 * @param clz 對應的類 103 * @return 此對象,如果發生任何異常,則返回null 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) { 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) { 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 有效記錄總數 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); 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) { 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 { 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); 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 從哪裡開始取 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()) { 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; 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); 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; 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(); 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 { 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 } 250 } catch (Exception exe) { 251 exe.printStackTrace(); 252 LogManager.error("RMSUtil.insertAttachDatas(),Lazy = " lazy ",exe = " exe); 253 return false; 254 } 255 } 256 257 /** 258 * 得到某個可以延遲載入的對象的某個附件的位元組數組內容 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) { 271 LogManager.error("RMSUtil.getAttachData(),Lazy = " lazy ",id = " id ",exe = " exe); 272 return null; 273 } 274 } 275 276 /** 277 * 更新某個對象的附件 278 * TODO 如果能把此方法變成私有就更好了 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); 289 } catch (Exception exe) { 290 exe.printStackTrace(); 291 LogManager.error("RMSUtil.updateAttachData(),Lazy = " lazy ",exe = " exe); 292 return false; 293 } 294 } 295 296 /** 297 * 從附件資料庫中刪除某個附件 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) { 309 LogManager.error("RMSUtil.deleteAttachData(),Lazy = " lazy ",id = " id ",exe = " exe); 310 return false; 311 } 312 } 313 314 /** 315 * 關閉所有的RMS 316 */ 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 } 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) { 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(); 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資料庫 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); 374 return rs; 375 } 376 } 377 } 378 |
相信看完代碼以後,大家應該知道如何使用它吧.如果有需要持久化的類,那麼就需要實現Serializable介面,然後只要調用RMSUtil.insertObject()就可以了,同理,查找也是一樣的,你可以查找同一個類的一系列對象,也可以自己定義記錄查詢器,在裡面設置查詢條件.
目前JAVAME的持久化框架,也有用其它的方法實現的,比如動態插入代碼的方法,也就是你在寫好了JAVAME的代碼以後,在編譯的過程中,它自動幫你加上相應的方法,我看了一個他們的源碼,其實也就是它們自己幫你實現了一個相當於Serializable介面,我覺得這樣不好的地方在於,它會為你的類添加方法,萬一你的類裡面原來就有那個方法的時候,那就會出現不可意料的情況了,還有,我覺得自己的數據還是自己一個一個把它寫出來,這樣心裡更踏實一些.我一直都認為,封裝得有一個度,不能過度的封裝,過度封裝表面上看是編碼更方便了,但是寫的時候,自己心裡也更沒底了,你不知道別人的代碼都做了一些什麼.別人的代碼做的事情太多了.呵呵,純屬個人意見.
大家如果有什麼自己的看法,歡迎留言.
還有,此代碼用到了我的另外一個通用框架,那就是LOG框架,所以如果直接下載的話,可能會編譯不過了,只要註釋掉LogManager的調用就可以了.LOG框架的說明點擊這裡,這個LOG框架現在正在改動中,以使它更好用,更沒有侵入性.
Netbeans項目工程打包下載,請點擊這裡.此工程中還有LOG框架在裡面.
[火星人 ] JavaME的RMS通用持久化框架已經有870次圍觀