千里冰封
          JAVA 濃香四溢
          posts - 151,comments - 2801,trackbacks - 0
          在寫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(nullnullfalse);
          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 千里冰封 閱讀(5052) 評論(3)  編輯  收藏 所屬分類: JAVAME

          FeedBack:
          # re: JAVAME的RMS通用持久化框架[未登錄]
          2009-03-01 08:38 | Aaron
          謝謝樓主的分享,非常好的文章  回復  更多評論
            
          # re: JAVAME的RMS通用持久化框架
          2009-03-01 09:58 | foot
          樓主學習的速度很快啊。  回復  更多評論
            
          # re: JAVAME的RMS通用持久化框架
          2009-05-20 15:57 | f
          javamE已死  回復  更多評論
            
          主站蜘蛛池模板: 仁寿县| 嘉兴市| 天长市| 乐至县| 屏山县| 饶河县| 花莲县| 安达市| 饶平县| 普格县| 略阳县| 翁牛特旗| 公安县| 衡阳县| 镇雄县| 定西市| 伽师县| 河津市| 榆树市| 额敏县| 合江县| 北流市| 星子县| 三河市| 卓尼县| 保靖县| 吉水县| 丹东市| 岑溪市| 福州市| 乡宁县| 邵阳县| 吐鲁番市| 华容县| 万全县| 沂源县| 云阳县| 湟源县| 台湾省| 浮梁县| 齐河县|