当前位置:文档之家› 郭克华J2ME移动开发实战教学视频讲义15

郭克华J2ME移动开发实战教学视频讲义15

郭克华J2ME移动开发

实战教学视频讲义

第15章 RMS基础编程

郭克华所有作品由ChinaSEI独家发布。

网址为:https://www.doczj.com/doc/7011219462.html,

对应视频可在https://www.doczj.com/doc/7011219462.html,上下载。

本讲义属于郭克华团队网友整理,比视频略有扩充,如果有文字等小错,请多包涵。

在不盈利的情况下,欢迎免费传播。

版权所有.郭克华

本讲义经过修正、扩充,连同视频,由清华大学出版社出版。

详细可查询https://www.doczj.com/doc/7011219462.html,/49067,

https://www.doczj.com/doc/7011219462.html,/product.aspx?product_id=20742080

第15章 RMS基础编程

【本章导读语】

在J2ME移动开发过程中,经常会出现数据需要持久存储的情况,如:游戏数据要存盘,怎么办?一种方法是存入文件。但是,并不是所有的手机都支持文件存储。

为了满足这种要求,MIDP中推出了一个记录管理系统(Record Management System, RMS),它和数据库管理系统很类似,相应的支持包为:

打开文档,可以看到,该包中只包含一个类:

这个类也就是进行RMS操作的基础。

RMS是MIDP中提供的数据持久化存储的支持,本章内容将特别针对RMS的基础开发进行讲解。

【15-1】RecordStore基本操作

〖实例需求〗

javax.microedition.rms中只包含一个RecordStore类,顾名思义,RecordStore是记录集的意思,里面可以存储一条条记录。为了便于理解,你可以将RMS和和数据库管理系统中的概念作简单类比:

RMS:记录管理系统,相当于数据库中的数据库管理系统。

RecordStore:记录集,相当于表格。

本例中将基于文档,利用MIDlet,来讲解RMS中RecordStore的维护,包括建立RecordStore、删除RecordStore、访问RecordStore基本信息等。

〖开发过程〗

第一步:了解基本知识。

我们打开文档,来看看javax.microedition.rms.RecordStore类。

首先,RecordStore类没有可用的构造函数。根据我们的经验,当一个类没有可用的构造函数时,有可能这个类是抽象类,供扩展的。但我们发现,该类不是抽象类。那就可能是另一种情况:该类的对象可以由一个静态函数来创建。

查看该类的成员函数,会发现有如下3个函数可以生成RecordStore对象:

(1):

J2ME移动开发实战教程

该函数有两个参数:

参数1表示记录集名称(区分大小写);

参数2表示如果记录集不存在,是否创建。

如下代码:

RecordStore rs1 = RecordStore.openRecordStore("RS1", true);

表示创建一个名为“RS1”的记录集,如果不存在,则创建。

(2):

该函数有4个参数:

参数1表示记录集名称;

参数2表示如果记录集不存在,是否创建;

参数3表示创建方式,一共有两种选项:

RecordStore.AUTHMODE_ANY:该记录集可以被任何其他套件访问;

RecordStore.PRIV ATE:该记录集不可被其他套件访问。

参数4表示其他套件是否可以进行写操作。

通过以上两个函数就可以创建记录集,可以简单理解为数据库中的建表。在实际开发的过程中,我们使用第一个函数即可。

继续查看文档,还可以发现,在RecordStore类中,关于记录集的维护,还有如下函数:

1:得到记录集占据的空间:

2:得到记录集名称:

3:关闭记录集:

4:列出系统中当前的所有记录集名称:

5:删除某个记录集:

x 2 x

第15章RMS基础编程

第二步:对RecordStore的测试。

(1)编写代码

建立项目Prj15_1,在里面创建MIDlet1,将代码改成如下形式:

MIDlet1.java

package prj15_1;

import javax.microedition.midlet.MIDlet;

import javax.microedition.midlet.MIDletStateChangeException;

import javax.microedition.rms.RecordStore;

