J2ME 101,第 3 部分: 深入记录管理系统

来源:互联网 发布:淘宝卖家app官方下载 编辑:程序博客网 时间:2024/05/01 04:59
J2ME 101,第 3 部分: 深入记录管理系统发布者:IBM   日期:2004-01-22 00:00:00 浏览次数:0 (共有_条评论) 查看评论 | 我要评论 级别: 初级

John Muchow (john@corej2me.com), 自由技术作家/开发人员

2004 年  1 月  22 日

MIDP 不使用文件系统保存应用程序数据。相反,MIDP 使用一种名为记录管理系统(Record Management System,RMS)的存储系统把所有信息都保存在非易失性存储器中。本文是           J2ME 101教程系列的补充系列的两篇文章中的第一篇,作者和开发者 John Muchow 在此本文介绍了 RMS 应用程序接口的基础知识,然后引导您完成几个展示了它的功能的开发实例。       

数据存储和检索是应用程序开发的重要方面。存储什么样的数据取决于应用程序的类型和复杂度。在某些情况下,被存储的持久数据仅仅是应用程序用户的首选项信息。在另外一些情况下,您可能需要存储和管理一个联系信息库。在极端的情况下,您可能要为整个供应链数据设计一个数据存储方案。不论是哪一种情况,对于支持MIDP 的设备来说只有一种通用的数据存储和管理方案,那就是记录管理系统 (RMS)。

作为                J2ME 101 教程系列的补充系列两篇文章中的第一篇,本文将向您介绍 MIDP 持久存储系统的内部工作原理。我们将从 RMS 的概览开始,不过大部分内容(如同教程系列一样)更倾向于实际应用。并且,我们将会构建一些MIDlet,帮助您了解如何从 RMS 中读取和写入数据记录,并了解这个功能非常全面而紧凑的数据管理系统的各种排序、搜索和检索选项。           

注意,本文假设您熟悉 J2ME 环境中的 MIDlet 开发。为了编译代码示例,系统上需要安装 J2ME 开发环境。请参阅                参考资料 部分,其中有 J2ME Wireless Toolkit(WTK)安装指南的超链接。           

MIDP 中的记录管理

简单地说,MIDP 记录管理系统(RMS)提供了一种跨多个 MIDlet 调用持久性地存储应用程序数据的手段。您可以把 RMS 记录存储看作一个简单的数据库,其中每一行都包括两列:一列用于存储惟一的行标识,另外一列存储代表记录中数据的一组字节。表1 展示了一个简单的记录存储数据库。

表1. 一个记录存储数据库

记录ID数据1字节数组2字节数组3字节数组......

不要错过本系列的其他文章 !

J2ME 101,第 1 部分:介绍 MIDP 的高层 UI(2003 年 12 月)分步介绍了推动用户和设备显示之间主要交互的各个组件。               

J2ME 101,第 2 部分:介绍 MIDP 的低层 UI (2003 年 12 月) 介绍创建和使用 Canvas 和 Graphics 类的基础知识,并概述了 MIDP 2.0 中引入的 Game API。               

惟一行标识是一个整型值。第一个条目的 ID 为 1,然后是 2,以此类推。一行被删除之后,它的行标识不会被重用。也就是说,如果表中有三行,ID分别是 1,2 和 3,删除 ID 2 将把这个标识永久性地从记录存储中删除。如果我们向这个表中添加另外一行,那么该行的标识将会是 4。

记录存储是用名称来标识的。记录存储的名称最多可以包含 32 个字符,并且所有字符都是大小写敏感的。同一个 MIDlet 套件(即打包在一起的一个或多个MIDlet)中不会包含两个名称相同的记录存储。

每个记录存储都有一个版本号和一个日期/时间戳。在添加、替换或删除一个记录时,这两个值都会被更新。




 

创建一个记录存储

没有用于创建记录存储的构造函数。相反,我们使用三个双功能的方法来创建和/或打开记录存储。清单 1 列出了这三个方法。


