RecordStore類供給了對J2ME記錄庫的基礎的拜訪功效。然而,lsMain顯示的開銷項目按照輸進次序排列產生了一個小標題,即查詢某一個項目比擬麻煩。
在本文中,我將先容RMS的記錄排序API——特別是RecordEnumeration類和 RecordComparator接口,你可以在Javax.microedition.rms軟件包中找到這兩者。我同時還要順便談談RecordFilter接口,它可以讓你在記錄庫中查找某個特別的記錄。你可以在這兒下載最新版本ExpensesApp的代碼。
用RecordComparator排序
在代碼清單 A中,你會發明ExpenseInfo.LoadExpenses又一次被修正了,這次利用RecordEnumeration對象來按照記錄所保留的某項數據,而不是記錄插進的次序,來從記錄庫中查找記錄:
RecordEnumeration enu = rs.enumarateRecords(null, new ExpenseComparator, false);
RecordStore.enumerateRecords接收一個ExpenseComparator類的對象(為參數),ExpenseComparator類實現了RecordComparator的接口,RecordEnumeration用它來斷定用來排序的記錄的次序。我在清單B中給出了ExpenseComparator的代碼。
讓我們來檢測一下RecordComparator.compare方法。Compare方法用於處理兩個記錄,這兩個記錄均處於字節數組的情勢(參數為bytes和bytes1數組),並且必需可以從中提取出任何可以決定先後次序的數據(決定先後次序的方法由RecordEnumeration斷定)。該方法然後這樣指出這兩個記錄的相對關系::
假如由bytes代表的記錄(插進時間)在前,那麼compare會返回ExpenseComparator.PRECEDES,並且bytes在bytes1之前呈現在枚舉(enumeration)中。
假如bytes所代表的記錄(的插進時間)在 bytes1所代表的記錄之後,那麼compare返回ExpenseComparator.FOLLOWS,這樣,byteIs在bytes之前呈現在枚舉中。
假如這兩個記錄是等價的(equivalent)(即同一天輸進的),compare返回ExpenseComparator.EQUIVALENT,這兩個記錄的次序任意。
在ExpenseComparator中,我從這兩個記錄中獲取ExpenseDate字段(它以“當前時刻的毫秒數”的格局被存到記錄庫中)並根據這兩個記錄的排序返回相應的值。
實現RecordComparator時,要記住對記錄庫中的每一個記錄至少要調用一次compare方法,(當枚舉開端產生時)。所以,你需要准確領導比擬過程,使得這所費時間盡量的短,以免沒有必要地下降利用程序的運行速度。還有一點就是,你用於比擬的兩個記錄在最後無需緊聯(immediately adjacent),分類枚舉。
應用記錄枚舉
你也許還記得我在上一篇文章中埋怨應用RecordStore類是如何讓人感到沮喪、而它“名不副實”(相對於RMS API中的其它類)的方法又有多少。再次誇張,獲得MIDP類API的JavaDoc文檔將會對你的J2ME工作特別有用。
再次看看清單A中的代碼,你會留心到現在記錄提取循環(*譯者注:用於提取、讀取記錄的循環*)是用RecordEnumeration.hasNextElement方法做為把持變量的。在以前,每一個開銷項目標ID號保留在ExpenseInfo的一個實例中,記錄中的數據——開銷日期、闡明、美元數、美分數(*譯者注:在前面已經說過,J2ME的變量沒有浮點型,所以花用度美元和美分這兩個整數來表現。*)、回類——是通過兩個流讀取類來按次序提取的。
聯合應用RecordEnumeration和xpenseComparator使得lsMain中的開銷項目按項目中的數據的次序來顯示,這就比按照開銷項目插進的次序顯得更加符合邏輯。然而,即使你並沒有打算對記錄進行排序,你也應當考慮用RecordEnumeration來在記錄庫中查詢記錄。這樣做比應用RecordStore類更加簡略,而且還回避了應用RecordStore類的幾個埋伏標題,如我在上一篇文章結尾所提到的刪除bug。
棘手的記錄指針
當你留心這些不一致的方法名稱時,它們就僅僅是一件令人討厭的事而已;但是除此之外,還存在一個更大的、會令你寢食難安的標題。當你偶然粗略浏覽文檔後,你可能會想到,僅有nextRecord方法和previousRecord方法可以在枚舉下移動記錄指針。進一步思考後,你會發明並情況不是這樣的,另一個方法也可以把持記錄指針,這就是nextRecordId。
你必需明白這個事實,由於當你希看更新一個記錄之前,獲知它的ID號幾乎是不可避免的。這樣,下面的查詢記錄的方法或許會派上用處:
調用 nextRecordId來獲取枚舉中下一條記錄的ID號。
用nextRecord方法得到下一條記錄。
獲取該記錄中的數據。
重復上述過程,直到最後一條記錄。
上述方法的標題是:由於調用nextRecordId也會移動記錄指針,當你剛剛查詢完一半記錄是,你就捕捉到一個莫名其妙的InvalidRecordIdException例外而不得不重新開端(to boot)。
正如你在代碼清單A中所見,解決第一個標題的方法是用RecordStore.getRecord來代替RecordEnumeration.nextRecord檢索記錄。我知道,這個方法並不完善;但是至少可以工作。那個讓人摸不著頭腦的例外是在記錄指針指到枚舉的最後一個記錄的情況下調用nextRecordId而產生,所以你在寫代碼時要留心避免這種情況。
用RecordFilter來查找記錄
盡管我沒有在ExpensesApp利用程序中完成它,RecordEnumeration也可能完成搜尋記錄的功效。為了做到這一點,你要向RecordStore.enumerateRecords傳遞一個類(該類實現了RecordFilter的接口)的實例,並完整疏忽RecordComparator。RecordFilter僅有一個名為matches方法,它接收一個字節數組參數(字節數組代表了某個記錄)。該方法用於檢測記錄,並根據被檢測的記錄是否符合預定尺度而返回“真”或者“假”。
舉例來說,假設我們有一個RecordFilter的實現:ExpenseFilter,它在全部記錄庫中搜尋開銷記錄中的“開銷分類(category)”字段符合ExpenseInfo.CATEGORYMEALS的開銷項目,如代碼清單C所示。為了獲得只有符合上述條件的記錄聚集(enumeration,枚舉),我可以這樣組織代碼:
RecordStore rs = RecordStore.openRecordStore(RS_NAME);
RecordEnumeration enu = es.enumerateRecords(new ExpenseFilter, null, false);
在這裡,變量enu僅包含分類為“膳食”(這在用戶界面指導)的開銷記錄。
工作尚未完成,我們仍需努力
到目前為止,ExpensesApp已經相當完善了。現在,它已經有了個像樣的用戶界面(包含添加新的開銷記錄的快捷方法),也有了些實際用處——在運行過程中存儲信息。但是仍有一些標題:
新添加的開銷記錄並沒有按照排序插進鏈表中的對應地位。
ExpenseInfo的實例中的任何轉變僅僅更新內存,而沒有考慮到假如該實例已經存盤,還需要將更新後的內容重新存盤——我擔心這一點可能還沒有人看出來。
本利用程序還不合適在移動設備運行,由於當它暫停運行時,它沒有試圖開釋它所占用的資源——而這一點是移動設備利用程序所必需考慮的。