日期:2014-05-20  浏览次数:20785 次

探索J2ME程序:如何使用记录管理系统

 MIDP规范要求运行平台提供某种稳固的存储手段(通过非挥发性的存储器,即掉电后存储的信息不会丢失的存储器)。RMS(记录管理系统)管理记录库,记录库即为二进制平面文件(flat file,由不包括重复组的一组同类型记录构成的文件)。记录库中的每一段数据都涉及到某一个记录,它还拥有一个由数字组成的记录ID号,ID在整个记录库中是互不重复的。每一个记录库的名字在创建它的MIDlet组件中也是互不相同的,MIDlet只可以访问它自己或同一组件中其它MIDlet所创建的记录库。当从设备(device)中删除MIDlet组件时,所有与MIDlet组件相关的记录库也全部被删除。
  javax.microedition.rms软件包包含了RecordStore类,RecordStore类提供了访问记录库中的数据的初步方法。该软件包还包含了对记录库中的记录进行排序、搜索的其它类。在本文中,为了简明起见,我将集中介绍RecordStore类,其它的类将在以后介绍。
  RecordStore类的运行
  
  RecordStore类的运行过程很简单明了。你可以用静态RecordStore.openRecordStore方法打开一个已有的记录库,这会为这个指定名字的记录库返回一个RecordStore实例。如果你指定的记录库的名字不存在,该方法也可以创建一个以改名字命名的新的记录库(参见createIfNecessary参数)。使用getRecord方法你就可以通过记录ID来得到ID对应的记录。你还可以用addRecord和setRecord方法分别实现添加记录和更新记录的功能。当你完成了对记录库的操作,别忘了用closeRecordStore来关闭这个记录库。
  关于记录ID的一个注释:
  在Sun公司的标准实现(Sun’s reference implementation)中,给定记录的ID号与它的插入次序是相同的。记录库中第一个插入的记录以1为它的ID号,第二个插入的记录以2为ID号,以此类推。尽管这听起来似乎很方便,但是实际上并不总是如此。MIDP规范只要求每个记录都有一个ID号——ID号是如何创建的问题则留给平台的实现者。
  RecordStore类的绝大多数方法都可以抛出一个或者多个RecordStoreException类型的例外。例外的子类和它们对应的意义如下所示:
  
  InvalidRecordIDException例外,当某个被调用的方法涉及一个不存在的记录ID号(当读或者更新某个记录时)或者记录ID号无效(当添加记录时)时,就会抛出InvalidRecordIDException例外。
  RecordStoreFullException例外,当RMS(记录管理系统)的存储器满时,就会抛出该例外。例如,用OpenRecordStore来打开一个指定名字的记录库时,即使没有更新记录库,它也有可能抛出RecordStoreFullException例外。
  RecordStoreNotFoundException例外,当某个被调用的方法涉及到一个不存在的的记录库时,就会抛出该例外。如果指定名字的记录库不存在,OpenRecordStore也可以创建拥有该名字的记录库。该例外也可能从一个奇怪的地方抛出:CloseRecordStore方法。
  RecordStoreNotOpenException例外,如果你试图访问一个尚未被打开的记录库(用OpenRecordStore方法打开记录库)时,就会抛出该例外。
  最后,由于被处理的对象是二进制数据,记录库访问方法(getRecord、addRecord和setRecord)均把记录数据做为字节数组处理。你会发现——除非你有些喜欢被虐待——java.io软件包的流处理类,如ByteArrayInputStream、ByteArrayOutputStream、 DataInputStream、和DataOutputStream,使用起来并不可靠。
  深入代码
  
  你可以从这儿下载最新版本的ExpensesApp应用程序的代码。在写本文时,我起初只想修改ExpenseInfo类,但是RecordStore类是由一种古怪(quirky)的方法实现的,这使得这么做会难以置信得复杂。(在别的地方需要好几个try嵌套语句)。结果,我对Expenses和DetailForm类也做了修改,从而简化了整个过程。
  
  事实上,绝大多数RMS(记录管理系统)都有类似的怪癖(quirk),把这些怪癖弄到一块会阻碍你的开发进度。下面是一些我曾遇到的问题:不同的类中的功能类似的方法应该有同样或者近似的名字,可是在RMS中不是这样;一些方法的名字“名不副实”,会误导你错误认识该方法的功能;还有一些方法会在编程者无法处理的情况下抛出例外。你会发现,如果你经常使用J2ME类,拥有一份MIDP的Java文档,或者至少一个有方法提示(method prompting)功能的编辑器是特别值得的。
  
  看看代码清单A,你会发现静态方法ExpenseInfo.LoadExpenses为了从RecordStore对象中读取数据而被修改了。当打开或者创建一个记录库后,我会遍历库中所有的记录,把每个记录中的数据提取到一个字节数组中,然后为每一个记录创建一个新的ExpenseItem实例,并用一个Vector返回整套条目。ExpenseInfo.LoadExpenses现在抛出RecordStoreException例外,closeRecordStore本身也会抛出例外,因此改变(代码)是不可避免的了,这样,Expenses类的构造函数也需要加上一个try语句。
  
  我还用RMS的API来更新了ExpenseInfo.save和 ExpenseInfo.delete方法。你可以在代码清单B中找到它们。这两个方法通过检查非零记录ID(私有ExpenseID变量)来检测记录是否存在于当前实例中。如果通过方法save来保存一个新的开销记录,就会调用addRecord方法来把该记录添加到记录库中。如果记录已经存在,save方法就调用setRecord方法来更新对应的记录。Delete方法工作过程与之类似,如果它的实例没有记录ID号就什么也不做(因为它还没有被保存),如果记录ID号(ExpenseID)非零,则调用deleteRecord方法来删除对应记录。
  
  还有进一步的工作需要完成
  我们已经让ExpensesApp应用程序前进了一大步,但是尚未全部完成。现在它还存在下列问题:
  
  删除函数还有错误(bug):在记录库文件的中部将会引发LoadExpenses,并抛出InvalidRecordIDException例外。因此,lsMain中的cmDelete命令不可靠。
  目前,开销条目按输入是到lsMain的顺序来显示的。如果按存储其中的数据来排序显示这些条目会比较有用。
  你能发现这些bug吗?
  请你考虑这个练习。首先,去掉Expense类的构造函数中的注释符,这样就恢复了先前被注释掉的删除函数。然后运行ExpensesApp应用程序,并添加若干个新的开销项目。接着,删除你刚才输入的第二个开销项目,并关闭ExpensesApp。当你再次进入ExpensesApp就会捕获到一个例外情况。想想这是为什么。