清单 1. 创建和打开记录存储
RecordStore openRecordStore(String recordStoreName,                               boolean createIfNecessary)RecordStore openRecordStore(String recordStoreName,                               boolean createIfNecessary,                               int authmode,                               boolean writable)RecordStore openRecordStore(String recordStoreName,                               String vendorName,                               String suiteName)

删除一个记录存储

从设备中删除一个 MIDlet 套件时,由这个套件创建的所有记录存储都将被删除。

如果指定的记录存储存在,第一个方法将会打开它。如果指定的记录存储不存在,并且参数                 createIfNecessary被设为                true ,该方法可以创建一个新的(指定名称的)记录存储。第二个方法与第一个类似,只不过使用另外两个附加参数来指定记录存储的访问限制。第一个参数指定是否只有位于相同套件中的MIDlet 才能访问这个记录存储。第二个参数指定有权访问记录存储的 MIDlet 是否可以创建一个新记录。最后一个方法提供了让 MIDlet打开在另外的 MIDlet 套件中的记录存储的方法。           




 

RecordStore API

有多个操纵记录存储的方法可供使用。涉及的范围从添加、删除、替换记录内容到枚举整个记录存储。清单 2 列出了所有可用的方法。


清单 2. 记录存储方法
void closeRecordStore()void deleteRecordStore(String recordStoreName)String[] listRecordStores()int addRecord(byte[] data, int offset, int numBytes)void setRecord(int recordId, byte[] newData, int offset, int numBytes)void deleteRecord (int recordId)byte[] getRecord (int recordId)int getRecord (int recordId, byte[] buffer, int offset)int getRecordSize (int recordId)int getNextRecordID()int getNumRecords()long getLastModified()int getVersion()String getName()int getSize()int getSizeAvailable()RecordEnumeration enumerateRecords(RecordFilter filter,                                      RecordComparator comparator,                                      boolean keepUpdated)void addRecordListener (RecordListener listener)void removeRecordListener (RecordListener listener)void setMode(int authmode, boolean writable)

锁定记录

与传统数据库不同,RMS 没有提供锁定记录存储的方法。您需要借助设备实现来保证所有操作是同步的。如果使用多个线程来访问一个记录存储,那么您应该确保访问资源的线程不会相互影响。

通过后面几节介绍的例子,您将会更多地了解                 RecordStore API 和它的方法。            




 

ReadWrite MIDlet

我们的第一个展示例子是 ReadWrite MIDlet。这个 MIDlet 的功能包括:创建记录存储、把多条记录写入持久存储中、读取这些记录、退出时删除记录存储。在查看下面的代码时,请注意这个MIDlet 包含了几个“便利”方法,这些方法在使用 RMS 的过程中被反复使用。便利方法就是那些用来打开、关闭和删除记录存储的方法。

请阅读                ReadWrite MIDlet的完整代码,下面我们将详细讨论它。           

关于代码的说明

在继续讨论之前有几点值得一提。首先,就像前面解释过的,在这个例子中我们在调用                 RecordStore.openRecordStore(REC_STORE,         true) 时,                createIfNecessary 参数传递的是                true以创建一个新的记录存储。           

其次,当使用                 writeRecord(String str) 向记录存储中写入数据时,我们首先把 Java 字符串参数转换成一个字节数组。然后把这个字节数组传递给                addRecord(rec, 0, rec.length) 方法以向记录存储中插入一条记录。           

最后,在                 readRecords() 方法中,我们分配了一个字节数组用于存储从 RMS 中读取的记录数据。这需要我们在每次读取时都进行一个特定的检查,以保证数组的长度足够容纳数据。提取记录以后,我们就可以把内容输出到控制台。           

图 1 展示了 ReadWrite MIDlet 在 J2ME WTK 中运行时得到的输出。


图 1. ReadWrite MIDlet 的记录输出
图 1. ReadWrite MIDlet 的记录输出



 

读取和写入 Java 基本类型

ReadWrite MIDlet 只能将文本字符串写入到记录存储中。在接下来的例子中,我们将增加存储和操纵整型、布尔型和字符串值的功能。我们还将添对加用Java 流进行读写的支持。同前面的例子一样,我们将会写入几条记录,然后再从存储中将它们读出。