public class MIDlet1 extends MIDlet {

protected void startApp() throws MIDletStateChangeException {

try{

//打开记录集rs1

RecordStore.openRecordStore("RS1", true);

=

RecordStore

rs1

//得到记录集rs1大小

System.out.println("RS1占据大小:" + rs1.getSize());

//得到记录集rs1名称

System.out.println("名称:" + rs1.getName());

//关闭记录集rs1

rs1.closeRecordStore();

//列出系统中的记录集

this.list();

//建立记录集rs2

RecordStore.openRecordStore("RS2", true);

=

rs2

RecordStore

//删除记录集rs1

RecordStore.deleteRecordStore("RS1");

//列出系统中的记录集

this.list();

}catch(Exception ex){

ex.printStackTrace();

}

}

public void list(){

String[] names = RecordStore.listRecordStores();

x 3 x

J2ME移动开发实战教程

System.out.println("------目前的记录集为------");

for(int i=0;i

System.out.println(names[i]);

}

}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {}

protected void pauseApp() {}

}

运行此MIDlet,出现手机界面,控制台打印如下信息:

以上效果说明,系统中可以建立多个记录集来存储数据。

读者也许会问,既然是持久化存储,在手机上应该永久保存。那么,在PC机的模拟器上,RMS文件是怎么保存的呢?

在windows平台中的默认安装下,找到C:\Documents and Settings\用户名称\j2mewtk\2.5.2\appdb\DefaultColorPhone,在里面可以看到相应的文件。如,在本题中,文件显示为:

【15-2】RecordStore记录操作

〖实例需求〗

javax.microedition.rms中只包含一个RecordStore类,如果说RecordStore就相当于数据库中的表格,那针对RecordStore也应该有一些增、删、改、查的方法。

本例中将基于文档,利用MIDlet,来讲解RecordStore中记录的维护,包括添加、删除、修改、遍历查询记录等。

〖开发过程〗

第一步:了解基本知识。

打开文档,找到javax.microedition.rms.RecordStore类。首先需要了解如下基本概念:1:RecordStore中存储数据比表格中简单,每一条记录都是一个字节数组。但是这样也带来了难度,有些内容,如对象,需要转为字节数组之后才能存入。

2:每条记录都有一个ID,第一条记录的ID号为1,依此类推。

3:记录集中间的记录被删除,后面的记录不会前移,它们的ID号不变。

x 4 x

第15章RMS基础编程

这几个基本概念在后面我们将会有专门的测试。

在RecordStore文档中,提供了对记录增删改查的功能,在这里一一列出:

1:添加记录:

该函数一共3个参数:

参数1为数据的字节数组;

参数2表示该字节数组写入RecordStore时,从第offset个字节截取,截取的字节数目由参数3决定。

例如,我们如果要将“中国人”存入记录集,代码如下:

RecordStore rs1 = RecordStore.openRecordStore("RS1", true);

String str = "中国人";

byte[] data = str.getBytes();

rs.addRecord(data, 0, data.length);

2:删除记录:

该函数有1个参数,表示删除某个ID处的记录,如果该ID处没有记录,则抛出异常。

例如,我们如果要将记录集中第2条记录删除,代码如下:

RecordStore rs1 = RecordStore.openRecordStore("RS1", true);

rs1.deleteRecord(2);

3:修改记录:

该函数较好理解,参数1被修改记录的ID位置,参数2表示新的数据,参数3和参数4表示对参数2数组的截取。

例如,我们如果将“中国人”存入记录集,然后改为“ChinaPeople”代码如下:

RecordStore rs1 = RecordStore.openRecordStore("RS1", true);

String str = "中国人";

byte[] data = str.getBytes();

rs.addRecord(data, 0, data.length);

x 5 x

J2ME移动开发实战教程

str = “ChinaPeople”;

data = str.getBytes();

rs.setRecord(1, data, 0, data.length);

4:查询记录包含以下几个功能:

(1):得到记录条数:

注意,当RecordStore中删掉一条记录之后,该函数的返回值会变小,但是由于被删除记录后面的记录并没有前移,因此系统中的最大ID号并没有减小。

如下代码:

RecordStore rs1 = RecordStore.openRecordStore("RS1", true);

String str1 = "中国人";

byte[] b1 = str1.getBytes();

rs.addRecord(b1, 0, b1.length);

String str2 = "郭克华";

