使用Java ME技术开发手机密码管理软件
陈跃峰
摘要:Java ME技术是3G开发主流的技术之一,本文将通过系统的方式介绍如何使用Java ME技术开发密码管理软件,使大家能够通过该文章快速熟悉Java ME技术,进入3G移动程序开发的大门!
关键字:Java ME 手机 密码 管理 记录存储
一、引言
随着3G时代的到来,手机程序开发作为一个新兴的平台,吸引了越来越多开发者的关注,特别是随着中国移动软件应用商店(Mobile Market)的推出,手机软件将迎来“百家争鸣”、“万花齐放”的时代,各种类型的手机软件将层出不穷,手机程序开发技术也将获得长足的发展和进步。
众所周知,现在主流的手机软件开发技术主要有以下几种:
l Java ME技术,旧称J2ME技术
l Symbian技术
l BREW技术
以及最近出现的iPhone和Android开发等。其中,Java ME技术以手机支持广泛,技术成熟和开发速度快等优势占据着手机软件开发领域的主导地位。本文将以Java ME技术开发手机密码管理软件为例,介绍Java ME技术的使用。
Java ME技术诞生于1998年,最初被称作J2ME技术,作为SUN公司针对嵌入式设备的一种开发技术出现,该技术现在在手机软件开发领域发展的比较成熟,市面上大概90%以上的手机支持该技术,是手机开发技术中最普及的技术之一。
二、Java ME开发环境配置
Java ME技术的集成开发工具(IDE)有很多,比较常见的有Eclipse、NetBeans和JBuilder等,本文以使用Eclipse3.5.0为例子,介绍开发环境的安装和配置。
无论进行Java语言的哪种技术的开发,都需要首先安装JDK,这个工具是Java技术开发的基础,可以到SUN公司的网站下载最新版本的JDK安装程序,下载地址为:
http://java.sun.com/javase/downloads/?intcmp=1281
Eclipse是一个开源、免费的Java开发工具,也是现在进行Java程序开发时主流的开发工具之一。Eclipse提供的丰富的功能,将使你的开发效率获得很大的提升。下载地址为:
http://www.eclipse.org/downloads/
下载“Eclipse Classic”版本的安装程序即可。
Eclipse的安装比较简单,只需要解压缩安装程序就可以实现安装了。启动时运行安装目录下的eclipse.exe就可以运行程序了。
如果需要进行Java ME技术开发,还需要安装SUN公司提供的通用的Java ME开发工具——Java Platform Micro Edition Software Development Kit,旧称WTK,下载地址为:
http://java.sun.com/javame/downloads/?intcmp=1283
如果需要使用Eclipse进行Java ME开发,还需要安装一个插件——MTJ,旧称EclipseME,该插件的下载地址为:
http://www.eclipse.org/dsdp/mtj/
下载完成以后,解压缩安装文件,覆盖eclipse中对应的文件夹就实现了安装。
以上4个软件都安装完成以后,基本的软件安装就完成了,但是,如果进行Java ME技术开发,还需要进行相关的配置:
1. 将Eclipse和WTK关联起来
选择Eclipse中的“Window-Preferences”,打开如下配置界面。再选择“Java ME-Device Management”,如下图1所示:
图1 设备配置界面
然后再选择右侧的“Import”按钮,打开导入设备界面,选择WTK的安装路径,如下图2所示:
图2 导入设备界面
最后选择“Finish”按钮完成设置,并选择图1中的“OK”按钮,就完成了设备的导入配置了。
2. 配置新建菜单
选择Eclipse中的“Window-Customize Perspective”,在打开的界面中选择“shortcuts”,选中该界面中的“Java ME”,按“OK”按钮完成配置。如下图3所示:
图3 配置新建菜单
该配置完成以后,在Eclipse的“File-New”菜单中将可以直接新建Java ME的项目和启动文件MIDlet。
经过这些配置以后,就可以完成了Java ME开发环境的安装和配置了,就可以开始进行《开源密码管理软件》的开发了。
三、密码管理软件开发
本部分将重点介绍《开源密码管理软件》的项目结构和代码实现。
1. 密码管理软件介绍
《开源密码管理软件》是一款运行在支持Java技术手机上的密码管理软件,使用该软件可以在手机中存储多个密码,可以对已经存储的密码信息进行查看、修改和删除。另外,本软件还包含生成随机密码功能,使用该功能可能可以生成符合指定规则的密码,从而确保密码不容易被别人猜出,最后为了保护用户存储的密码信息,该软件强制用户设置登录密码,使用该密码才能登录到软件中,从而保护用户的隐私安全。
2. 密码管理软件结构
在开发《开源密码管理软件》时,采用界面和逻辑相分离的技术,从而方便代码的维护,在该项目中实现的项目代码结构如下图4所示:
图4 项目代码结构
在该项目,总计包含4个包:midlet包是项目的入口类,rms包是记录存储操作功能类,ui包是项目界面类,每个界面通过一个类实现,util包存储生成随机密码的功能类。通过这种结构,将本项目中最核心的数据存储和密码生成逻辑独立成单独的类进行实现。
界面类代码和界面的对应关系如下表1所示:
表1 界面类和界面对应关系表
序号 |
代码名称 |
界面功能 |
1 |
AboutForm |
关于界面 |
2 |
AddPwdForm |
新增密码界面 |
3 |
AllPwdList |
显示所有密码界面 |
4 |
DisplayGenPwdForm |
显示生成的随机密码界面 |
5 |
DisplayPwdForm |
显示需要查看的密码信息界面 |
6 |
GeneratePwdSettingForm |
随机密码设置界面 |
7 |
HelpForm |
帮助界面 |
8 |
InputPwdForm |
登录密码输入界面 |
9 |
MenuList |
程序主界面 |
10 |
ModifyPwdForm |
修改密码界面 |
11 |
SettingLoginPwdForm |
设置登录密码界面 |
3. 核心技术讲解
1) 界面功能开发
界面是一个程序和用户进行交互的平台,几乎每个软件都需要进行一定量的界面开发。在本程序的实现中,总计包含11个界面,每个界面都由ui包中一个独立的类进行实现,在每个类中,都既包含界面功能的实现,也包含界面事件的处理。
由于篇幅所限,不能一个界面一个界面的进行介绍,只能介绍界面编程中比较核心的功能的实现。下面主要以“显示所有密码”界面的AllPwdList为例子介绍界面功能的开发。
l 界面切换功能实现
界面切换是界面编程中一个基本的功能实现。在Java ME界面编程中,由于手机屏幕只能显示一个界面,所以界面切换的功能实现比较简单。在本软件中,由于每个界面使用一个单独的类进行代表,所以切换界面就只需要显示对应的界面对象即可。
在本程序中的MIDlet入口类中:
/**界面控制对象*/
public static Display display;
public OpenPMMIDlet() {
display = Display.getDisplay(this);
声明了一个公共的静态界面控制对象display,并且在入口类的构造方法中完成了对象的初始化,这样无论在该MIDlet类中,还是在其它界面类中,都可以通过该display对象进行界面的切换。由于display对象是static的,所以在其它界面类中调用也很方便,例如在输入登录密码界面中,切换到程序主界面的切换代码为:
OpenPMMIDlet.display.setCurrent(a,new MenuList());
其中a为登录成功的提示界面。
l 显示所有密码界面制作
“显示所有密码”界面的功能是显示所有已经存储的密码信息,在该界面实现时,由于需要列表显示很多的密码,在Java ME高级界面编程中,界面类型主要有:
a) Form——窗口类型,适合制作比较复杂的界面
b) List——列表类型,适合列表显示信息
c) Alert——提示框,适合显示提示信息
d) TextBox——文本盒,适合显示和接受大量文本信息
所以选择List类型的界面进行实现。不选择Form主要是考虑简化界面的制作,且使用Form类型时,事件处理会比较复杂。
在该界面实现中,界面中显示已经存储的密码标题列表,如果未存储一条密码,则在界面中显示“未存储密码”条目,另外在该界面中添加了“查看”、“返回”和“删除”三个软按钮。
该界面类AllPwdList类的声明如下:
public class AllPwdList extends List implements CommandListener
在AllPwdList类的声明中,该类继承了List类,从而实现了列表类型的界面,实现了CommandListener接口,从而使该类具备事件处理的能力。
实现界面功能初始化的代码如下:
private void init(){
cmdOk = new Command("查看",Command.OK,1);
cmdBack = new Command("返回",Command.BACK,1);
cmdDel = new Command("删除",Command.OK,1);
addCommand(cmdOk);
addCommand(cmdBack);
addCommand(cmdDel);
//获得记录标题,并添加到列表中
label = RMSUtil.getAllLabel();
//添加到列表中
for(int i = 0;i < label.length;i++){
this.append(label[i], null);
}
//如果没有密码,则添加提示信息
if(label.length == 0){
this.append("未存储密码", null);
}
}
在该代码中包含的init方法实现了界面的初始化,该方法在AllPwdList类的构造方法中被调用执行。
在该方法中,首先调用Command类的构造方法,创建了三个软按钮对象,然后依次添加到当前界面中,再通过RMSUtil类的getAllLabel方法获得已经存储的所有密码的标题信息,该信息以String数组的形式返回,如果未存储密码,则该返回数组的长度为0。
当label数组获得以后,使用一个for循环依次将存储的信息添加到界面中,如果label数组的长度为0,则该for循环一次也不会得到执行。最后判断label数组的长度,如果长度为0,则在界面中显示“未存储密码”提示用户。
这样就完成了AllPwdList界面的制作。
l 显示所有密码事件处理
在Java ME开发中,采用的事件处理模型和Java AWT/SWING中的事件处理模型保持一致,都是采用事件监听器的模式进行处理,实现界面事件处理的步骤一般分为三个步骤:
a) 声明一个类实现监听器接口
b) 注册事件监听器
c) 在事件处理方法内部编写逻辑代码
在AllPwdList界面的事件处理中,需要处理软按钮类型的事件处理,则需要实现的接口为CommandListener接口,这样AllPwdList类不仅是一个界面类,也可以进行Command类型的事件处理,使用这种方式进行多界面开发时可以一个界面类以及该类的事件处理放置在一个类的内部,方便程序的开发。
基于以上的说明,注册事件监听器的代码如下:
this.setCommandListener(this);
该代码位于AllPwdList类的构造方法中,这样就是将当前界面的事件处理委托给当前对象处理。
对于Command类型的事件处理中,事件处理的逻辑代码只需要书写在commandAction方法内部即可,实现的代码如下:
public void commandAction(Command c, Displayable d) {
if(c == cmdOk || c == SELECT_COMMAND){
//如果没有密码项
if(label.length == 0){
return;
}
//获得选择
int index = this.getSelectedIndex();
//显示密码
if(index != -1){
OpenPMMIDlet.display.setCurrent(new DisplayPwdForm(index + 1));
}
}
if(c == cmdDel){
//如果没有密码项
if(label.length == 0){
return;
}
//获得选择
int index = this.getSelectedIndex();
if(index != -1){
RMSUtil.delPassword(index + 1);
Alert a = new Alert("开源密码管理软件");
a.setString("密码删除成功!");
OpenPMMIDlet.display.setCurrent(a,new AllPwdList());
}
}
if(c == cmdBack){
OpenPMMIDlet.display.setCurrent(new MenuList());
}
}
在commandAction方法中,传入的参数c代表被按下的Command按钮对象,d代表发生事件的界面对象,由于该方法由系统自动调用,则这些参数由系统进行传递。
在该方法内部,判断按下的按钮c是哪个按钮,然后进行对应的逻辑处理。其中如果按下的按钮时cmdOk查看密码或系统自动添加的“选择”按钮,则判断列表是否为空,如果为空则返回。如果不为空,则获得用户选择的密码序号,调用显示密码内容的界面显示密码信息。
2) 数据存储功能开发
在Java ME技术中,对于文件操作的支持是可选的,有些手机支持,有些手机不支持,所以使用文件系统存储数据不是最好的选择。而在Java ME技术中,专门提供了一个专用的数据存储结构——记录管理系统(RMS)进行数据的存储,该存储系统类似于一个小型的数据库系统,每个RMS都是数据库中的一张表格,该表格拥有一个几个名称,这个名称就是RMS名称,该表格只有两列:第一列为编号,该列由系统管理,从1开始编号,每次加1,类似数据库中的自动编号,第二列为数据,类型为byte数组,由用户存储数据使用。
在使用数据存储系统进行数据存储时,首先需要根据需要存储的数据进行数据存储结构的设计,然后再基于这种结构进行逻辑代码的编写。
在本软件中,需要存储的内容主要包含三种:1、登录密码,2、每个密码的标题,3、每个密码的密码内容。这里登录密码只需要存储一个,而后续的2和3则会包含多条内容。而且每个Java ME程序在手机上能够创建的RMS数量都受到限制,所以实现时需要尽量减少RMS的数量。根据以上需求,则设计出的RMS结构如下:
a) 设计一个单独的RMS,其中第一条记录用于存储登录密码
b) 设计一个单独的RMS,使用其中的相邻的2条记录存储一组密码记录。第一条记录存储第一组密码的标题,第二条记录存储第一组密码的密码内容,第三条记录存储第二组密码的标题,第四条记录存储第二组密码的密码内容,依次类推。
最后,为了方便密码的删除,所以又新增一个逻辑数据——有效记录的数量,这个数据也存储在一个单独的RMS中的第一条记录中。
这样本软件就设计出了3个RMS,分别存储登录密码、密码记录和有效记录数量。设计完成了存储结构以后,就可以编写存储功能的代码了。
l 登录密码存储控制
按照前面设计好的结构,登录密码存储在一个名称为“softPwd” RMS中,使用该RMS结构的第一条记录存储登录密码。所以读取登录密码时,只需要直接读取该RMS名称中的第一条记录内容即可,如果第一条记录不存在,则代表未设置登录密码。在存储登录密码时,则需要判断当前第一条记录是否存在,如果不存在则新增一条记录,如果第一条记录存在则覆盖该记录的内容。 读取的核心代码为:
//打开记录仓库
rs = RecordStore.openRecordStore(SOFTPWD, false);
//获得记录数量
int num = rs.getNumRecords();
//判断是否有记录
if(num > 0){
byte[] b = rs.getRecord(1);
return convertBytesToString(b);
}
存储的核心代码为:
//打开记录仓库
rs = RecordStore.openRecordStore(SOFTPWD,true);
//获得记录数量
int num = rs.getNumRecords();
byte[] b = convertStringToBytes(pwd);
//判断是否有记录
if(num > 0){
rs.setRecord(1, b, 0, b.length);
}else{ //新记录
rs.addRecord(b, 0, b.length);
}
l 记录数量存储控制
记录数量数据是实现程序中数据删除逻辑时,需要使用的一个数据,使用该数据控制密码记录的添加逻辑实现。
在名称为“num”的RMS中存储记录数量,也就是一个int数据,当新增密码存储时该记录的内容增加1,在删除密码记录时该数量的值减少1。
该数据的存储代码和登录密码的存储代码类似,这里就不再重复列举了。
l 密码记录存储控制
在该软件实现中,主要实现密码的存储以及控制。按照前面介绍的密码存储结构,在本软件中实现的主要逻辑有:存储密码、获得密码和删除密码三种功能。
存储密码功能的实现为,首先将需要存储的密码标题和密码内容分别转换为byte数组,然后判断存储密码的RMS中实际记录的数量是否大于有效记录数量,如果大于,则覆盖有效记录后续的内容,如果等于有效记录数量,则添加记录。实现的代码如下所示:
//打开记录仓库
rs = RecordStore.openRecordStore(PWD,true);
//转换为数组
byte[] bLabel = convertStringToBytes(label);
byte[] bPwd = convertStringToBytes(pwd);
//存储密码记录
int num = rs.getNumRecords();
int actualNum = 2 * getActualRecordNum();
//判断是添加还是覆盖
if(num == actualNum){
rs.addRecord(bLabel, 0, bLabel.length);
rs.addRecord(bPwd, 0, bPwd.length);
}else{
rs.setRecord(actualNum + 1, bLabel, 0, bLabel.length);
rs.setRecord(actualNum + 2, bPwd, 0, bPwd.length);
}
//有效记录数量增加1
addRecordNum();
获得密码功能是按照密码的序号显示密码的内容,在程序中只需要按照该序号读取RMS中的内容,然后放置在一个长度为2的数组中,该数组中第一个元素存储密码标题,第二个元素存储密码内容。实现的代码如下所示:
//打开记录仓库
rs = RecordStore.openRecordStore(PWD,false);
//获得记录
byte[] b1 = rs.getRecord(id * 2 - 1);
byte[] b2 = rs.getRecord(id * 2);
//初始化数组
s[0] = convertBytesToString(b1);
s[1] = convertBytesToString(b2);
删除密码功能是这三个功能中实现比较复杂的一个,在本软件中实现的是逻辑删除,而不是物理删除功能。实现删除的原理为:将后续的记录依次覆盖前面的记录内容,并减少实际记录的数量存储。例如RMS中总计有5组密码记录,删除第二组密码的逻辑实现为:首先用第三组密码记录覆盖第二组密码记录,再使用第四组密码记录覆盖第三组密码记录,依次类推,并减少实际的记录数量为4。实现的代码如下所示:
//打开记录仓库
rs = RecordStore.openRecordStore(PWD,false);
//移动后续的记录,覆盖当前记录
byte[] b1;
byte[] b2;
for(int i = id;i < getActualRecordNum();i++){
b1 = rs.getRecord(2 * i + 1);
b2 = rs.getRecord(2 * i + 2);
rs.setRecord(2 * i - 1, b1, 0, b1.length);
rs.setRecord(2 * i, b2, 0, b2.length);
}
//实际记录数量减少1
reduceRecordNum();
3) 随机密码功能开发
随机密码功能是本软件的一个特色功能,使用该功能可以生成一个符合要求的密码,方便用户的使用。实现的原理主要是通过随机数字实现。下面以最复杂的生产6-10位,内容为数字和字母组合的实现逻辑,介绍代码的实现。
需要实现以上要求的功能,程序实现的逻辑为:首先生成一个[6,10]之间的随机整数,然后循环该随机整数次,在循环内部每次再生成一个随机数字,如果该数字的值为0则生产一个[0,9]之间随机数字,如果是1则生产一个随机字母。通过这样的逻辑实现随机密码的生成。
实现随机密码的功能代码均包含在PWDUtil类中。
4) 中文问题解决办法
在进行中文存储时,需要解决的一个重要问题就是“中文问题”。因为不同的手机中使用的字符集不同,甚至有些手机允许用户在使用时设置手机使用的字符集,这样如果不做处理,就将造成存储时使用的字符集和读取时使用的字符集不一致,这样就会出现乱码。
解决该问题的思路就是在存储和读取时都使用固定的字符集,考虑到UTF-8字符集在Java手机上支持最为广泛,所以使用该字符集作为转换时的字符集,而转换的代码使用IO技术实现。
将字符串按照UTF-8字符集转换为byte数组的核心代码为:
//建立流
baos = new ByteArrayOutputStream();
dos = new DataOutputStream(baos);
//写入流
dos.writeUTF(s);
//转换为byte数组
return baos.toByteArray();
将读取出的byte数组按照UTF-8字符集转换为字符串的核心代码为:
bais = new ByteArrayInputStream(b);
dis = new DataInputStream(bais);
String utfStr = dis.readUTF();
return utfStr;
使用以上的代码就可以很方便的实现字符串和byte数组之间的转换了。
这里特别要说明的是,使用new String(b,”utf-8”)这样将byte数组b转换为字符串的方式在索爱K700和S700上不支持,所以这里才采用IO技术实现了转换。
四、项目实现步骤
前面介绍了项目代码的结构和核心代码的逻辑,如果需要运行该项目,还需要使用Eclipse进行实现,实现步骤为:
3. 建立项目
选择Eclipse中的“File-New-MIDlet Project”,在“Project Name”项目名称中输入“OpenPM”作为项目名称。选择“Configurations”中选择“Add…”按钮添加一个配置,完成项目的建立。
4. 新建MIDlet
选择Eclipse中的“File-New-Java Me MIDlet”,在打开窗口的“Package”后输入“midlet”包名,在“Name”后输入“OpenPMMIDlet”类名,然后选择“Finish”按钮完成创建。
5. 复制代码
选择Eclipse中左侧项目列表中OpenPM项目下的src文件夹,将本文的源代码(包含目录)复制,粘贴到“OpenPM”项目目录下的src目录中。
6. 运行项目
选择OpenPM项目中src目录中midlet包中的OpenPMMIDlet类,右键选择“Run As-Emulated Java ME MIDlet”运行该程序。程序运行的界面如下图5所示:
图5 程序运行界面
五、结束语
本文简单介绍了手机程序的各种开发技术,并系统介绍了使用Eclipse进行Java ME开发时开发环境的安装和配置,并以该环境为基础详细介绍了《开源密码管理软件》程序的开发过程、核心代码实现以及程序编写中需要注意的问题,通过本文,可以使开发者快速熟悉Java ME技术,进入移动程序开发的新领域!
参考文献
[1] 陈跃峰 《新编J2ME就业培训教程》 2006
[2] 张孝祥 《Java就业培训教程》 2007
[3] http://java.sun.com