像所有需要访问记录存储的 MIDlet 一样,我们首先使用                 openRecordStore() 方法分配和打开(或创建)一个记录存储,如清单 3 所示。           


清单 3. 创建一个记录存储
private RecordStore rs = null;    // Record store...public void openRecStore(){     ...     // Create record store if it does not exist     rs = RecordStore.openRecordStore(REC_STORE, true);     ...}

清单 4 显示了我们将会写入到记录存储中的基本数据类型。


清单 4. Java 基本数据类型
public void writeTestData(){     boolean[] booleans = {true,false};     int[] integers = {17 , 4};     String[] strings = {"Golf", "Tennis"};     writeStream(booleans, integers, strings);}

如果熟悉 Java 语言中流的用法,您会发现使用 MIDlet 与使用更传统的 Java 应用程序稍有不同。在 MIDlet 中使用流的步骤如下:

  • 分配流。
  • 写入数据。
  • 清空流。
  • 把流数据转移到数组中。
  • 把数组写入到记录存储。
  • 关闭流。

清单 5 展示了如何使用流向 RMS 中写入数据:


清单 5. 把流写入到记录存储
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());     }}

然后,我们要从记录存储中读取数据。首先创建必要的输入流,然后遍历所有记录,把其中的内容存储到一个字节数组。通过访问数据输入流,我们从字节数组中读取每一个Java 基本类型,并把相应的内容输出到控制台。如清单 6 所示。


清单 6. 从记录存储中读取流
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());     }}

现在检查                ReadWritePrimitives MIDlet的源代码。请仔细研究,如果愿意的话,您也可以在自己的 WTK 模拟器中运行这段代码以查看输出。           

ReadWritePrimitives MIDlet 的控制台输出如图 2 所示。


图 2. ReadWritePrimitives MIDlet 的输出
图 2. ReadWritePrimitives MIDlet 的输出



 

RecordEnumeration API

至此,我们已经使用一个简单的                 for 循环遍历了记录存储。尽管这种技术已经足以满足我们的需求,在这一节,我们还是会扩展自己的代码,加入一个记录枚举器看看会有什么结果。在代码中加入一个记录枚举器的一个最大好处在于枚举器中包含搜索记录存储以及以排序的顺序提取记录的选项。           

清单 7 列出了 RecordEnumeration API 的方法。&#160


清单 7. RecordEnumeration API
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());     ...}

为什么需要记录枚举

乍一看,枚举循环提供的功能与                 for 循环提供的没什么不同,但实际上它提供了多得多的功能。除了提供一种搜索和排序的手段(我们马上将会看到),记录枚举还克服了                for 循环的最大弊端(尽管不是一眼就能看出来)。           

本文开头部分曾经提到记录 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 中,您可以了解到这两个数组如何表示来自记录存储的记录。枚举调用                compare() 方法,把来自存储的两个记录的内容作为参数传递给该方法。枚举和我们的搜索代码将遍历整个记录存储,根据指定的搜索条件构建一个排序过的结果集。           


清单 10. RecordComparator API
int compare(byte[] rec1, byte[] rec2)static int EQUIVALENTstatic int FOLLOWSstatic int PRECEDES

清单 11 展示了一个实现了                 Comparator 接口的简单类,它通过比较每条记录的字符串内容对记录进行排序。注意,返回值必须是预定义的值:                EQUIVALENT ,                PRECEDES或                FOLLOWS 。紧跟 comparator 类之后的代码创建了一个比较器的实例,并在创建记录枚举时将其作为一个参数。           


清单 11. 一个用于排序的 Comparator 类
//********************************************************// 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 sortingcomparator comp = new comparator();// Reference the comparator when creating the result setRecordEnumeration re = rs.enumerateRecords(null, comp, false);// Retrieve each record in sorted orderwhile (re.hasNextElement()){     String str = new String(re.nextRecord());     ...}




 

排序多种数据类型