byte[] b2 = str2.getBytes();

rs.addRecord(b2, 0, b2.length);

rs.deleteRecord(1);

System.out.println("当前纪录条数:"+ rs.getNumRecords());

删除第1条记录之后,系统打印的结果为:

当前记录条数:1

但是要注意,第2条记录并没有前移。也就是说第2条记录还是存在的。而第1条记录变为无效了。

(2):得到记录集中的下一个记录号:

该函数实际上是首先找到记录集中记录号的最大值,然后加1,就是记录集中的下一个记录号。当RecordStore中删掉一条记录之后,由于被删除记录后面的记录并没有前移,因此该函数的返回值不变。

如下代码:

RecordStore rs1 = RecordStore.openRecordStore("RS1", true);

String str1 = "中国人";

byte[] b1 = str1.getBytes();

rs.addRecord(b1, 0, b1.length);

String str2 = "郭克华";

byte[] b2 = str2.getBytes();

x 6 x

第15章RMS基础编程

rs.addRecord(b2, 0, b2.length);

rs.deleteRecord(1);

System.out.println("下一个记录号:"+ rs.getNextRecordID());

删除第1条记录之后,系统打印的结果为:

下一个记录号:3

(3):根据ID号获得数据的字节数组:

以上两个函数中,第一个函数使用较广。

如下代码:

RecordStore rs1 = RecordStore.openRecordStore("RS1", true);

String str1 = "中国人";

byte[] b1 = str1.getBytes();

rs.addRecord(b1, 0, b1.length);

String str2 = "郭克华";

byte[] b2 = str2.getBytes();

rs.addRecord(b2, 0, b2.length);

byte[] b3 = rs.getRecord(1);

String str3 = new String(b3);

System.out.println(str3);

系统打印的结果为:

中国人

(4):根据ID获得记录所占字节数:

如下代码:

RecordStore rs1 = RecordStore.openRecordStore("RS1", true);

String str1 = "中国人";

byte[] b1 = str1.getBytes();

rs.addRecord(b1, 0, b1.length);

x 7 x

J2ME移动开发实战教程

String str2 = "China";

byte[] b2 = str2.getBytes();

rs.addRecord(b2, 0, b2.length);

System.out.println("记录1占据空间为:"+ rs1.getRecordSize(1));

System.out.println("记录2占据空间为:"+ rs1.getRecordSize(2));

系统打印的结果为:

记录1占据空间为:6

记录2占据空间为:5

注意,因为一个汉字占用2个字节,所以“中国人”所占字节数为6。第二步:对RecordStore中增删改查的综合测试。

(1)编写代码

在项目Prj15_1中创建MIDlet2,将代码改成如下形式:

MIDlet2.java

package prj15_1;

import javax.microedition.midlet.MIDlet;

import javax.microedition.midlet.MIDletStateChangeException;

import javax.microedition.rms.RecordStore;

public class MIDlet2 extends MIDlet {

protected void startApp() throws MIDletStateChangeException {

null;

=

RecordStore

rs

try{

rs

RecordStore.openRecordStore("RS1", true);

=

System.out.println("添加:中国人");

"中国人";

=

String

str1

byte[] b1 = str1.getBytes();

0,

b1.length);

rs.addRecord(b1,

System.out.println("当前纪录条数:"+ rs.getNumRecords());

System.out.println("添加:郭克华");

=

"郭克华";

str2

String

byte[] b2 = str2.getBytes();

b2.length);

rs.addRecord(b2,

0,

System.out.println("当前纪录条数:"+ rs.getNumRecords());

x 8 x

第15章RMS基础编程

System.out.println("修改记录第一条记录为:中国");

=

"中国";

newStr

String

byte[] newBytes = newStr.getBytes();

newBytes.length);

0,

newBytes,

rs.setRecord(1,

//根据ID得到纪录

byte[] b = rs.getRecord(2);

System.out.println("第二条记录是:" + new String(b));

//得到第一条记录所占字节的大小

System.out.println("第一条记录所占字节的大小为:" + rs.getRecordSize(1));

}catch(Exception ex){

ex.printStackTrace();

}

finally{

try{

rs.closeRecordStore();

}catch(Exception ex){}

}

}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {}

protected void pauseApp() {}

}

