private RecordStore rs = null; // Record store
...
public void openRecStore()
{
...
// Create record store if it does not exist
rs = RecordStore.openRecordStore(REC_STORE, true);
...
}
public void writeStream(boolean[] bData, int[] iData, String[] sData)
{
try
{
// Write data into an internal byte array
ByteArrayOutputStream strmBytes = new ByteArrayOutputStream();
// Write Java data types into the above byte array
DataOutputStream strmDataType = new DataOutputStream(strmBytes);
byte[] record;
for (int i = 0; i < sData.length; i++)
{
// Write Java data types
strmDataType.writeBoolean(bData[i]);
strmDataType.writeInt(iData[i]);
strmDataType.writeUTF(sData[i]);
// Clear any buffered data
strmDataType.flush();
// Get stream data into byte array and write record
record = strmBytes.toByteArray();
rs.addRecord(record, 0, record.length);
// Toss any data in the internal array so writes
// starts at beginning (of the internal array)
strmBytes.reset();
}
strmBytes.close();
strmDataType.close();
}
catch (Exception e)
{
db(e.toString());
}
}
public void readStream()
{
try
{
// Allocate space to hold each record
byte[] recData = new byte[50];
// Read from the specified byte array
ByteArrayInputStream strmBytes = new ByteArrayInputStream(recData);
// Read Java data types from the above byte array
DataInputStream strmDataType = new DataInputStream(strmBytes);
for (int i = 1; i <= rs.getNumRecords(); i++)
{
// Get data into the byte array
rs.getRecord(i, recData, 0);
// Read back the data types
System.out.println("Record #" + i);
System.out.println("Boolean: " + strmDataType.readBoolean());
System.out.println("Integer: " + strmDataType.readInt());
System.out.println("String: " + strmDataType.readUTF());
System.out.println("--------------------");
// Reset so read starts at beginning of array
strmBytes.reset();
}
strmBytes.close();
strmDataType.close();
}
catch (Exception e)
{
db(e.toString());
}
}
int numRecords()
byte[] nextRecord()
int nextRecordId()
byte[] previousRecord()
int previousRecordId()
boolean hasNextElement()
boolean hasPreviousElement()
void keepUpdated(boolean keepUpdated)
boolean isKeptUpdated()
void rebuild()
void reset()
void destroy()
為代碼添加枚舉功能非常簡單,隻需在一個打開的記錄存儲上建立一個
RecordEnumeration
對象并周遊所有記錄即可。清單8 展示了如何建立一個
RecordEnumeration
。前面兩個參數(在本例中都是
null
)指定在記錄存儲上進行搜尋和/或排序的類。第三個參數是API 中定義的
keepUpdated
标志。如果這個變量設定為
true
,當記錄存儲發生改變時,枚舉結果集将被重新初始化。将這個參數設定為
false
則指定枚舉忽略記錄存儲的更新。
清單 8. 建立一個 RecordEnumeration
rs = RecordStore.openRecordStore(REC_STORE, true);
...
RecordEnumeration re = rs.enumerateRecords(null, null, false);
while (re.hasNextElement())
{
// Get next record
String str = new String(re.nextRecord());
...
}
本文開頭部分曾經提到記錄 ID 不能在記錄存儲中被重用。是以,如果一個存儲有三條記錄,ID 分别為 1,2 和 3,當 ID 2 被删除時,存儲中就隻剩下ID 1 和 3。如果不去考慮我們是如何周遊記錄存儲的,那麼這不會是一個問題。清單 9 中的代碼将改變您的想法。
清單 9. 一個讀取記錄的典型 for 循環
byte[] recData = new byte[5];
int len;
for (int i = 1; i <= rs.getNumRecords(); i++)
{
// Allocate more storage if necessary
if (rs.getRecordSize(i) > recData.length)
recData = new byte[rs.getRecordSize(i)];
len = rs.getRecord(i, recData, 0);
}
您可能已經發現了,代碼的最後一行将會帶來問題。盡管
getNumRecords()
可以傳回正确的記錄數(兩條),但是
for
循環傳回的變量
i
的值将會是 1 和 2。但是在存儲中的記錄 ID 是 1 和 3。當 i 等于 2 時對
rs.getRecord()
的調用将會失敗,因為已經沒有記錄 ID 為 2 的記錄了。這正是記錄枚舉可以發揮作用的地方,因為它不是根據ID 來擷取記錄。
是否可以忽略更新?
在建立枚舉器時,把
keepUpdated
參數設為
true
需要慎重考慮。盡管存儲發生改變時,記錄存儲結果集會更新,但連續的更新卻可能帶來性能瓶頸。
RecordComparator API
RecordEnumeration
API 提供了使用枚舉的基礎,使得我們可以周遊 RMS 中所有條目。真正讓我們可以按順序從記錄存儲中提取資料的是
RecordComparator
接口。
RecordComparator
API 包括一個方法和三個預定義的傳回值。該接口中惟一的方法接收兩個參數,它們都是位元組數組。在清單10 中,您可以了解到這兩個數組如何表示來自記錄存儲的記錄。枚舉調用
//********************************************************
// Create comparator class for sorting
//********************************************************
public class comparator implements RecordComparator
{
public int compare(byte[] rec1, byte[] rec2)
{
String str1 = new String(rec1), str2 = new String(rec2);
int result = str1.compareTo(str2);
if (result == 0)
return RecordComparator.EQUIVALENT;
else if (result < 0)
return RecordComparator.PRECEDES;
else
return RecordComparator.FOLLOWS;
}
}
...
//********************************************************
// How to access the comparator using a record enumeration
//
// Note: Variable 'rs' is created outside the scope of
// this method
//********************************************************
// Create a new comparator for sorting
comparator comp = new comparator();
// Reference the comparator when creating the result set
RecordEnumeration re = rs.enumerateRecords(null, comp, false);
// Retrieve each record in sorted order
while (re.hasNextElement())
{
String str = new String(re.nextRecord());
...
}
/*--------------------------------------------------
* Compares two integers to determine sort order
* Each record passed in contains multiple Java data
* types - use only the integer data for sorting
*-------------------------------------------------*/
class ComparatorInteger implements RecordComparator
{
private byte[] recData = new byte[10];
// Read from a specified byte array
private ByteArrayInputStream strmBytes = null;
// Read Java data types from the above byte array
private DataInputStream strmDataType = null;
public void compareIntClose()
{
try
{
if (strmBytes != null)
strmBytes.close();
if (strmDataType != null)
strmDataType.close();
}
catch (Exception e)
{}
}
public int compare(byte[] rec1, byte[] rec2)
{
int x1, x2;
try
{
// If either record is larger than our buffer, reallocate
int maxsize = Math.max(rec1.length, rec2.length);
if (maxsize > recData.length)
recData = new byte[maxsize];
// Read record #1
// We want the integer from the record, which is
// the second "field" thus we must read the
// String first to get to the integer value
strmBytes = new ByteArrayInputStream(rec1);
strmDataType = new DataInputStream(strmBytes);
strmDataType.readUTF(); // Read string
x1 = strmDataType.readInt(); // Read integer
// Read record #2
strmBytes = new ByteArrayInputStream(rec2);
strmDataType = new DataInputStream(strmBytes);
strmDataType.readUTF(); // Read string
x2 = strmDataType.readInt(); // Read integer
// Compare record #1 and #2
if (x1 == x2)
return RecordComparator.EQUIVALENT;
else if (x1 < x2)
return RecordComparator.PRECEDES;
else
return RecordComparator.FOLLOWS;
}
catch (Exception e)
{
return RecordComparator.EQUIVALENT;
}
}
public void readStream()
{
...
if (rs.getNumRecords() > 0)
{
// Create instance of the comparator
ComparatorInteger comp = new ComparatorInteger();
// Create enumerator, referencing the comparator
RecordEnumeration re = rs.enumerateRecords(null, comp, false);
// Loop through all elements in the result set
int i = 1;
while (re.hasNextElement())
{
// Get data into the byte array
rs.getRecord(re.nextRecordId(), recData, 0);
// Read back the data types
System.out.println("Record #" + i++);
System.out.println("String: " + strmDataType.readUTF());
System.out.println("Integer: " + strmDataType.readInt());
System.out.println("--------------------");
// Reset so read starts at beginning of array
strmBytes.reset();
}
...
}
//********************************************************
// Create filter class for searching
//********************************************************
class SearchFilter implements RecordFilter
{
private String searchText = null;
public SearchFilter(String searchText)
{
// Text to find
this.searchText = searchText.toLowerCase();
}
public boolean matches(byte[] candidate)
{
String str = new String(candidate).toLowerCase();
// Does the text exist?
if (searchText != null && str.indexOf(searchText) != -1)
return true;
else
return false;
}
}
...
//********************************************************
// How to access the filter using a record enumeration
//
// Note: Variable 'rs' is created outside the scope of
// this method
//********************************************************
// Create search filter
SearchFilter search = new SearchFilter("abc");
// Reference filter when creating the result set
RecordEnumeration re = rs.enumerateRecords(search, null, false);
// If there is at least one record in result set, a match was found
if (re.numRecords() > 0)
{
// At least one record in the result set, do something here...
...
}
public void writeTestData()
{
String[] strs = {
"I think this would be a good time for a beer. (FDR)",
"I'll make it a felony to drink small beer. (Shakespeare)",
"They who drink beer will think beer. (Washington Irving)",
"I would give all my fame for a pot of ale. (Shakespeare)"};
writeRecords(strs);
}
圖 4 展示了有兩個不同搜尋結果的 MIDlet。
圖 4. 記錄搜尋結果
使用者界面的建立包括指定一個
Form
、一個
TextField
和兩個
Command
―― 一個用于搜尋記錄存儲,另外一個用于退出 MIDlet,如清單 19 所示。
清單 19. 建立使用者界面元件
...
// Define textfield, stringItem and commands
tfFind = new TextField("Find", "", 12, TextField.ANY);
cmExit = new Command("Exit", Command.EXIT, 1);
cmFind = new Command("Find", Command.SCREEN, 2);
// Create the form, add commands
fmMain = new Form("Record Search");
fmMain.addCommand(cmExit);
fmMain.addCommand(cmFind);
// Append textfield and stringItem to form
fmMain.append(tfFind);
...
public void commandAction(Command c, Displayable s)
{
if (c == cmFind)
{
searchRecordStore();
}
else if (c == cmExit)
{
destroyApp(false);
notifyDestroyed();
}
}
...
//********************************************************
// Search the record store
//********************************************************
private void searchRecordStore()
{
try
{
// Record store is not empty
if (rs.getNumRecords() > 0)
{
// Setup the search filter with the user requested text
SearchFilter search = new SearchFilter(tfFind.getString());
RecordEnumeration re = rs.enumerateRecords(search, null, false);
// Remove any previous record entries displayed on the form
clearForm();
// A match was found using the filter
if (re.numRecords() > 0)
{
// Append all records found onto the form
while (re.hasNextElement())
fmMain.append(new String(re.nextRecord()));
}
re.destroy(); // Release enumerator
}
}
catch (Exception e)
{
db(e.toString());
}
}
...
//********************************************************
// Called for each record when creating the enumerator.
// Checks to see if the record contains text that
// matches the text string entered by the user.
//********************************************************
class SearchFilter implements RecordFilter
{
private String searchText = null;
public SearchFilter(String searchText)
{
// Text to find
this.searchText = searchText.toLowerCase();
}
public boolean matches(byte[] candidate)
{
String str = new String(candidate).toLowerCase();
// Look for text
if (searchText != null && str.indexOf(searchText) != -1)
return true;
else
return false;
}
}
RecordListener API
RecordListener
接口是我們讨論的最後一個 API,但并不表明它是最不重要的。在代碼中實作
RecordListener
可以保證當記錄存儲修改、添加或删除的時候您可以得到通知。
以下是使用
RecordListener
的基本步驟:
打開(建立)一個記錄存儲。
建立一個新的監聽器。
實作
RecordListener
接口中的所有方法。
RecordListener
API 中的所有方法的傳入參數都相同:一個指向發生修改的記錄存儲的引用和受到影響的記錄ID。清單 21 顯示了
RecordListener
API 的方法。
清單 21. RecordListener API
void recordAdded(RecordStore recordStore, int recordId)
void recordChanged(RecordStore recordStore, int recordId)
void recordDeleted(RecordStore recordStore, int recordId)
// Open record store
rs = RecordStore.openRecordStore(REC_STORE, true);
...
// Using handle to open record store, create a listener
rs.addRecordListener(new DemoRecordListener());
...
//********************************************************
// Listener to process updates to the record store
//********************************************************
class DemoRecordListener implements RecordListener
{
public void recordAdded(RecordStore recordStore, int recordId)
{
System.out.println("Record added");
}
public void recordDeleted(RecordStore recordStore, int recordId)
{
System.out.println("Record deleted");
}
public void recordChanged(RecordStore recordStore, int recordId)
{
System.out.println("Record changed");
}