显然,对只包含字符串数据的记录进行排序非常简单。然而,对每条记录都包含不同数据类型的记录存储进行排序要复杂一些。例如,请回忆一下                 清单4,在这个例子中我们在每条记录中写入三种基本 Java 数据类型,它们分别是 boolean、integer 和 string。怎样才能根据其中特定的一种类型进行排序呢?为了做到这一点,比较器函数需要获取用于比较的适当字段,执行比较,然后向枚举器返回适当的值(                EQUIVALENT ,                PRECEDES 或                FOLLOWS )。           

为了更好地理解这一过程,我们来编写一个根据整型进行排序的 MIDlet。首先,我们将更新                清单4 中的                writeTestData() 方法,使记录存储中的每条记录既包含字符串数据,也包含整型数据,如下所示:           


清单  12. 用于排序的 Java 基本数据类型
public void writeTestData(){     String[] strings = {"Java", "J2ME", "C"};     int[] integers = {2, 1, 3};     writeStream(strings, integers);}

IntegerSort MIDlet 的输出如图 3 所示。注意,比较器函数(我们马上将会讲到)返回根据每条记录中整型值排序之后的多条记录。


图 3. 根据整型值进行排序的记录
图 3. 根据整型进行排序的记录

构建一个比较器

IntegerSort MIDlet 的代码(请参阅                 完整的源代码)与前面的MIDlet 在整体上差别不大。最大的改变是添加了 comparator 类(清单 13)以及抽取适当字段并进行实际排序的方法。以下是为 IntegerSort MIDlet 处理所有细节的                ComparatorInteger 类。           


清单 13. ComparatorInteger 类
/*--------------------------------------------------* 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;       }     }

请注意读取 Java 基本类型的代码。我们需要从每条记录中提取出整型值,这个值是每条记录的第二个“字段”。因此,我们只是读取字符串(UTF)值并将它丢到一边。第二次读取把整型值存储在一个本地变量中(x1或 x2)。接着,比较这些值以确定正确的排序顺序。


清单 14. 读取并排序 Java 基本类型
// Read record #1...strmDataType.readUTF();       // Read stringx1 = strmDataType.readInt();  // Read integer// Read record #2...strmDataType.readUTF();       // Read stringx2 = strmDataType.readInt();  // Read integer// Compare record #1 and #2if (x1 == x2)     return RecordComparator.EQUIVALENT;else if (x1 < x2)     return RecordComparator.PRECEDES;else     return RecordComparator.FOLLOWS;

构建一个枚举器

完成比较器的编写之后,我们来创建一个枚举器,其中引用                 ComparatorInteger类的一个实例。这个枚举器将使用比较器作为排序算法,从记录存储创建一个记录结果集。清单 15 列出了                readStream()方法的一部分,该方法创建比较器和枚举器,遍历整个结果集并把记录内容显示在控制台上。           


清单 15. readStream() 方法
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();       }     ...}




 

RecordFilter API

使用比较器进行排序是使用枚举器时的一种选择,另外一种选择是使用过滤器进行搜索。比较器和过滤器之间的一个微小差别在于                 比较器返回排序后的整个记录存储,而                过滤器只返回那些满足指定条件的记录。如果同时使用比较器和过滤器,那么将会按照排序顺序返回满足搜索条件的记录。           

与                 RecordComparator 类似,                 RecordFilter 也是通过在枚举器代码中增加一个方法                matches() 实现的。枚举器对存储中的每条记录调用                matches() 方法。根据返回的布尔值,每条记录要么成为结果集的成员,要么被归为不满足搜索条件的记录放弃。           


清单 16. RecordFilter API 的 matches() 方法
boolean matches(byte[] candidate)

构建一个过滤器

清单 17 展示了一个实现了                 RecordFilter 接口的类。注意,搜索字符串被指定为                SearchFilter        构造函数的一个参数。这个字符串被保存在私有变量中,这样当枚举器调用                matches() 方法创建结果集的时候就可以访问这个字符串。在这个例子中,搜索字符串还被转换成小写字符,因此这个搜索                不是大小写敏感的。           


清单 17. 构建一个 RecordFilter
//********************************************************// 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 filterSearchFilter search = new SearchFilter("abc");// Reference filter when creating the result setRecordEnumeration re = rs.enumerateRecords(search, null, false);// If there is at least one record in result set, a match was foundif (re.numRecords() > 0){     // At least one record in the result set, do something here...     ...}