运行此MIDlet,出现手机界面,控制台打印如下信息:

以上效果说明,RecordStore中提供了比较灵活的做法,来对记录进行增删改查。(2)对记录删除和ID的测试

前面已经说过,当RecordStore中删掉一条记录之后,getNumRecords函数的返回值会变小,但是由于被删除记录后面的记录并没有前移,因此系统中的最大ID号并没有减小。本节对这个原理进行测试。在项目Prj15_1中创建MIDlet3,将代码改成如下形式:

MIDlet3.java

package prj15_1;

import javax.microedition.midlet.MIDlet;

import javax.microedition.midlet.MIDletStateChangeException;

x 9 x

J2ME移动开发实战教程

import javax.microedition.rms.RecordStore;

public class MIDlet3 extends MIDlet {

protected void startApp() throws MIDletStateChangeException {

null;

=

RecordStore

rs

try{

RecordStore.openRecordStore("RS1", true);

=

rs

System.out.println("空记录集大小:" + rs.getSize());

System.out.println("添加:中国");

byte[] b1 = "中国".getBytes();

b1.length);

rs.addRecord(b1,

0,

System.out.println("当前纪录条数:"+ rs.getNumRecords());

System.out.println("当前记录集大小:" + rs.getSize());

System.out.println("添加:美国");

byte[] b2 = "美国".getBytes();

b2.length);

0,

rs.addRecord(b2,

System.out.println("当前纪录条数:"+ rs.getNumRecords());

System.out.println("当前记录集大小:" + rs.getSize());

System.out.println("删除第1条记录");

rs.deleteRecord(1);

System.out.println("当前纪录条数:"+ rs.getNumRecords());

System.out.println("当前记录集大小:" + rs.getSize());

System.out.println("第2条记录是:" + new String(rs.getRecord(2)));

System.out.println("第1条记录是:" + new String(rs.getRecord(1)));

}catch(Exception ex){

ex.printStackTrace();

}

finally{

try{

rs.closeRecordStore();

}catch(Exception ex){}

}

}

x 10 x

第15章RMS基础编程

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {}

protected void pauseApp() {}

}

运行此MIDlet,出现手机界面,控制台打印如下信息:

以上效果说明,记录删除之后,其他记录ID不变,RecordStore的大小也不会减小,但是getNumRecords函数的返回值会变小。

【15-3】电话簿中的RMS对象存储

〖实例需求〗

前面章节中我们都提到了将数据保存在RecordStore中,但是保存的是简单数据。在某些特定场合,我们需要保存在RecordStore中的可能不是简单数据。如将用户的通讯记录保存在RecordStore中时,就必须同时保存姓名和电话号码两个字段,这就牵涉到怎样将对象保存在RMS中的技术。本节中,我们将完成如下案例:

有一个Customer类,里面包含了两个属性:cname和phone,要求能够将Customer 类的对象存入RecordStore,然后读入。

〖开发过程〗

第一步:编写Customer类。

在Prj15_1中建立一个Customer类,并增加相应属性,代码如下:

Customer.java

package prj15_1;

public class Customer {

private String cname;

private String phone;

public String getCname() {

return cname;

}

public void setCname(String cname) {

https://www.doczj.com/doc/7011219462.html,ame = cname;

}

x 11 x

J2ME移动开发实战教程

public String getPhone() {

return phone;

}

public void setPhone(String phone) {

this.phone = phone;

}

}

第二步:了解基本知识。

我们打开文档,来看看javax.microedition.rms.RecordStore类。找到其中的“添加记录”的函数:

该函数中,参数1是一个字节数组,并不能传入一个对象,因此,现在的关键问题是:怎样将一个对象转化为字节数组?

https://www.doczj.com/doc/7011219462.html,ng.Object并没有提供将对象变成字节数组的方法。在这里,我们需要另辟蹊径。

这里我们要借助java.io包里面的几个类。

首先讲解怎样将一个对象变成字节数组。将对象变成字节数组相当于将对象中的每个成员变量写入流,然后将流变成字节数组。

打开文档,找到java.io包,在里面有一个类:java.io.ByteArrayOutputStream,在该类中有一个方法:

