论文部分内容阅读
摘要:本文介绍J2ME技术中的RMS(Record Management System)、一些MIDP高级UI的使用以及文件流操作,比较了J2ME技术中查询应用的关键技术,并简析了一个手机号码归属地查询的实例,还介绍了开发过程中的一些心得和体会。
关键词:J2ME;RMS;MIDP;文件流
中图分类号:TP311文献标识码:A文章编号:1009-3044(2007)15-30690-03
Based on J2ME Technology Inquiry Applied Research
QU Cheng-yuan
(Mathematics and Information Science Department of Zhangzhou Normal University, Zhangzhou 363000, China)
Abstract:This article introduced in J2ME technology RMS(Record Management System), some MIDP high-level UI use as well as thedocument flow the operation, inquired the application essential technology compared with the J2ME technology in, and Jan has analyzeda handset number ownership inquiry example, but also introduced inperformance history some attainments and the experience.
Key words:J2ME; RMS; MIDP; document stream
1 引言
在计算机技术突飞猛进发展的今天,移动设备的程序开发变得越来越备受开发者的青睐。而Java语言作为一种跨平台,在移动设备程序开发中也算是一支独秀。本文就是使用Java语言中的J2ME技术,开发了一个基于移动设备的实用实例程序。程序的功能实现了对全国手机号码归属地和归属地区号的查询。本实例中主要用到了部分MIDP高级UI,文中没有花大量的篇幅介绍J2ME技术的基础知识,而是简单介绍本实例的开发环境和本实例用到的部分MIDP高级UI开发基础,对于实例中的实际开发经验和技巧,是本文的重点,贯穿文章始终,本实例代码短小精练,注释详尽明了,希望对广大J2ME开发者提供一定的参考。
2 开发环境和运行环境
在开发J2ME应用程序之前,作者还必须选择这些程序的开发平台,J2ME开发平台有很多选择,比如WTK,Eclipse,SunOne,Jbuilder等。本实例,作者选择JbuilderX,JbuilderX 是非常理想的开发环境,JbuilderX自带了MobileSet,它内附J2ME Wireless Toolkit2.0,所以开发人员仅需简单配置好环境变量,便可直接进入JbuilderX进行开发了。因为作者开发的时候使用的模拟器是系统默认的,所以不需要设置J2ME,而对于JbuilderX的一个天然的瑕疵——光标定位问题,作者进行了改进,两个步骤:首先打开菜单Tools -> Editor options -> color,然后把Symbol的Attributes中Bold前的勾去掉,这样就可以正常编写代码了。
在开发J2ME应用程序之前,作者还必须选择这些程序运行的平台,亦即开发出来的J2ME程序运行在那一种手机或者是移动设备上面。在开发的过程中,作者需要对J2ME应用程序进行测试,大多数的J2ME开发环境都提供了各种各样的手机、移动设备模拟器,开发者可以首先在手机模拟器上测试你的J2ME程序。模拟器和真实的设备之间有一定的差别,但是程序只要在模拟器上通过了,那么问题不会很大。
J2MEWTK提供六种模拟器,分别是:Default Color Phone、Default GrayPhone、Minimum Phone、Motolola i85s、PalmOS Device、RIM Java Handle。这些模拟器虽然外观不一样,操作也不太一样,但是J2ME程序在其上运行的结果是不会有什么区别的,在真实的设备上也是没有什么区别的。在本实例中,作者使用JbuilderX开发平台默认的模拟器。这样也少了很多配置过程。
3 部分MIDP高级UI开发基础与开发经验
在本实例,主要用到了以下高级UI:
Form:Form是J2ME 里面一个比较重要的容器类型,可以说是集中了高级UI 中的精华,是开发当中常用的一个关键类。通常是往Form 里面添加个中Item 的子类(使用append()方法),从而达到让画面更加丰富的目的,每一个Item 的子类在同一时刻只能属于同一个容器,否则会引发异常。
StringItem :StringItem的作用就是在屏幕上显示一串字符,配合不同的外观类型,StringItem可以用按钮或者超级链接的外观呈现。StringItem有两个构造函数,其中需要三个参数的构造函数,第一个参数是该Item的Label,第二个是内容,第三个则是外观。外观有三种选择:PLAN、BUTTON以及HYPERLINK。需要两个参数的构造函数等同于使用PLAIN类型的外观。由于这些StringItem没有和Command关联在一起,所以即使使用三种不同的外观,但是看起来却没有什么不同。我们随时可以利用getAppearanceMode()取得StringItem所选用的外观。利用getFont()/setFont()可以用来取得/设定字型。getText()/setText()可以用来取得/设定内容。如果要存取Label,则必须使用定义与Item之中的getLabel()/setLabel()。
TextField:在用户有数据输入时,我们可以选择使用TextField,它用来输入单行数据,TextField的构造函数共有四个,第一个是TextField的标题,第二个是TextField的初始化内容,第三个是允许输入字符的最大长度(不管中文还是字母,都算一个字,因为Java是采用Unicode来表示文字的),第四个是限制类型。TextField的内容可以在程序中使用setString()/getString()来设定或者取出。最大长度可以在程序中使用getMaxSize()/setMaxSize()来取出或者设定内容。Size()可以容我们知道目前内容的长度。用来限制TextField类型很多,如TextField.NUMERIC只允许输入数字,这里不就一一讲解。
这里关于TextField的使用,作者有两点小经验:
第一,TextField的最大长度和限制类型,有些开发者指定最大的长度为8,可是在初始化内容却超过8个字。此时就会引发IllegalArgumentException,如果限制的类型和初始化的类型不一致,也会引发IllegalArgumentException。
第二,TextField初始化数据后,在计算机上模拟运行时候,运行结果正确,而安装到移动设备里面运行时,程序初始化后就卡住了,也不提示错误。后经查实,是WKT版本不匹配造成,为了增强程序的兼容性,建议尽量不初始化数据。
Ticker:Ticker是一个类似跑马灯的类,所有Displayable类的之类都可以加入Ticker。我们可以利用定义在Displayable类中的setTicker()设定来设定画面上的Ticker,或者用getTicker()取出画面内含的Ticker对象。
4 关键技术比较
在Java的体系结构里,我们通常会使用JDBC技术对数据进行操作,但是这是针对桌面平台或者企业用户的,对于处理能力和存储空间都十分有限的无线设备而言,必须有一种特殊的机制与之适应,MIDP2.0 规范里不支持全面的树型文件系统,但为我们提供了这样一种数据持久化机制——记录管理系统(Record Management System RMS)。记录管理系统就是一个小型的数据库管理系统,它以一种简单的,类似表格的形式组织信息,并存储起来形成持久化存储,以供应用程序在重新启动后继续使用。但是就是基于移动设备平台,RMS有自身的一些天然的弱势:
(1)对数据的处理没有JDBC方便;
(2)一条记录大小不能超过32 个Unicode 字符;
(3)最大记录存储大小64K。
基于移动设备的信息查询系统来说,后两点通常会限制系统的数据质量和数量。如果我们使用文件存储数据,则不会对信息有这么多的限制,只是对数据的时候要注意一下几点:
(1)将数据分类,分别存放到多个文件,这样很大程度上提高查询效率;
(2)对数据进行逻辑上的简化,也可以提高查询效率;
(3)在数据操作的过程中,避免使用Vector,使用定长的数组代替;
(4)数据编码采用统一的编码方式,如UTF-8,以免出现乱码。
5 开发实例
本实例实现了查询手机号码归属地和归属地区号的功能。
关于本实例的算法比较简单,我们知道,我国手机号码归属地城市名称和区号1:1的关系,手机号码的分配是以号码前7位为单位的。这样手机号码的前7位就是可以找到归属地。在程序里面定义了一个二维字符数组qh[][],qh[0][i]存放归属地的唯一区号,区号前面的0未保留,qh[1][i]存放归属地省份名称,qh[2][i]存放归属地二级城市名称,因为这个数据是一个比较固定的不大的数据,作者采用数组存储,这样也对数据的安全有一定的作用。
手机号码段的存放是在data文件夹下面的文件中,文件名的命名规则是phone*.txt,*为手机号码服务商的前3为,如phone131.txt就中国联通131号码段的数据文件。文件里面的格式是固定的。
如phone131.txt文件中有……0010,0190,10|……,1310010-1310190号码段归属地为区号为010的城市北京。
程序框架如下图:
下文是程序的部分核心代码,代码力求短小精练,并辅助适当的说明和注释。
private String[][] qh = {//这个数组存储区号与归属地省份和城市,这里只存储了四个数据
{"10","北京","北京"},
{"20","广东","广州"},
{"21","上海","上海"},
{"22","天津","天津"},
};
void findphonenum() { //查找号码归属地函数
findok = false;
int pnsub = Integer.parseInt(phonenum.getString().substring(3,7)); //输入号码的4-7位
String begin3str = phonenum.getString().substring(0,3);
//输入号码的前3位
String filename = "data/_phone" + begin3str +".txt";
//文件路径
InputStream is = getClass().getResourceAsStream(filename);
//输入文件流
InputStreamReader in = new InputStreamReader(is);
//读入文件流
int num = 0;
try {
while (in.read() != -1) {
num++;}
in.reset();
char[] pdata = new char[num];
//pdata用来存储文件流的字符数组
in.read(pdata); //读入文件流到字符数字
in.close();
int spcount = 0;
//存储当前的字符数组中的位置,第一次从0开始
String spstr = ""; //当前操作的字符串 如:0010,0190,10
String na = ""; //存储起始4-7位4位号码段
String nb = ""; //存储结束4-7位4位号码段
String qhstr = ""; //存储归属地区号
for (int i = 0; i < num; i++) {
if (pdata[i] == '|') {
spstr = "";
for (int j = spcount; j < i; j++) {
spstr += pdata[j]; //给当前操作的字符串赋值 如:0010,0190,10 }
spcount = i + 1;
//截取当前字符串中的起始号码段如0010
na = spstr.substring(0,spstr.indexOf(","));
spstr = spstr.substring(spstr.indexOf(",") + 1,spstr.length());
//截取当前字符串中的起始号码段如0190
nb = spstr.substring(0,spstr.indexOf(","));
//截取当前字符串中的归属地区号
qhstr = spstr.substring(spstr.indexOf(",") + 1,spstr.length());
//如果输入的号码段4-7位在当前的起始号码段与结束号码段之间,则找到该号码的归属地了,再用这个归属地的区号到数组中去找归属地省份和城市即可
if (Integer.parseInt(na, 10) <= pnsub &&Integer.parseInt(nb, 10) >= pnsub) {
for(int row = 0;row { if(qhstr.equals(qh[row][0]))
{ String ptype = "××手机号码"//存储服务商名称
//清除form上的内容,并显示查询结果
form.deleteAll();
form.append("号码:" + phonenum.getString() + "\n");
form.append("所属地:" + qh[row][1] + qh[row][2] + "\n");
form.append("所属地区号:0" + qhstr + "\n");
form.append("号码类型:" + ptype + "\n");
form.addCommand(returncmd);
form.removeCommand(pnfindcmd);
findok = true;
break; //如果查询到归属地,跳出循环
}}}}}}
catch (IOException e) {//出错处理
form.deleteAll();
form.append("号码文件读取错误!\n对不起,您输入错误\n或者软件还没有最新数据!\n");
form.addCommand(returncmd);
form.setCommandListener(this);
display.setCurrent(form);}
if(findok==false)//没有找到归属地
{form.deleteAll();
form.append("手机号码号码:" + phonenum.getString() + "\n");
form.append("所属地:未知\n");
form.append("所属地区号:未知\n");
form.append("号码类型:未知\n");
form.addCommand(returncmd);}}}
6 结束语
由于篇幅有限,本文只对部分MIDP高级UI介绍,也只列出了本实例的部分源代码,如果需要已经编译好的执行程序,请登陆作者的猫眼查询网站http://www.owleyes.cn下载。
参考文献:
[1]J2ME中文教程(1.01修正版). http://www.j2medev.com.
[2]王森. Java手机/PDA程序设计入门[M]. 电子工业出版社.
注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。
关键词:J2ME;RMS;MIDP;文件流
中图分类号:TP311文献标识码:A文章编号:1009-3044(2007)15-30690-03
Based on J2ME Technology Inquiry Applied Research
QU Cheng-yuan
(Mathematics and Information Science Department of Zhangzhou Normal University, Zhangzhou 363000, China)
Abstract:This article introduced in J2ME technology RMS(Record Management System), some MIDP high-level UI use as well as thedocument flow the operation, inquired the application essential technology compared with the J2ME technology in, and Jan has analyzeda handset number ownership inquiry example, but also introduced inperformance history some attainments and the experience.
Key words:J2ME; RMS; MIDP; document stream
1 引言
在计算机技术突飞猛进发展的今天,移动设备的程序开发变得越来越备受开发者的青睐。而Java语言作为一种跨平台,在移动设备程序开发中也算是一支独秀。本文就是使用Java语言中的J2ME技术,开发了一个基于移动设备的实用实例程序。程序的功能实现了对全国手机号码归属地和归属地区号的查询。本实例中主要用到了部分MIDP高级UI,文中没有花大量的篇幅介绍J2ME技术的基础知识,而是简单介绍本实例的开发环境和本实例用到的部分MIDP高级UI开发基础,对于实例中的实际开发经验和技巧,是本文的重点,贯穿文章始终,本实例代码短小精练,注释详尽明了,希望对广大J2ME开发者提供一定的参考。
2 开发环境和运行环境
在开发J2ME应用程序之前,作者还必须选择这些程序的开发平台,J2ME开发平台有很多选择,比如WTK,Eclipse,SunOne,Jbuilder等。本实例,作者选择JbuilderX,JbuilderX 是非常理想的开发环境,JbuilderX自带了MobileSet,它内附J2ME Wireless Toolkit2.0,所以开发人员仅需简单配置好环境变量,便可直接进入JbuilderX进行开发了。因为作者开发的时候使用的模拟器是系统默认的,所以不需要设置J2ME,而对于JbuilderX的一个天然的瑕疵——光标定位问题,作者进行了改进,两个步骤:首先打开菜单Tools -> Editor options -> color,然后把Symbol的Attributes中Bold前的勾去掉,这样就可以正常编写代码了。
在开发J2ME应用程序之前,作者还必须选择这些程序运行的平台,亦即开发出来的J2ME程序运行在那一种手机或者是移动设备上面。在开发的过程中,作者需要对J2ME应用程序进行测试,大多数的J2ME开发环境都提供了各种各样的手机、移动设备模拟器,开发者可以首先在手机模拟器上测试你的J2ME程序。模拟器和真实的设备之间有一定的差别,但是程序只要在模拟器上通过了,那么问题不会很大。
J2MEWTK提供六种模拟器,分别是:Default Color Phone、Default GrayPhone、Minimum Phone、Motolola i85s、PalmOS Device、RIM Java Handle。这些模拟器虽然外观不一样,操作也不太一样,但是J2ME程序在其上运行的结果是不会有什么区别的,在真实的设备上也是没有什么区别的。在本实例中,作者使用JbuilderX开发平台默认的模拟器。这样也少了很多配置过程。
3 部分MIDP高级UI开发基础与开发经验
在本实例,主要用到了以下高级UI:
Form:Form是J2ME 里面一个比较重要的容器类型,可以说是集中了高级UI 中的精华,是开发当中常用的一个关键类。通常是往Form 里面添加个中Item 的子类(使用append()方法),从而达到让画面更加丰富的目的,每一个Item 的子类在同一时刻只能属于同一个容器,否则会引发异常。
StringItem :StringItem的作用就是在屏幕上显示一串字符,配合不同的外观类型,StringItem可以用按钮或者超级链接的外观呈现。StringItem有两个构造函数,其中需要三个参数的构造函数,第一个参数是该Item的Label,第二个是内容,第三个则是外观。外观有三种选择:PLAN、BUTTON以及HYPERLINK。需要两个参数的构造函数等同于使用PLAIN类型的外观。由于这些StringItem没有和Command关联在一起,所以即使使用三种不同的外观,但是看起来却没有什么不同。我们随时可以利用getAppearanceMode()取得StringItem所选用的外观。利用getFont()/setFont()可以用来取得/设定字型。getText()/setText()可以用来取得/设定内容。如果要存取Label,则必须使用定义与Item之中的getLabel()/setLabel()。
TextField:在用户有数据输入时,我们可以选择使用TextField,它用来输入单行数据,TextField的构造函数共有四个,第一个是TextField的标题,第二个是TextField的初始化内容,第三个是允许输入字符的最大长度(不管中文还是字母,都算一个字,因为Java是采用Unicode来表示文字的),第四个是限制类型。TextField的内容可以在程序中使用setString()/getString()来设定或者取出。最大长度可以在程序中使用getMaxSize()/setMaxSize()来取出或者设定内容。Size()可以容我们知道目前内容的长度。用来限制TextField类型很多,如TextField.NUMERIC只允许输入数字,这里不就一一讲解。
这里关于TextField的使用,作者有两点小经验:
第一,TextField的最大长度和限制类型,有些开发者指定最大的长度为8,可是在初始化内容却超过8个字。此时就会引发IllegalArgumentException,如果限制的类型和初始化的类型不一致,也会引发IllegalArgumentException。
第二,TextField初始化数据后,在计算机上模拟运行时候,运行结果正确,而安装到移动设备里面运行时,程序初始化后就卡住了,也不提示错误。后经查实,是WKT版本不匹配造成,为了增强程序的兼容性,建议尽量不初始化数据。
Ticker:Ticker是一个类似跑马灯的类,所有Displayable类的之类都可以加入Ticker。我们可以利用定义在Displayable类中的setTicker()设定来设定画面上的Ticker,或者用getTicker()取出画面内含的Ticker对象。
4 关键技术比较
在Java的体系结构里,我们通常会使用JDBC技术对数据进行操作,但是这是针对桌面平台或者企业用户的,对于处理能力和存储空间都十分有限的无线设备而言,必须有一种特殊的机制与之适应,MIDP2.0 规范里不支持全面的树型文件系统,但为我们提供了这样一种数据持久化机制——记录管理系统(Record Management System RMS)。记录管理系统就是一个小型的数据库管理系统,它以一种简单的,类似表格的形式组织信息,并存储起来形成持久化存储,以供应用程序在重新启动后继续使用。但是就是基于移动设备平台,RMS有自身的一些天然的弱势:
(1)对数据的处理没有JDBC方便;
(2)一条记录大小不能超过32 个Unicode 字符;
(3)最大记录存储大小64K。
基于移动设备的信息查询系统来说,后两点通常会限制系统的数据质量和数量。如果我们使用文件存储数据,则不会对信息有这么多的限制,只是对数据的时候要注意一下几点:
(1)将数据分类,分别存放到多个文件,这样很大程度上提高查询效率;
(2)对数据进行逻辑上的简化,也可以提高查询效率;
(3)在数据操作的过程中,避免使用Vector,使用定长的数组代替;
(4)数据编码采用统一的编码方式,如UTF-8,以免出现乱码。
5 开发实例
本实例实现了查询手机号码归属地和归属地区号的功能。
关于本实例的算法比较简单,我们知道,我国手机号码归属地城市名称和区号1:1的关系,手机号码的分配是以号码前7位为单位的。这样手机号码的前7位就是可以找到归属地。在程序里面定义了一个二维字符数组qh[][],qh[0][i]存放归属地的唯一区号,区号前面的0未保留,qh[1][i]存放归属地省份名称,qh[2][i]存放归属地二级城市名称,因为这个数据是一个比较固定的不大的数据,作者采用数组存储,这样也对数据的安全有一定的作用。
手机号码段的存放是在data文件夹下面的文件中,文件名的命名规则是phone*.txt,*为手机号码服务商的前3为,如phone131.txt就中国联通131号码段的数据文件。文件里面的格式是固定的。
如phone131.txt文件中有……0010,0190,10|……,1310010-1310190号码段归属地为区号为010的城市北京。
程序框架如下图:
下文是程序的部分核心代码,代码力求短小精练,并辅助适当的说明和注释。
private String[][] qh = {//这个数组存储区号与归属地省份和城市,这里只存储了四个数据
{"10","北京","北京"},
{"20","广东","广州"},
{"21","上海","上海"},
{"22","天津","天津"},
};
void findphonenum() { //查找号码归属地函数
findok = false;
int pnsub = Integer.parseInt(phonenum.getString().substring(3,7)); //输入号码的4-7位
String begin3str = phonenum.getString().substring(0,3);
//输入号码的前3位
String filename = "data/_phone" + begin3str +".txt";
//文件路径
InputStream is = getClass().getResourceAsStream(filename);
//输入文件流
InputStreamReader in = new InputStreamReader(is);
//读入文件流
int num = 0;
try {
while (in.read() != -1) {
num++;}
in.reset();
char[] pdata = new char[num];
//pdata用来存储文件流的字符数组
in.read(pdata); //读入文件流到字符数字
in.close();
int spcount = 0;
//存储当前的字符数组中的位置,第一次从0开始
String spstr = ""; //当前操作的字符串 如:0010,0190,10
String na = ""; //存储起始4-7位4位号码段
String nb = ""; //存储结束4-7位4位号码段
String qhstr = ""; //存储归属地区号
for (int i = 0; i < num; i++) {
if (pdata[i] == '|') {
spstr = "";
for (int j = spcount; j < i; j++) {
spstr += pdata[j]; //给当前操作的字符串赋值 如:0010,0190,10 }
spcount = i + 1;
//截取当前字符串中的起始号码段如0010
na = spstr.substring(0,spstr.indexOf(","));
spstr = spstr.substring(spstr.indexOf(",") + 1,spstr.length());
//截取当前字符串中的起始号码段如0190
nb = spstr.substring(0,spstr.indexOf(","));
//截取当前字符串中的归属地区号
qhstr = spstr.substring(spstr.indexOf(",") + 1,spstr.length());
//如果输入的号码段4-7位在当前的起始号码段与结束号码段之间,则找到该号码的归属地了,再用这个归属地的区号到数组中去找归属地省份和城市即可
if (Integer.parseInt(na, 10) <= pnsub &&Integer.parseInt(nb, 10) >= pnsub) {
for(int row = 0;row
{ String ptype = "××手机号码"//存储服务商名称
//清除form上的内容,并显示查询结果
form.deleteAll();
form.append("号码:" + phonenum.getString() + "\n");
form.append("所属地:" + qh[row][1] + qh[row][2] + "\n");
form.append("所属地区号:0" + qhstr + "\n");
form.append("号码类型:" + ptype + "\n");
form.addCommand(returncmd);
form.removeCommand(pnfindcmd);
findok = true;
break; //如果查询到归属地,跳出循环
}}}}}}
catch (IOException e) {//出错处理
form.deleteAll();
form.append("号码文件读取错误!\n对不起,您输入错误\n或者软件还没有最新数据!\n");
form.addCommand(returncmd);
form.setCommandListener(this);
display.setCurrent(form);}
if(findok==false)//没有找到归属地
{form.deleteAll();
form.append("手机号码号码:" + phonenum.getString() + "\n");
form.append("所属地:未知\n");
form.append("所属地区号:未知\n");
form.append("号码类型:未知\n");
form.addCommand(returncmd);}}}
6 结束语
由于篇幅有限,本文只对部分MIDP高级UI介绍,也只列出了本实例的部分源代码,如果需要已经编译好的执行程序,请登陆作者的猫眼查询网站http://www.owleyes.cn下载。
参考文献:
[1]J2ME中文教程(1.01修正版). http://www.j2medev.com.
[2]王森. Java手机/PDA程序设计入门[M]. 电子工业出版社.
注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。