注意,本例中执行实际搜索的代码非常简单。我们使用 Java 字符串的                 indexOf() 方法来查找指定的搜索字符串,并返回一个表明成功还是失败的布尔值。            




 

StringSearch MIDlet

我们将构建最后一个 MIDlet 来展示到目前已经学过的内容(这里是                 StringSearch MIDlet 的完整源代码)。除了可以在 RMS 中搜索记录,这个 MIDlet 还加强了用户界面。这一次,我们不再只使用控制台显示输出,还将显示一个提示用户输入文本字符串的                TextField 组件(您应该可以想起教程系列中的这部分内容!)。提出请求后,我们将在记录存储中搜索这个字符串。所有匹配的字符串都添加到显示中以显示搜索结果。           

清单 18 展示了将被写入到记录存储中的搜索字符串。


清单 18. 记录存储中的条目
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. 记录搜索结果
图 4. 记录搜索结果

用户界面的创建包括指定一个                 Form 、一个                 TextField 和两个                Command  ―― 一个用于搜索记录存储,另外一个用于退出 MIDlet,如清单 19 所示。           


清单 19. 创建用户界面组件
...// Define textfield, stringItem and commandstfFind = new TextField("Find", "", 12, TextField.ANY);cmExit = new Command("Exit", Command.EXIT, 1);cmFind = new Command("Find", Command.SCREEN, 2);// Create the form, add commandsfmMain = new Form("Record Search");fmMain.addCommand(cmExit);fmMain.addCommand(cmFind);// Append textfield and stringItem to formfmMain.append(tfFind);...

事件处理

一旦                 StringSearch MIDlet 处于活动状态,所有事件都将在                commandAction()方法中处理。请求                cmFind 命令时,程序将会调用                searchRecordStore()开始搜索处理。这个过程包括指派一个                SearchFilter() 类的实例,并把这个实例关联到记录枚举对象。指派了枚举对象后,它将会调用搜索函数来搜索记录存储。所有与搜索字符串相匹配的记录都将成为枚举结果集的一部分。           

清单 20 展示了处理事件以及搜索与用户输入文本字符串相匹配的记录的代码。请注意创建搜索过滤器时对                 tfFind.getString()        的引用,该调用从文本字段中获取用户输入的搜索字符串。然后,字符串被保存在                SearchFilter类的变量                searchText 中。当枚举器调用方法                matches() 时――对记录存储中的每条记录调用一次――它将在当前的活动记录中搜索匹配                searchText 的字符串。           


清单 20. 处理用户事件
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)

清单 22 中的一小段代码展示了如何在 RMS 的记录发生添加、删除或更改的时候把消息显示在控制台上。


清单 22. 创建一个记录监听器
// Open record storers = RecordStore.openRecordStore(REC_STORE, true);...// Using handle to open record store, create a listenerrs.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");     }




 

结束语

在                 J2ME 101 系列的这第三篇文章中,您了解了如何在自己的 MIDP 应用程序中创建和管理数据记录。文章开头部分介绍的便利方法使您可以在记录存储中写入和读取记录。                RecordEnumeration类使您可以在 RMS 的所有记录之间移动。结合                RecordComparator 时,它可以返回排序的记录,结合                RecordEnumeration 类时它可以搜索指定的记录。如果将这三者 ―― 枚举器、比较器和过滤器 ―― 结合在一起,您将可以查找特定的记录,并按顺序返回结果。最后讨论的、但并非最不重要的API:                RecordListener可以用于为应用程序设置事件通知。           

几周之后,我们将以对 MIDP中网络支持的全面介绍来结束这个系列,请密切关注。