该方法能够将流中的内容转换为字节数组返回。但是,该方法并不能很方便地将对象中的成员写入流中;在文档中找到另一个类:java.io.DataOutputStream,该类的构造函数为:

能够将ByteArrayOutputStream对象传入,并且,该类中有大量的write方法可以支持将各种类型写入流中。如:

1:写字符串:

2:写整数:

等等。

例如,如果我们要将一个Customer对象cus中的cname和phone字段变成字节数组,就可以用下面的代码:

ByteArrayOutputStream baos = new ByteArrayOutputStream();

x 12 x

第15章RMS基础编程

DataOutputStream dos = new DataOutputStream(baos);

//通过dos将对象内容写入baos

dos.writeUTF(https://www.doczj.com/doc/7011219462.html,ame);

dos.writeUTF(cus.phone);

baos.close();

dos.close();

接下来讲解怎样将一个字节数组变为对象。将字节数组变成对象相当于从流中的字节数组中读入每个成员,然后包装为对象。

打开文档,找到java.io包,在里面有一个类:java.io.ByteArrayInputStream,该类有一个构造函数为:

能够将字节数组放入流中。但是,该方法并不能很方便地将字节数组中的数据读入;在文档中找到另一个类:java.io.DataInputStream,该类的构造函数为:

能够将ByteArrayInputStream对象传入。并且,该类中有大量的read方法可以支持从流中读取各种数据类型。如:

1:读字符串:

2:读整数:

等等。

例如,如果我们要将一个字节数组b中的数据从流中读入,然后赋值为Customer对象cus中的cname和phone字段,就可以用下面的代码:

ByteArrayInputStream bais = new ByteArrayInputStream(b);

DataInputStream dis = new DataInputStream(bais);

//从bais读取内容

=

new Customer();

Customer

cus

cus.setCname(dis.readUTF());

cus.setPhone(dis.readUTF());

bais.close();

dis.close();

第三步:编写代码。

(1)修改Customer代码

Customer类并没有提供将对象变为字节数组和将字节数组转为对象的方法,因此可以在里面自定义。在项目Prj15_1中,打开Customer.java,将代码改为:

Customer.java

x 13 x

J2ME移动开发实战教程

package prj15_1;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.DataInputStream;

import java.io.DataOutputStream;

public class Customer{

private String cname;

private String phone;

//将对象转为字节数组

public byte[] object2ByteArray() throws Exception{

new ByteArrayOutputStream();

=

baos

ByteArrayOutputStream

new DataOutputStream(baos);

=

DataOutputStream

dos

//通过dos将对象内容写入baos

dos.writeUTF(https://www.doczj.com/doc/7011219462.html,ame);

dos.writeUTF(this.phone);

baos.close();

dos.close();

//返回字节数组

return baos.toByteArray();

}

//将字节数组转为对象

public static Customer byteArray2Object(byte[] b) throws Exception{

new ByteArrayInputStream(b);

=

ByteArrayInputStream

bais

new DataInputStream(bais);

=

DataInputStream

dis

//从bais读取内容

=

new Customer();

cus

Customer

cus.setCname(dis.readUTF());

cus.setPhone(dis.readUTF());

bais.close();

dis.close();

return cus;

}

public String getCname() {

return cname;

}

public void setCname(String cname) {

https://www.doczj.com/doc/7011219462.html,ame = cname;

}

x 14 x

第15章RMS基础编程

public String getPhone() {

return phone;

}

public void setPhone(String phone) {

this. phone = phone;

}

}

(2)编写MIDlet

在项目Prj15_1中,建立MIDlet4,将代码改为:

MIDlet4.java

package prj15_1;

import javax.microedition.midlet.MIDlet;

import javax.microedition.midlet.MIDletStateChangeException;

import javax.microedition.rms.RecordStore;

public class MIDlet4 extends MIDlet {

protected void startApp() throws MIDletStateChangeException {

null;

=

RecordStore

rs

try{

=

RecordStore.openRecordStore("RS1", true);

rs

new Customer();

=

cus

Customer

cus.setCname("王强");

cus.setPhone("025********");

//转换为字节数组写入

byte[] b1 = cus.object2ByteArray();

b1.length);

0,

rs.addRecord(b1,

//读

byte[] b2 = rs.getRecord(1);

Customer.byteArray2Object(b2);

=

newCus

Customer

System.out.println("姓名为:" + newCus.getCname());

System.out.println("电话号码为:" + newCus.getPhone());

}catch(Exception ex){

ex.printStackTrace();

}

finally{

try{

x 15 x

J2ME移动开发实战教程

rs.closeRecordStore();

}catch(Exception ex){}

}

}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {}

protected void pauseApp() {}

}

运行此MIDlet,出现手机界面,控制台打印如下信息:

该结果表明,对象能够存进RMS,并能读入。

【15-4】备忘录编写

〖实例需求〗

在本节中,我们将根据前面所讲解的RMS开发的基本内容,以及对象读写的知识,制作一个简单的备忘录。

用户可能在将来的某个时间要作一件事情,但是怕忘记,因此,可以保存在手机内,等到在那个时候提醒。实际上,闹钟也是一种特殊的备忘录。本项目界面出现,效果如图15-1所示:

图15-1 界面效果

界面上有个Form,标题为:“备忘录界面”,在该界面中,有两个控件。一个是“请您输入内容”,在该控件中可以输入备忘录的内容,如“参加奥运会开幕式”;第2个控件是“请您选择时间”,是一个DateField,可以选择要参加的时间。如选择“2008年8月8日下午8点”之后,界面变为图15-1右边的样子。

x 16 x

第15章RMS基础编程

在界面右下方有一个“保存备忘录”按钮,选择此按钮,能够将该条备忘录记录保存在RMS中。注意,这里简单起见,我们的程序只能保存1条备忘录。

备忘录保存之后,界面关闭。重新打开该程序,界面效果如下:

图15-2 备忘录界面

注意,该界面中应该判断RMS中是否有备忘录,如果有,就显示在界面上;如果没有,则显示图15-1中的界面。

〖开发过程〗

第一步:系统分析。

显而易见,该程序,由于备忘录内容和时间是绑定在一起的,因此最好用到对象读写。而界面还需要能够判断是否有记录存储在RMS中。

不过,这里有一个特殊问题需要阐明。我们知道,对象写入RMS,要转为字节数组,本项目中我们首先应该编写Memo类,代码基本结构如下:

public class Memo{

private String content;

private Date date;

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

public Date getDate() {

return date;

x 17 x

J2ME移动开发实战教程

}

public void setDate(Date date) {

this.date = date;

}

}

前面说过,将一个对象变成字节数组时,应该将对象中的每个成员变量写入流,然后将流变成字节数组。将成员变量写入流,我们用到的是一个类:java.io.DataOutputStream,该类中有大量的write方法可以支持将各种类型写入流中。如:写字符串、写整数等等。但是,没有写Date对象的函数!

同样的道理,将一个字节数组变为对象时,实际上就是从流中的字节数组中读入每个成员,然后包装为对象。我们用到的是java.io.DataInputStream,该类中有大量的read方法可以支持从流中读取各种数据类型。如:读字符串、读整数等等。但是就是没有读Date 对象的函数!

怎么办呢?

打开Date文档,会发现里面有一个重要函数:

该函数相当于将一个Date对象转换成long型数据;

而Date的构造函数也有如下形式:

相当于将一个long型数据包装成Date对象;或者:

也相当于用一个long型数据设置Date对象的状态。

因此,对Date的读写,就完全可以转换成对long型数据的读写。而在java.io.DataOutputStream中,有如下函数:

能够向流中写出一个long型数据;在java.io.DataInputStream中,有如下函数:

能够从流中读入一个long型数据。

因此,写一个Memo对象的代码可以采用如下方案:

public byte[] object2ByteArray() throws Exception{

=

new ByteArrayOutputStream();

baos

ByteArrayOutputStream

new DataOutputStream(baos);

=

DataOutputStream

dos

//通过dos将对象内容写入baos

dos.writeUTF(this.content);

long time = date.getTime();

dos.writeLong(time);

baos.close();

dos.close();

x 18 x

相关主题
文本预览
相关文档 最新文档