■ 引言 在上讲中,我们认识到汲取音乐播放的专长,能够制作成拥有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.*; /** * 表示record store的内容 */ public class recordstoretest1 extends midlet { /** * 启动时运行的方法 */ protected void startapp() throws midletstatechangeexception { recordstore rs = null; recordenumeration re = null; try {
rs = recordstore.openrecordstore("test",true);//读取record store re = rs.enumeraterecords(null,null,true);// 列举record
// 取得并表示record byte[] data; while(re.hasnextelement()){ data = re.nextrecord(); system.out.println("record =" + data); }
} catch (exception e) { } finally { // 取消record 的列举 if (re != null) { try { re.destroy(); } catch (exception e) { } }
// 关闭record store if (rs != null) { try { rs.closerecordstore(); }catch(exception e){ } } } } protected void pauseapp() { }
protected void destroyapp(boolean arg0) throws midletstatechangeexception { } }
|
ex. 2 recordfilter 用recordenumeration取得record时,指定条件能够过滤record。实际安装recordfilter接口,该instance在recordenumeration完成时就成为构造函数的自变量,所以能够过滤。recordfilter接口中定义了如下方法。 boolean matches(byte[] candidate) 此方法中记述了过滤的条件,还记述了若条件适合则返回到true,若不合适则会返回到false。
以下显示的是实际安装完recordfilter的testfilter。此例中过滤了比用构造函数所指定的数值还低的数值。
import javax.microedition.rms.recordfilter; /** * record过滤 * 过滤被指定的数值以下的数值 */ public class testfilter implements recordfilter {
private int min;// 数值的下限
/** * 构造函数 */ public testfilter(int min){ this.min = min; }
/** * 过滤 */ public boolean matches(byte[] data) { if(data[0] > min){ return true; }else{ return false; } } }
|
ex. 3 在取得ex. 2的程序的recordenumeration画面中,将程序更改为 re = rs.enumeraterecords(new testfilter(4),null,true); 在自变量上指定ex. 3的testfilter时的运行结果如下所示。(图 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; /** * 比较record并定义comparator顺序 */ public class testcomparator implements recordcomparator { /** * 比较record */ public int compare(byte[] rec1, byte[] rec2) {
if(rec1[0] == rec2[0]){ return recordcomparator.equivalent; }else if(rec1[0] > rec2[0]){ return recordcomparator.precedes; }else { return recordcomparator.follows; } } }
|
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.*; import javax.microedition.rms.*; /** * 表示record store内容 */ public class recordstoretest2 extends midlet implements recordlistener{ /** * 启动时所运行的方法 */ protected void startapp() throws midletstatechangeexception { recordstore rs = null; recordenumeration re = null; try { rs = recordstore.openrecordstore("test",true);// 读取record store recordfilter filter = new testfilter(3); recordcomparator comparator = new testcomparator();
// 增加recordlistener rs.addrecordlistener(this);
// 将数据加入record store if(rs.getnumrecords() < 1){ byte[] data = new byte[1]; for(int i=0;i < 10;i++){ data[0] = (byte)i; rs.addrecord(data,0,data.length); } }
// 增加record byte[] data = new byte[1]; data[0] = (byte)3; rs.addrecord(data,0,data.length);
// 清除record rs.deleterecord(7);
// 更改record rs.setrecord(8,data,0,data.length);
// 取得并表示record re = rs.enumeraterecords(filter,comparator,true);// 列举record while(re.hasnextelement()){ data = re.nextrecord(); system.out.println("record =" + data[0]); }
} catch (exception e) { } finally { // 取消record 的列举 if (re != null) { try { re.destroy(); } catch (exception e) { } }
// 关闭record store if (rs != null) { try { rs.closerecordstore(); }catch(exception e){ } } } } protected void pauseapp() { }
protected void destroyapp(boolean arg0) throws midletstatechangeexception { } /** * 增加record 时的处理 */ public void recordadded(recordstore recordstore, int recordid) { try{ system.out.println(recordstore.getrecord(recordid)[0]+"is added."); }catch(exception e){ } } /** * 更改recordrecord时的处理 */ public void recordchanged(recordstore recordstore, int recordid) { try{ system.out.println(recordstore.getrecord(recordid)[0]+"is changed"); }catch(exception e){ }
} /** * 取消record时的处理 */ public void recorddeleted(recordstore recordstore, int recordid) { try{ system.out.println(recordstore.getname()+"id:"+recordid+"is deleted."); }catch(exception e){ } } } |
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.*; /** * memo类 */ public class memo {
private date date; private string content = "";
/********************************** * 构造函数 **********************************/ public memo(){ date = new date(); }
public memo(string content, date date) { this.content = content; this.date = date; }
/********************************** * getter & setter **********************************/ /** * 取得内容 */ public string getcontent() { return content; }
/** * 取得最后访问的日期和时间 */ public date getdate() { return date; } /** * 设定内容 */ public void setcontent(string string) { content = string; date = new date(); } /** * 设定最后访问的日期和时间 */ public void setdate(date date) { this.date = date; } }
|
ex. 62. 制作画面的实物模型 接下来制作画面与画面之间的迁移。 本讲讲述利用高级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.*; import javax.microedition.midlet.*; import javax.microedition.rms.*; /** * 手机记事本(画面实物模型版) */ public class mobilememo extends midlet implements commandlistener { // 画面类 private display display; private form inputform; private list list; private textbox memotext;
// 命令群 private command add = new command("add", command.ok, 1); private command show = new command("show", command.ok, 1); private command ok = new command("ok", command.ok, 1); private command delete = new command("delete", command.ok, 1);
/** * 构造函数 */ public mobilememo() { display = display.getdisplay(this);
// 完成memo list initializelist(); // 作成记事显示画面 initializememotext(); } /** * 进行memo list的初期化 */ private void initilizelist(){ list = new list("memo list", list.exclusive); list.addcommand(show); list.addcommand(add); list.setcommandlistener(this); } /** * 进行记事显示画面的初期化 */ private void initilizememotext(){ memotext = new textbox("memo", "", 100, textfield.any); memotext.addcommand(ok); memotext.addcommand(delete); memotext.setcommandlistener(this); } /** // 开始应用程序 */ protected void startapp() throws midletstatechangeexception { // 从record store读取信息 // 转换到memo list画面 display.setcurrent(list); } /** // 暂时停止应用程序 */ protected void pauseapp() {} /** // 结束应用程序 */ protected void destroyapp(boolean bool) throws midletstatechangeexception {} /** * 命令处理 */ public void commandaction(command c, displayable d) { if (d == list) { if (c == add) { // 重新制作memo
} else if (c == show) { // 取得memo
}
// 转换到memo显示画面 display.setcurrent(memotext); } else if (d == memotext) { if (c == ok) { // recordmemo内容 } else if (c == delete) { // 清除memo内容 } // 转换到memo list display.setcurrent(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.*; /** * memo类 */ public class memo {
private int recid; private date date; private string content = "";
/********************************** * 构造函数 **********************************/ public memo(){ date = new date(); }
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){ } }
public memo(string content, date date) { this.content = content; this.date = date; }
/********************************* * instance方法 *********************************/
/** * 变换字节排列 */ 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; }
/********************************** * getter & setter **********************************/
/** * 设定record id */ public int getrecid(){ return recid; }
/** * 取得内容 */ public string getcontent() { return content; }
/** * 取得最后访问的日期和时间 */ public date getdate() { return date; } /** * 设定内容 */ public void setcontent(string string) { content = string; date = new date(); } /** * 设定最后访问的日期和时间 */ public void setdate(date date) { this.date = date; }
/** * 设定record id */ public void setrecid(int id){ this.recid = id; } }
|
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 initilizelist(); for (int i = 0; i < memos.size(); i++) { memo memo = (memo) memos.elementat(i); list.append(memo.getdate().tostring(), null); } }
|
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内容 */ public void writememo(memo memo) { changememotorms(memo, ok); } /** * 消除memo */ public void deletememo(memo memo) { changememotorms(memo, delete); } /** * 在record store上反映memo的变更 */ private memo changememotorms(memo memo, command c) { recordstore rs = null; try { rs = recordstore.openrecordstore(rs_name, true); // memo的更改被反映到record store // 依据命令更改操作 if (c == ok) { // memo的编辑 int id = memo.getrecid(); rs.setrecord(id, memo.tobytes(), 0, memo.tobytes().length); } else if (c == delete) { // 清除memo int id = memo.getrecid(); rs.deleterecord(id); } else if (c == add) { // 重新制作memo int id = rs.addrecord(memo.tobytes(), 0, memo.tobytes().length); memo.setrecid(id); } } catch (exception e) { } finally { if (rs != null) { try { rs.closerecordstore(); return memo; } catch (exception e) { } } } return null; }
|
ex. 11 下面将完成的处理与命令处理结合在一起。(ex. 12)memo显示画面中显示的memo内容被保持为名为currentmemo的instance变量,并且在memo显示画面中,可以显示currentmemo内容。 /** * 命令处理 */ public void commandaction(command c, displayable d) { if (d == list) { if (c == add) { // 重新制作memo currentmemo = createnewmemo(); } else if (c == show) { // 取得memo int index = list.getselectedindex(); if(index == -1)return; currentmemo = (memo) memos.elementat(index); }
// 转入memo显示画面 memotext.setstring(currentmemo.getcontent()); display.setcurrent(memotext); } else if (d == memotext) { if (c == ok) { // recordmemo内容 currentmemo.setcontent(memotext.getstring()); writememo(currentmemo); } else if (c == delete) { // 清除memo内容 deletememo(currentmemo); } // 转入memo list reloadfromrsm(); display.setcurrent(list); } }
|
ex. 12■ 完成 本讲中所制作的源码如下所示: - mobilememo.java
- memo.java
运行后如下: ■ 总结 在本讲中,介绍了利用rms可以进行数据保存和读取。由于利用这种功能,能够在应用程序内永久保存数据,所以能够制作成高级功能的应用程序。从下讲开始,计划介绍手机网络的有关知识。学习了第一讲[创建开发环境] 到第七讲的[网络],有关手机java的相关技术应该有个大概的轮廓了。以这些基础的java技术信息为基础,一定可以制作成出色的应用程序的!
|