■ 引言
在上讲中,我们认识到汲取音乐播放的专长,能够制作成拥有BGM和音效、表现力丰富的应用程序。在本讲中,我们主要就应用程序本身内部保持的数据保存、读取进行详细阐述。
■ 数据的保存读取
RMS:
要在应用程序中保存用户设定和高分等相关信息,所以必须在手机终端上的记忆中保存数据。MIDP上定义了称之为RMS(Record Management System)的数据库系统。RMS中有称为“Record store”的列表,其中存在名为Record 的entry。Record按照字节排列,用id可以识别。利用下面的类、接口可以操作Record Store和Record。
类、接口名 | 作用 |
RecordStore | 表示Record Store的类 |
RecordEnumeration | 列举Record的接口 |
RecordComparator | 定义Record comparator的接口 |
RecordFilter | 定义Record过滤的接口 |
RecordListener | 监视Record Store变更的接口 |
表 1
RecordStore
表示Record Store的是Record Store类。Record Store是用RecordStore 中的static方法的open Record Store制作的。
RecordStore.openRecordStore("RecordStoreName", true);
第2个自变量转化成“true”后,名为“Record Store Name”的Record Store存在的情况下,能够打开保存的Record Store。但是,名为“Record Store Name”的Record Store不存在的情况下,则会制作新的Record Store,并打开所制作的Record Store 。
第2个自变量转化成“false”后,名为“Record Store Name”的Record Store存在的情况下,能够打开保存的Record Store。但,不存在的情况下,则放弃Record Store Not Found Exception。
操作Record Store时,应该在操作前打开Record Store,并在操作完后关闭Record Store。利用刚刚介绍的Record Store的static方法——open Record Store打开Record Store。利用Record Store的引证方法的close Record Store按照下面的叙述可以关闭Record Store。在这儿将关闭的Record Store 例子表示为rs。
rs.closeRecordStore();
以下是表示针对 Record Store 的连贯操作。(ex. 1)
RecordStore rs = null; try{ // 打开Record Store rs = RecordStore.openRecordStore("RecordStoreName",true); 记述针对Record Store的操作 ? ? }catch(Exception e){ }finally{ if(rs != null){ try{ // 关闭Record Store rs.closeRecordStore(); }catch(Exception e){ } } } |
ex. 1
按照下面所述的Record Store的static方法清除Record Store。
RecordStore.deleteRecordStore("RecordStoreName");
RMS的限制
RMS是非常单纯的数据库系统,所以只需准备最小限度的操作。因此,一般数据库系统所具有的处理控制和系统故障的恢复功能,在RMS上是不能被支持的。
Record操作
增加Record
利用以下Record Store类的方法增加Record 。
int addRecord(byte[] data,int offset, int numBytes)
在data中指定希望保存的数据字节排列、在offset中指定数据显示排列中的数据开始位置、在numBytes中指定保存数据的字节数。能在numBytes中指定0。此时,null被收藏在Record中。另外,增加数据的Record的id作为返还值返还。
清除Record
利用以下方法清除Record 。
void deleteRecord(int recordId)
指定用recordId清除Record中的id。对应的Record不存在的情况下,放弃例外。而且,清除后的RecordrecordId也不能再次利用。
更改Record
利用以下方法更改Record 。
void setRecord(int recordId, byte[] newData, int offSet, int numBytes)
指定recordId中更改的Record,并在newData中指定新收藏的数据字节排列。对应的Record不存在的情况下,放弃例外。
获取Record
利用下面的方法获取Record。
int getRecord(int recordId, byte[] buffer, int offset)
byte[] getRecord(int recordId)
上述方法是指,从offset的位置上复制recordId方法指定的Record内容到buffer字节排列。返还值是所复制的数据的字节数。
下面的方法是指,recordId所指定的Record内容作为返还值返还。
Record Store的信息
利用如下方法可以获得Record Store操作和Record 操作之外的Record Store信息。
方法 | 功能 |
public int getLastModified() | 用long获取最后的修改时刻。用System.currentTimeMillis()形成能够取得的形式相同的形式。 |
public int getNextRecordID() | 获取下一个recordId。 |
public int getNumRecords() | 获取Record Store中的Record 数。 |
public int getVersion() | 获取Record Store的版本编号。版本编号通常在每次修改完Record Store后都会自动加1。 |
public int getSizeAvailable() | 获取Record Store的容许量。 |
public int getSize() | 获取Record Store的占有字节数。 |
public static String[] listRecordStores() | 全部获取Record Store名。 |
表 2
RecordEnumeration
RMS中准备了能够访问Record Store中的全部Record 的RecordEnumeration接口。由于厂商进行具体实际安装,所以开发者就没必要实际安装RecordEnumeration了。RecordEnumeration是具有双向链接的list,在各自的Note中都保存了recordId。
制作RecordEnumeration
利用下面的Record Store Instance方法制作RecordEnumeration。
RecordEnumeration enumerateRecords(RecordFilter filter, RecordComparator comparator, boolean keepUpdated)
一旦keepUpdated成为true,就会增加Record Store中的Record 的更改量,同时RecordEnumeration也会随着更新。成为False时,只要不呼出RecordEnumeration接口的rebuild()方法,即使在Record Store上增加变更,也不能反映,但可能会得到清除后的recordId。在Filter中将用于过滤Record的RecordFilter接口指定为实际安装的类的接口。此外,在comparator中将用于定义Record顺序号的RecordComparator接口指定为实际安装的类的接口。后面将详细说明。
访问Record
应用以下方法访问利用RecordEnumeration的Record。
方法 | 作用 |
void destroy() | 取消RecordEnumeration |
boolean hasNextElement() | 调查是否有下一个Record |
boolean hasPreviousElement() | 调查是否有前一个Record |
byte[] nextRecord() | 取得下一个Record |
int nextRecordId() | 取得下一个Record的ID |
byte[] previousRecord() | 取得前一个Record |
int previousRecordId() | 取得前一个Record的ID |
int numRecords() | 取得Record的数 |
void rebuild() | 再构筑RecordEnumeration |
void reset() | 将Record的pointer返回到开头 |
表 3
如下所示的是利用RecordEnumeration所取得的简单Record。
import javax.microedition.midlet.*; import javax.microedition.rms.*; /** /** RecordStore rs = null; } protected void pauseApp() { } } |
ex. 2
RecordFilter
用RecordEnumeration取得Record时,指定条件能够过滤Record。实际安装RecordFilter接口,该Instance在RecordEnumeration完成时就成为构造函数的自变量,所以能够过滤。RecordFilter接口中定义了如下方法。
boolean matches(byte[] candidate)
此方法中记述了过滤的条件,还记述了若条件适合则返回到true,若不合适则会返回到false。
以下显示的是实际安装完RecordFilter的TestFilter。此例中过滤了比用构造函数所指定的数值还低的数值。
import javax.microedition.rms.RecordFilter; /** |
ex. 3
在取得ex. 2的程序的RecordEnumeration画面中,将程序更改为
re = rs.enumerateRecords(new TestFilter(4),null,true);
在自变量上指定ex. 3的TestFilter时的运行结果如下所示。(图 1)。
图 1
RecordComparator
用RecordEnumerator取得Record时,能够进行利用RecordComparator接口所取得的Record的Comparator。RecordComparator中定义了下面方法。
int compare(byte[] rec1, byte[] rec2)
返还值中返还了以下3个定数。
RecordComparator.PRECEDES | 在Comparator上Record rec1到达Record rec2的前一位置时 |
RecordComparator.FOLLOWS | 在Comparator上Record rec1到达Record rec2的后一位置时 |
RecordComparator.EQUIVALENT | 在Comparator上Record rec1到达Record rec2的同一位置时 |
表 4
以下显示的是实际安装RecordComparator的TestComparator。在此例中,按照数值的大小顺序进行比测Record。(ex. 4)
import javax.microedition.rms.RecordComparator; /** /** |
ex. 4
取得ex. 2程序的RecordEnumeration的画面中,程序变更为
re = rs.enumerateRecords(new TestFilter(3),new TestComparator,true);
并自变量中指定ex. 4的TestComparator时的运行结果。该内容如下图所示(图 2)。
图 2
RecordListener
利用RecordListener接口,能够监视针对Record Store的变更。例如,同期取得两个Record Store的情况下,单方面的Record Store被更改时,能够实现在单方的Record Store上增加更改等的操作。
RecordListener中有recordChanged,recordAdded,recordDeleted等3个种类的事件,并分别定义了每个方法。
void recordAdded(RecordStore recordStore, int recordId)
void recordChanged(RecordStore recordStore, int recordId)
void recordDeleted(RecordStore recordStore, int recordId)
利用Record Store类中所准备的如下方法能够在Record Store 上登记RecordListener。
void addRecordListener(RecordListener listener)
void removeRecordListener(RecordListener listener)
以下是实际安装RecordListener的Instance。此示范程序中,可以监视并复制Record的操作。
import javax.microedition.midlet.*; /** /** RecordStore rs = null; protected void pauseApp() { } /** /** /** } |
ex. 5
下图是运行结果。(图3)该图能够确认Record 操作内容是被监视并被表示的。
图3
开始制作应用程序了。
手机记事本的说明
利用MIDP的RMS功能,制作手机简易记事本应用程序。
就是用户输入记事内容,保存输入的内容、并进行记事的编辑、清除等所谓的简单的记事本应用程序。
如下是画面结构。
Memo list画面 | 显示Memo list。可以进行Memo的重新制作和显示。显示每个Memo的最后访问日期和时间。 |
Memo画面 | 表示Memo的内容。可以进行Memo的编辑、清除。 |
下图是画面迁移图。(图 4)
图 4
手机记事本的实际安装
下面是实际安装的顺序
1. 制作模型
2. 制作画面的实物模型
3. 制作RMS的操作
1. 制作模型
在本记事本中,要制作成表示一个一个的记事的Memo类。Memo类中有最后访问日期和时间、记事内容等属性。(ex. 6)
import java.io.*; import java.util.*;
/** /** /** |
ex. 6
2. 制作画面的实物模型
接下来制作画面与画面之间的迁移。
本讲讲述利用高级API制作画面。Memo list用高级API的List表示,Memo画面则利用TextBox。
而且,还要在各个画面中设定命令。分别设定如下表格所示的命令。
Memo list画面 | ADD | 重新制作Memo |
SHOW | 显示Memo | |
Memo画面 | OK | 刷新Memo的内容 |
DELETE | 清除Memo |
以下内容是实际安装画面实物模型的具体步骤。对于实物模型,Sample Source命令实际安装所记述的处理后,则应用程序就完成了。
import java.util.Vector; import javax.microedition.lcdui.*; /** // 画面类 // 作成记事显示画面 } /** /** // 从Record Store读取信息 // 转换到Memo list画面 /** /** /** if (d == list) { } else if (c == delete) { // 转换到Memo list |
按照上面表格中的程序所示,则可完成实物模型,并能完成应用程序的大致框架。
3. 完成RMS的操作
马上就要完成RMS操作了。
现在我们只想在 Record store中原样保存Memo内容,所以就要在刚才作成的Memo类中进行精雕细琢。从取得Memo类字节排列方法和字节排列中增加制作Memo类的构造函数。而且,还能用Memo类保存Record ID。(Record store中保存的Record ID则不写入。)
利用ByteArrayOutputStream类和DataOutputStream类,变换字节排列中的类的内容。Memo类中记述了如下Instance方法。(ex. 7)
/** * 变换字节排列 */ 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; } |
ex. 7
还是从字节排列中制作作成Memo对象的构造函数吧!利用ByteArrayInputStream类、DataInputStream类,如下记述。(ex. 8)
public Memo(byte[] data){ ByteArrayInputStream bais = new ByteArrayInputStream(data); DataInputStream in = new DataInputStream(bais); try{ date = new Date(in.readLong()); content = in.readUTF(); }catch(Exception e){ } } |
ex. 8
为能保持Memo类中的Record ID ,增加一个属性。由此,则完成了Memo类。以下程序显示的是完成后的Memo类 。(ex. 9)
import java.io.*; import java.util.*;
/** /** /** } |
ex. 9
接下来,开始在MobileMemo类上制作RMS操作吧。RMS操作有如下四种方法。
- 全部取得Memo内容
- 重新制作Memo内容
- 保存Memo内容
- 清除Memo内容
全部取得Memo内容的方法中,利用RecordEnumeration全部取得Record,并从取得的Record字节排列中制作Memo对象。制作完成的Memo对象能够将Vector型的memos作为Instance变量加以定义,并在此保持。而且,从Record Store中全部取得完Record 后,则可以按照Record 更新Record Store画面。在此,定义了如下方法。(ex. 10)
/** // 从Record Store更新信息 */ public void reloadFromRSM() { memos.removeAllElements(); // 取得Record RecordStore rs = null; RecordEnumeration re = null; try { rs = RecordStore.openRecordStore(RS_NAME, true); re = rs.enumerateRecords(null, null, true); // 取得Record while (re.hasNextElement()) { int id = re.nextRecordId();// 取得Record Id byte[] data = rs.getRecord(id);// 取得Record Memo memo = new Memo(data);// 完成Memo memo.setRecId(id);// 设定Record Id memos.addElement(memo);// 增加Memo } } catch (Exception e) { } finally { // 取消RecordEnumeration if (re != null) { try { re.destroy(); } catch (Exception e) { } } // 关闭Record Store if (rs != null) { try { rs.closeRecordStore(); } catch (Exception e) { } } } // 更新Memo list } |
ex. 10
转换到Memo list画面时呼出ex. 10的方法。
接着,继续记述余下的操作。这些操作主要针对Record store进行更改。即使将每一步操作步骤都详细记述也是没关系的,为了简单化,现在将针对Record store的操作都整合在一个方法中,并记述根据if文的区分处理。(ex. 11)
/** * 重新制作Memo */ public Memo createNewMemo() { Memo memo = new Memo(); return changeMemoToRMS(memo, add); } /** /** /** // Memo的更改被反映到Record Store |
ex. 11
下面将完成的处理与命令处理结合在一起。(ex. 12)Memo显示画面中显示的Memo内容被保持为名为currentMemo的Instance变量,并且在Memo显示画面中,可以显示currentMemo内容。
/** * 命令处理 */ public void commandAction(Command c, Displayable d) { if (d == list) { // 转入Memo list |
ex. 12
■ 完成
本讲中所制作的源码如下所示:
运行后如下:
■ 总结
在本讲中,介绍了利用RMS可以进行数据保存和读取。由于利用这种功能,能够在应用程序内永久保存数据,所以能够制作成高级功能的应用程序。从下讲开始,计划介绍手机网络的有关知识。学习了第一讲[创建开发环境] 到第七讲的[网络],有关手机JAVA的相关技术应该有个大概的轮廓了。以这些基础的JAVA技术信息为基础,一定可以制作成出色的应用程序的!