随着J2ME(java2 Platform Micro Edition)技术在手机领域的快速发展,在Java手机上运行的软件的功能也越来越丰富。移动信息设备描述(MIDP)通过记录管理系统 (Record Management System,RMS) 提供数据的持久存储。下面将以开发手机日记本为例,详细分析了RMS的存储原理,并给出开发手机日记本的设计原理与关键技术。
1.记录的定义
MIDP 对持久存储的数据局限于简单的字节数组 (byte array),且记录是按整条读写的,而不是按字段读写。
RMS 是用来管理记录的系统,一条记录是一个单一的数据项。RMS 没有对放入记录的内容加以限制:记录可以是数字、字符串、数组以及图像等任何一段连续字节可以描述的事物。如果将数据编码成字节数组,并且具备相应的解码手段,就能将其存入一条记录中,当然要受系统分配的容量大小的限制。
在RMS中,记录的长度是可变的,这样,解释记录内容的任务就完全依靠应用程序来完成。RMS只提供存储和惟一标识符。虽然这样分工导致应用程序趋于复杂化,但是它使得 RMS 小巧灵活,这是作为 MIDP 子系统的一项重要属性。所有的RMS类和接口在 javax.microedition.rms 包中定义。
2.记录存储
记录存储是记录的有序集合。记录不是独立的实体,每一条记录必须从属于一个记录存储,而且所有的存取都通过记录存储来实现。在创建一条记录时,记录存储为其分配一个惟一的标识符。该标识符是被称作记录 ID 的整数。加入记录存储的第一条记录的ID是1,第二条的记录ID是2,依此类推。记录ID不是索引,记录删除后不会对现存的记录进行重新编号,也不会影响后面记录的ID 值。
在 API级别中,用javax.microedition.rms.RecordStore类的实例表示记录存储,它提供了几个方法来管理记录,包括插入、更新、删除和列举。
每一个MIDlet Suite都会有属于自己的一个用于RMS的私有空间。可以通过jad描述文件事先规定Midlet Suite运行所必需的RMS空间大小,在手机内部存储空间中预存的一个空间,供由jad指定的jar包文件使用。在MIDP 2.0 以后,只要MIDlet开放了属于自己RMS空间的相应RecordStore的使用权限,那么这个RecordStore可以被Suite外部的其他MIDlet访问。
所有与RMS相关的API都集中在javax.microedition.rms包下,包括了一个主类、四个接口,以及五个可能的被抛出异常。
1.打开和关闭记录存储
方法openRecordStore(String recordStoreName,boolean createIfNecessary)用来打开一个记录存储,也可创建一个记录存储。如:
rs=RecordStore.openRecordStore(rs_name,true);
该方法返回一个RecordStore对象的实例,第二个参数表示若该记录存储不存在时是否创建。完成对记录存储的操作之后,调用closeRecordStore()将其关闭。当不再用一个打开的RecordStore时,要关闭它以节约资源。
2.添加和更新记录
使用方法addRecord() 添加新的记录到打开的记录存储。
int addRecord(byte[] data,int offset, int numBytes)
在data中指定希望保存的字节数组,在offset中指定数据显示排列中的数据开始位置,在numBytes中指定保存数据的字节数。返回值是新增加记录的id。
可以用 RecordStore.setRecord() 更新记录:
void setRecord(int recordId, byte[] newData, int offSet, int numBytes)
更改recordId中指定的记录,在newData中是新的数据字节。
3.读取记录
可以利用 RecordStore.getRecord() 的两种形式之一读取记录。第一种形式分配合适大小的字节数组并将记录数据复制到里面:
byte[] data = rs.getRecord( recordID );
第二种形式是从指定的偏移量开始,将数据复制到预分配的数组中并返回所复制的字节数量:
int numCopied = rs.getRecord( recordID, data, offset );
4.删除记录,删除记录存储
用 RecordStore.deleteRecord() 删除记录:
rs.deleteRecord( recordID );
用RecordStore.deleteRecordStore()删除记录存储:
RecordStore.deleteRecordStore(rs_name );
记录存储只有当其处于未打开状态时才能被删除。一个MIDlet套件只能够删除它自己的记录存储。在删除之前,需要确保当前的存储是处于关闭状态,如果改存储仍旧处于开启状态,那么删除记录将导致RecordStoreException异常抛出。如果该删除的存储记录本身不存在,那么这将会引起RecordStoreNotFoundException异常抛出。
5.列举记录
使用RecordEnumeration enumerateRecords(RecordFilter filter, RecordComparator comparator, boolean keepUpdated) 对记录进行列举操作,本文正是利用了该方法的rs.enumerateRecords(null,null,true)列举所有记录。代码如下:
RecordStore rs=null;
RecordEnumeration re=null;
rs=RecordStore.openRecordStore("test",true);
re=rs.enumerateRecords(null,null,true);//列举所有记录
byte[] data;
while(re.hasNextElement()){//是否有下一条记录
data = re.nextRecord();//取得下一条记录
System.out.println("record =" + data);
}
1.工作流程及运行画面
手机日记本的工作流程如图1所示。日记列表画面按创建日期显示日期列表,可进行日记的添加和显示内容。日记内容画面显示日记内容,可进行日记的编辑和删除。两个画面如图2所示。
图1 手机日记本的工作流程
图2 日记列表画面和日记内容画面
根据工作流程,要在各个画面中分别设定如表1所示的命令。
表1 画面中设定的命令
日记列表画面 |
添加 |
创建日记 |
显示 |
显示内容 |
日记内容画面 |
保存 |
保存日记内容 |
删除 |
删除日记 |
2.文件组成及实现功能
(1)Memo.java
表示日记的Memo类,关于日记的属性和方法都在里面。属性包括记录ID、创建日期和日记内容。实例方法有把日记内容变换成字节的toByes(),还有设置和获得日记的日期及内容等方法。
(2)MobileMemo.java
是MIDlet主类,日记列表画面和显示日记内容画面都是这个类实现的。其主要方法有:
reloadFromRSM()从记录存储读取所有记录到memos中。
changeMemoToRMS():访问记录存储,完成用户操作菜单的命令,包括:删除、添加和更新记录。
commandAction:事件处理程序,在列表画面中实现创建日记,显示日记内容。在显示日记内容画面中实现保存和删除日记。
1.属性
Memo类的属性有记录ID、日记的创建日期和日记内容。
private int recId;
private Date date;
private String content="";
2.实例方法toByes()
把日记的日期和内容变换成字节数组,是Memo类的重要方法,也是任何一个管理记录存储的程序必须要定义的方法。只有将日记的日期和内容转换成字节数组,才能将其存入一条记录中,利用ByteArrayOutputStream类和DataOutputStream类实现。
public byte[] toBytes(){
byte[] data = null;
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
//把创建日记和日记内容发送给输出流
out.writeLong(date.getTime());
out.writeUTF(content);
data = baos.toByteArray();
baos.close();
out.close();
}catch(Exception e)
{
}
return data;//返回转换结果
}
利用RecordEnumeration取得记录存储中的全部记录,通过Memo对象,保存在Vector型的实例变量memos中。而且从记录存储中取得全部记录后,可随时更新日记列表的画面。Vector中加入的对象是有序的,即按加入的顺序排列,并且能够根据索引访问,可以看成是可变大小的数组。
public void reloadFromRSM()
{
memos.removeAllElements();
Record Store rs = null;
RecordEnumeration re = null;
try
{
rs = Record Store .open Record Store (RS_NAME, true);
//列举所有记录
re = rs.enumerateRecords(null, null, true);
//取得记录
while (re.hasNextElement())
{
int id = re.nextRecordId();//取得Record Id
byte[] data = rs.getRecord(id);// 取得Record
Memo memo = new Memo(data);
memo.setRecId(id);//设定记录的Id
memos.addElement(memo);
}
…
//利用momes更新日记列表
initilizeList();
for (int i = 0; i < memos.size(); i++) {
Memo memo = (Memo) memos.elementAt(i);
//取出日记的日期date,利用Calendar转换成年月日表示,并添加到list中。
Calendar Now = Calendar.getInstance();
Now.setTime(memo.getDate());
String year=String.valueOf(Now.get(java.util.Calendar.YEAR));
String month=String.valueOf(Now.get(java.util.Calendar.MONTH)+1);
String day=String.valueOf(Now.get(java.util.Calendar.DATE));
list.append(year+"年"+month+"月"+day+"日",null);
}
属私有方法,供事件处理程序调用处理用户菜单命令时,通过不同参数,对记录存储进行保存、删除和添加记录操作。
private Memo changeMemoToRMS(Memo memo, Command c)
{
RecordStore rs = null;
try
{
rs = RecordStore.openRecordStore(RS_NAME, true);
if (c == ok) {
//保存日记内容
int id = memo.getRecId();
rs.setRecord(id, memo.toBytes(), 0, memo.toBytes().length);
} else if (c == delete) {
//删除日记
int id = memo.getRecId();
rs.deleteRecord(id);
} else if (c == add) {
//创建新日记
int id = rs.addRecord(memo.toBytes(), 0, memo.toBytes().length);
memo.setRecId(id);
}
…
如果当前是列表画面,可以添加新日记,可以取得选中的日记内容准备显示。如果当前是日记内容画面,可以保存日记,也可以删除日记。事件处理程序中创建新日记的createNewMemo()方法,保存日记内容的writeMemo()方法和清除日记内容deleteMemo()方法,都调用了前面的changeMemoToRMS()方法,通过不同参数执行相应的功能。
public void commandAction(Command c, Displayable d)
{
//如果当前画面是列表
if (d == list)
{
if (c == add) {
//add命令则创建新的日记
currentMemo = createNewMemo();
} else if (c == show)
{//show命令则取得日记内容准备显示
int index = list.getSelectedIndex();
if(index == -1)return;
currentMemo = (Memo) memos.elementAt(index);
}
//转到显示日记内容的画面
memoText.setString(currentMemo.getContent());
display.setCurrent(memoText);
//如果当前画面是日记内容
} else if (d == memoText)
{
if (c == ok)
{
//ok命令保存日记
currentMemo.setContent(memoText.getString());
writeMemo(currentMemo);
} else if (c == delete)
{
//delete命令删除日记
deleteMemo(currentMemo);
}
//转到日记列表画面
reloadFromRSM();
display.setCurrent(list);
}
}
RMS是MIDP中一个非常重要的子系统,它是J2ME应用程序进行持久性存储的唯一途径。持久性存储在编写应用程序的时候经常要用到,如记录游戏排行榜、记录用户名和密码等。本文通过手机日记本介绍了RMS的基础知识以及如何管理记录存储,在分析手机日记本工作流程的基础上,给出了实现的关键代码。工程在jdk1.5和j2me wireless toolkit 2.2下调试和模拟运行通过。
|