如何将MIDlet应用移植到BlackBerry


1 如何将 MIDlet 应用移植到 BlackBerry 作者: 邓明轩 王恒进 王志刚 2 目录 前言................................................................................................................................ 3 使用 rapc 直接转换 MIDlet .......................................................................................... 3 在 BlackBerry 开发环境中导入 MIDlet 项目 ............................................................... 5 键盘适配........................................................................................................................ 9 键盘类型................................................................................................................. 9 左右软键的处理................................................................................................... 10 滚轮操作的映射................................................................................................... 12 其它按键操作....................................................................................................... 13 提醒用户...................................................................................................................... 14 通过响铃提醒用户............................................................................................... 14 通过振动提醒用户............................................................................................... 14 通过状态灯提醒用户........................................................................................... 15 通过主屏幕标记提醒用户................................................................................... 15 通过应用图标提醒用户....................................................................................... 17 网络连接调整.............................................................................................................. 17 BlackBerry Enterprise Server(BES)方式 ................................................................ 18 BlackBerry Internet Servie 方式 ........................................................................... 18 Direct TCP 方式 ..................................................................................................... 18 Wi-Fi ...................................................................................................................... 18 WAP 1.x ................................................................................................................. 18 WAP 2.0 ................................................................................................................. 19 数据存储与共享.......................................................................................................... 21 使用 RMS 进行存储 ............................................................................................. 21 使用 RunTimeStore 进行存储 ............................................................................. 22 使用 Persistent Store 进行存储 ........................................................................... 23 使用 SQLite 进行存储 .......................................................................................... 24 使用全局事件来进行应用交互........................................................................... 25 接收推送数据.............................................................................................................. 26 应用自启动........................................................................................................... 26 推送侦听............................................................................................................... 28 侦听程序与主程序的交互................................................................................... 29 推送侦听的完整代码........................................................................................... 29 小结.............................................................................................................................. 31 3 前言 BlackBerry 平台是一个 java 平台,支持标准的 j2me,所以,对于 j2me 开发人员来讲, 进入 BlackBerry 开发世界的一个直接方法就是将现有的 MIDlet 移植到 BlackBerry 平台上。将 MIDlet 移植到 BlackBerry 上有不同的方法,从简单的程序转换到复杂的项目重写,可以适合 不同的开发人员和不同的项目。本文将逐一介绍把 MIDlet 程序移杆到 BlackBerry 平台上的 各种方法,让读者可以轻松进入 BlackBerry 开发世界。 注意,本文针对在 j2me 开发方面有一定经验并且有需要将现有的 MIDlet 应用移植到 BlackBerry 平台上的开发人员。如果你只是希望开发一个新的 BlackBerry 应用,请参考其它 BlackBerry 入门文章。 使用 rapc 直接转换 MIDlet 在 BlackBerry 手机上运行 MIDlet 的最简单方法是将 jar 文件直接安装到 BlackBerry 手机 上,BlackBerry 手机自身有能力将 jar 文件转换成 cod,然后安装运行。具体操作方式如下: 1. 将 MIDlet 对应用的 jad 文件和 jar 文件拷贝到 BlackBerry 手机的媒体卡上 2. 打开 BlackBerry 手机上的文件浏览器 3. 找到第一步拷贝的 jad 文件,并打开这个文件 4. 此时系统会出现类似于以下界面的应用下载界面,选择“下载”即可开始安装 当然,有条件的开发人员可以将 MIDlet 对应用的 jad 文件和 jar 文件放在公网上, BlackBerry 用户可以通过 BlackBerry 浏览器打开 jad 文件开始 OTA 安装该应用。 然而,很多情况下开发人员需要将 MIDlet 的 jar 文件转换成 cod 文件,让用户可以使用 更为正式的方法部署应用。将MIDlet应用转换成cod文件可以使用RIM提供的编译工具rapc。 rapc 应用是一个命令行编译工具,可以将 java 文件或者是 jar 文件转换成 cod 文件。其 实 BlackBerry 开发环境最终也是调用 rapc 将开发人员编写的代码编译成 cod 文件。所以,要 获得 rapc 这个应用,其中一个方法就是安装 BlackBerry 开发环境,不管是 Blackberry JDE 还 是 Eclipse Plug-in For BlackBerry 都会带有这个程序。如何安装的是 Elipse Plug-in For BlackBerry , 你 可 以 在 安装目录的 “\plugins\net.rim.ejde.componentpackxxxxxx\ components\bin”子目录中找到 rapc 应用。如果读者没有安装 BlackBerry 开发环境,也可以 尝试在网上直接查找 rapc 的下载地址,有一些第三方网站直接提供了 rapc 这个应用的下载。 4 如上所述,rapc 应用是一个命令行工具,使用时需要配置相关参数。rapc 应用的的参数 列表如下:  import= (用于指定 RIM APIs 对应用包和其它应用需要使用的包)  codename= (用于指定编译出来的应用名,一般使用 JAR 文件相同的名字)  -MIDlet (如果是 MIDlet 程序的话一定要使用这个参数,如果不是 MIDlet,比如只是 一个工具包,则不需要使用这个参数)  jad= (指定 JAD 文件名)  \filename_1.java \filename_2.java ... (指定所有需要编译的 java 文件名, 这种是指对 java 文件进行编译的情况)  \JAR_filename.jar (指定需要编译的 jar 文件名,这种是指对 jar 文件进行编译 的情况) 结合以上参数描述,将一个 MIDlet 程序转换成 cod 文件的命令如下: rapc –import= codename= -MIDlet jad= 其中是指文件 net_rim_api.jar 的完整路径,对应用是需要转换的应用 名,是指需要转换的 MIDlet 的 jad 文件名, 是指需要转换的 MIDlet 的 jar 文件名。 下面是一个 rapc 使用的实例: rapc import="c:\Program Files\Research in Motion\Blackberry JDE 4.5\lib\net_rim_api.jar" codename=virca -MIDlet jad=Virca.jad Virca.jar 该例中使用的是 BlackBerry JDE 开发环境中的 net_rim_api.jar,转换的 MIDlet 应用名为 virca。如果读者使用的 Eclipse Plug-In for BlackBerry,则 net_rim_api.jar 文件在安装目录的以 下目录中:“\plugins\net.rim.ejde.componentpackxxxxxx\components\bin”。 命令运行后所生成的 cod 文件可以通过另一个命令行工具 javaloader 直接加载到 BlackBerry 手 机 上 。 Javaloader 工 具 同 样 可 以 在 目 录 “ \plugins\net.rim.ejde. componentpackxxxxxx\components\bin”中找到,加载的命令如下: Javaloader –u load virca.cod 注意加载时需要将 Blackberry 手机通过 USB 线连接到 PC 上。 当然, 对于很多最终用户来讲,javaloader 并不是一个可选的方案,对于大多数最终用 户而言都会通过 BlackBerry Desktop Manager 加载应用。而通过 BlackBerry Desktop Manager 加载应用则需要提供对应的 alx 文件。公网上也有一些非官方的 alx 文件生成器,可以根据 cod 文件生成对应用的 alx 文件。其实 alx 文件是一个 xml 描述文件,对于开发人员来讲完全 可以自己编写一个。下面列出一个基本的 alx 文件样例,开发人员可以根据自己应用的相关 信息修改该样例从而生成一个 alx 文件。 MyCompany Copyright (c) 2009 MyCompany MyVClient.cod 5 本章节描述了使用 rapc 将 MIDlet 转换成 cod 的过程,因为 rapc 是一个命令行工具,开 发人员可以编写一个批处理文件让转换过程更加方便。 如果所处理的 MIDlet 有代码修改,开发人员可以在原有的开发环境对代码完成修改, 对项目进行编译,然后重复一次 cod 转换过程。在这种情况下更是建议使用批处理文件完成 cod 转换。但是,无论如何,如果这是一个需要做较大修改的项目,这种工作都给开发人员 带来很大不便。为了更好地使用 BlackBerry 的开发环境,让 MIDlet 开发修改过程更加方便, 开发人员可以将整个 MIDlet 项目导入到 BlackBerry 开发环境中,在 BlackBerry 开发环境中直 接修改调试应用,同时让开发环境直接生成 cod 文件。有关这一点在下一章节中详细描述。 在 BlackBerry 开发环境中导入 MIDlet 项目 如上一节所述,如果开发人员希望在 BlackBerry 开发环境中直接修改调试 MIDlet 项目, 可以将现有的 MIDlet 项目导入 BlackBerry 开发环境中。本节以 Eclipse Plug-in for BlackBerry 为例说明导入 MIDlet 项目的过程,在 BlackBerry JDE 环境中的操作类似,读者可以通过类似 的步骤将 MIDlet 项目导入到 BlackBerry JDE 环境中。 为了导入 MIDlet 项目,首先需要建立一个 BlackBerry 项目。打开 Eclipse Plug-in for BlackBerry,选择“File->New->Project…”新建项目。 在新建项目向导中选择“BlackBerry->BlackBerry Project”作为项目类型: 6 在项目名称中输入你希望创建的项目名,这个项目名可以和你的 MIDlet 项目名不同, 本例使用 MyMIDlet 作为项目名。 项目创建后展开项目,可以看到名为 src 的目录,该目录是用于存放源代码的,我们可 以在这里导入原 MIDlet 项目的源代码。选择 src 目录,点击右键并选择“Import…”。 7 在导入向导中选择导入类型,如果直接导入 java 源代码,可以选择“General->File System”,如果从 jar 文件中导入源代码,可以选择“General -> Archive File”,注意此时导入 的 jar 文件必须包含源代码,编译出来但是不带源文件的 jar 文件是不能用于导入的。本例 将从一个 MIDlet 项目中直接导入 java 源代码,所以这里以“General->File System”作为导入 类型。 在“File System”选择框中,选择希望导入的 MIDlet 项目的源代码目录,注意在“From directory”选择框中要选择源代码所在的目录(一般是 src),不要直接选择其中的子目录, 否则导入的包会不完整。本例导入 MyMIDlet 项目的源代码,该应用的包为“cn.searb”, 所 以 在 选 择 “ From directory ” 是 选 择 了 “ c:\blackberry\MyMIDlet\src ”, 而 不 是 “c:\blackBerry\MyMIDlet\src\cn”。选择了“From directory”后,该目录的子目录会被显示 8 在下方的详细列表中,在这里选择需要导入的目录,如果希望导入整个项目的话直接选择根 目录就可以,在本例中选择了根目录“src”,然后点击“Finish”完成导入。 在 导 入 MIDlet 项目的源文件后,需要指定 MIDlet 类 。 打 开 项 目 中 的 “BlackBerry_App_Descriptor.xml”, 在“General Information”的“Application type”中选择 “MIDlet”作为应用类型,这一步是必须,如果不做选择的话,编译器会认为这是一个普通 的 BlackBerry 应用而不是一个 MIDlet。指定类型后还需要输入 MIDlet 类名,在“Name of main MIDlet class”一栏中输入 MIDlet 类名,注意要输入完整的包名,同时注意不要输入 java 或 者是 class 后缀。本例中输入的是“cn.searb.MyMIDlet”,指定包“cn.searb”中的类“MyMIDlet” 作为入口。 保存“BlackBerry_App_Descriptor.xml”后检查有没有其它错误,没有错误就可以开始调 9 试这个 MIDlet 了,对项点击右键,选择“Debug As”或者是“Run As”都可以开始测试。 测试完成之后可以在项目目录的“deliverables\Standard\5.0.0”目录中找到可以部署的 cod 文件,对应的 jad 文件也可以在这个目录中找到。 键盘适配 开发人员将 MIDlet 项目导入 BlackBerry 开发环境之后,可以在 BlackBerry 开发环境中直 接修改和调试代码。如上所述,BlackBerry 平台支持标准的 j2me,所以普通的 MIDlet 应用 程序在导入之后也不需要做代码修改就可以在 BlackBerry 上运行。不过,BlackBerry 手机的 键盘与普通手机有所不同,所以在一些情况下需要修改或者是增加键盘操作。下面详细介绍 如何对键盘进行适配。 键盘类型 大部分 BlackBerry 手机使用全键盘,有一部分 Blackberry 手机使用“SureType”键或者 是触摸屏。不管是哪种类型的 BlackBerry 手机都有菜单键和退出键,其中一些类型的 BlackBerry 手机使用特定按键充当菜单键。 下面是目前主流的带轨迹板的全键盘手机上的菜单键和退出键分布,中间为轨迹板,支 持四向操作,轨迹板左边为菜单键,右边为退出键。 较早一些的 BlackBerry 手机使用轨迹球,使用轨迹球的 BlackBerry 全键盘手机的键盘分 布也类似: 使用“SureType”的 BlackBerry 手机在菜单键和退出键分布方面和全键盘 BlackBerry 手 机类似,只是宽度相对较小: 触摸屏的 BlackBerry 手机没有键盘,菜单键和退出键分布在触摸屏的下方,中间靠左为 菜单键,中间靠右为退出键,中间没有轨迹球或者是轨迹板。 10 对于更早的 BlackBerry 手机,如 87 系列,它在键盘分布差别更大一些,它们使用滚轮 作为导航工具。BlackBerry 的滚轮分布在手机右侧,滚轮下方为退出键,使用点击滚轮的方 式激活菜单,但是滚轮点击事件不同于其它系列的菜单点击事件,对于这一点在代码上需要 特殊处理。 左右软键的处理 很多 j2me MIDlet 应用都使用左右软键响应用户操作,在屏幕左下方和右下方分别显示 左软键和右软键的标签,用户根据左右软键的标签提示决定点击左软键还是右软键,由程序 响应左右软键事件从而响应用户操作。 但是,如上一节所述,在 BlackBerry 手机上并没有左右软件,所以需要对这样的操作进 行适配。首先要明确的是如果开发人员使用“Command Action”添加操作,这些操作会被自 动添加到 BlackBerry 的菜单中,用户点击菜单键的时候可以看到这些“Command”。对于这 种情况则不需要调整原来的代码。 如果开发人员自行绘制左右软键,使用键盘事件侦听左右软键的话,则需要对程序进行 调整,因为在 BlackBerry 手机上没有左软键或者是右软键的事件发生。现在在公网上看到的 一种简单的调整方法是将全键盘中的“Q”键映射为左软键,将“P”键映射为右软件。使 用这种方法开发人员只需要修改键盘侦听代码,当侦听到“Q”键点击事件时执行左软键响 应代码,当侦听到“P”键点击事件时执行右软键响应代码。这种方法虽然简单可行,但是 有一定的限制,比如在一个可编辑的域被选中时不能工作,否则用户将无法输入 Q 字母和 P 字母。另外,这种方法也不符合 BlackBerry 用户的操作习惯。 一个较为合适的方法是将菜单键映射成左软键,将退出键映射为右软键。使用这种方法 的做法是获取菜单键点击事件和退出键单点事件,在这两个事件的响应代码中执行左软键的 11 代码或者是右软键的代码。有一些开发人员尝试使用标准的 j2me 方式获取 BlackBerry 上的 菜单键事件和退出键事件,发现这两个键无法通过标准的 j2me 方式获取。在这里菜单键事 件和退出键事件的获取成为关键。 在 BlackBerry 手机上,菜单键事件和退出键事件需要通过 KeyListener 来实现。开发人员 可以为一个应用添加一个 KeyListener 作为侦听器,当这个应用在运行同时有按键被点击时, 该 KeyListener 侦听器的 keyDown 方法会被执行,开发人员可以实现这个 keyDown 方法,获 取当前被点击的按键的键值,通过键值的比较判断是哪个键被按下,如果是菜单键或者是退 出键被按下,则执行左软键或者是右软键的响应逻辑。 所开发的 KeyListener 需要实现接口 net.rim.device.api.system.KeyListener,如以下代码所 示: public class MyKeyListener implements net.rim.device.api.system.KeyListener 该接口有几个键盘相关的方法,其中最主要的是方法 keyDown,对应按键事件,代码如 下: public boolean keyDown(int arg0, int arg1) { 其中第一个参数是所按下的按键的键值,第二个值数是与上一次按键相隔的时间,不做 重复按键处理的情况下,一般只需要使用第一个参数。使用键值的方法是通过调用 Keypad 的 key 方法将键值转换成标准值,然后与 Keypad 类中对应用静态成员进行比较,如菜单键 的标准值为 Keypad.KEY_MENU,退出键的标准值为 Keypad.KEY_ESCAPE。其中类 KeyPad 在包 net.rim.device.api.ui 中,虽然是 ui 包中的类,但是我们只使用静态方法和成员,并不使用 ui 包中的类进行展现,不会和 MIDlet 的 UI 有冲突。 此外,方法 keyDown 有布尔类型的返回值,返回 true 的话表明事件已经被消费,系统 不会再响应该事件,返回 false 的话表明事件没有被消费,系统会继续响尖该事件。如果开 发人员希望点击菜单键响应左软键,同时系统不再处理该按键事件,则判断到菜单事件时需 要返回 true 作为返回值。 具体请参考以下侦听器的样例代码: package cn.searb.keymenu; import net.rim.device.api.ui.Keypad; public class MyKeyListener implements net.rim.device.api.system.KeyListener { public boolean keyChar(char arg0, int arg1, int arg2) { // TODO Auto-generated method stub return false; } public boolean keyDown(int arg0, int arg1) { if (Keypad.key(arg0) == Keypad.KEY_MENU) { // 执行菜单响应代码,即左软键响应代码 return true; 12 } else if (Keypad.key(arg0) == Keypad.KEY_ESCAPE) { // 执行退出键响应代码,即右软键响应代码 return true; } return false; } public boolean keyRepeat(int arg0, int arg1) { // TODO Auto-generated method stub return false; } public boolean keyStatus(int arg0, int arg1) { // TODO Auto-generated method stub return false; } public boolean keyUp(int arg0, int arg1) { // TODO Auto-generated method stub return false; } } 开发了侦听器类以后在系统启动时需要将侦听器添加到应用中,添加侦听器需要调用应 用的 addKeyListener 方法。可以在 MIDlet 的构造函数中加入以下语句添加侦听器。 Application.getApplication().addKeyListener(new MyKeyListener()); 其中方法 Application.getApplication()用于获取当前应用,addKeyListener 则用于添加键盘 侦听器。 添加以上代码后 MIDlet 就可以通过菜单键和退出键响应左右软键了。有一点需要注意 的是 BlackBerry 用户习惯使用退出键完成“退出”,“取消”,“关闭”等否定性操作。所以在 移植 MIDlet 的时候尽量在右软键位置放置“退出”,“取消”或者是“关闭”等操作,而不 要放置“进入”,“打开”,“开始”等肯定性的操作。 滚轮操作的映射 如第一小节描述的,在较早的 BlackBerry 机型上,用户通过滚轮进行导航,在这些机型 上没有单独的菜单键,用户通过点击滚轮激活菜单。 在这种机型上,要响应滚轮点击事件需要实现额外的侦听器,因为滚轮不属于键盘的一 部分,在 KeyListener 中无法侦听到。对于其它按键,如退出键,各个字母键,虽然键盘分 布和新型的机型有所不同,但是都可以使用上一节描述的 KeyListener 进行侦听。 响应滚轮事件的侦听器为 net.rim.device.api.system.TrackwheelListener,开发人员需要实 现这个接口。该接口有滚轮滚动,滚轮点击等方法,要响应滚轮点击事件需要实现的方法为 trackwheelClick。 13 一个滚轮侦听器的样例代码如下: package cn.searb.keymenu; import net.rim.device.api.system.TrackwheelListener; public class MyTrackWheelListener implements TrackwheelListener { public boolean trackwheelClick(int status, int time) { // 执行滚轮点击响应代码 return true; } public boolean trackwheelRoll(int amount, int status, int time) { // TODO Auto-generated method stub return false; } public boolean trackwheelUnclick(int status, int time) { // TODO Auto-generated method stub return false; } } 开发了滚轮侦听器后可以通过应用程序的 addTrackwheelListener 方法为应用添加滚轮 侦听器,样例代码如下: Application.getApplication().addTrackwheelListener(new MyTrackWheelListener()); 其中 Application.getApplication()用于获取当前应用实例,addTrackwheelListener 用于添 加侦听器。 其它按键操作 在 blackBerry 手机上还有一些其它的按键,如静音键,音量键等,这些按键事件可以通 过标准的 j2me 方法,比如 Canvas 的 keyPressed 方法获取。 开发人员可以通过 keyPressed 方法对这些按键进行处理: protected void keyPressed(int keyCode) 参数 keyCode 为当时用户按下的按键,开发人员可以通过 keyCode==判断用户按下的是 哪个按键。在 BlackBerry 包中有类 net.rim.device.api.system.Characters,包含了很多常量,其 中包括了这些键的键值,如 Characters.CONTROL_VOLUME_UP 为增加音量键的键值, Characters.CONTROL_VOLUME_DOWN 为减少音量键的键值,开发人员可以使用这些常量进 行判断,避免在代码中直接使用键值进行比较。不过测试发现的问题是这些常量和实际的健 值不符,导致有些判断失效。所以,使用健值直接进行比较更加稳妥一些,比如增大音量键 的键值为-150,减少音量键的键值为-151,左快捷键的键值为-21,右快捷键的键值为-19。 当然,一个稳妥和简单的方法是在开发过程中通过 System.out.println()将键值打印到输出界 面上,从而测试不同键的键值。下面是按键检测的代码片段: 14 protected void keyPressed(int keyCode) { if (keyCode==-150) { System.out.println("volume up"); } else if (keyCode==-151) { System.out.println("volume down"); } } 使用以上方法也可以对一般的字母键进行处理。不过,对于字母键的处理,建议是尽量 使用高级界面自带的功能,从而避免直接对键盘事件进行响应。比如多利用输入框编辑框等 输入文字,可以更好地利用平台的功能,如自带的输入法等,不需要开发人员响应键盘事件。 对于特定的低级界面必须响应键盘事件时则需要考虑不同的键盘类型,主要是全键盘类型和 “SureType”键盘类型的差别,在不同机型上可能需要对不同的键进行响应。 提醒用户 手机应用的一个特点是充分和手机的功能集成,其中一个重要的功能就是通过手机的功 能对用户进行提醒。如一般常用的响铃,振动,闪灯等提醒。在 BlackBerry 平台上还可以通 过修改应用图标,增加主屏幕提醒标记等方式提醒用户。 通过响铃提醒用户 响铃是常用的用户提醒方式,一般 j2me 开发人员使用 Player 类加载一段声音,当有事 件发生需要提醒用户通过播放声音的方式提醒用户。因为 BlackBerry 平台支持标准的 j2me, 这种使用 Player 提醒用户的方法可以在 BlackBerry 平台上使用,不需要对代码进行修改。此 时播放声音的大小,时间长短都由程序决定。给用户带来的影响是即使用户将场景设置为“静 音”,即既不振动也不响铃,当该程序有提醒时仍会“响铃”。要改善这一点可以在应用内部 为用户提供一个设置,设置这个应用本身是否启用声音,是否启用振动等。 通过振动提醒用户 在标准的 j2me 中,通过 Display.vibrate()的调用可以让设备振动,该方法在 BlackBerry 设备上同样支持,有关振动的代码可以不加修改直接在 BlackBerry 手机上使用。此时设备是 否振动,振动时间多长完全由程序控制,不受系统设置影响。所以,这种方法与上一小节中 提到的响铃提示的方法有同样的问题,也同样可以通过相同的方法改善这个问题。 15 通过状态灯提醒用户 在 BlackBerry 平台上开发人员还可以通过闪烁状态灯来提醒来户,开发人员可以通过 API 打开状态灯、关闭状态灯、闪烁状态,此外,如果设备支持的话,还可以设置状态灯的 颜色。 BlackBerry 手机上的状态灯位置如下图: 用于控制状态灯的类为 net.rim.device.api.system.LED,它包括几个静态方法,主要的方 法为 setState,用于改变状态灯的状态。setState 方法接收一个参数,即希望改变成的目标 状态,该参数为 int 类型,但是只能接受以下常量中间的一个:LED.STATE_ON,LED.STATE_OFF, LED.STATE_BLINKING,LED.STATE_AUDIO_SYNC。常用的为 LED.STATE_ON,LED.STATE_OFF, LED.STATE_BLINKING,它们的效果为:  LED.STATE_ON 将状态灯打开  LED.STATE_OFF 将状态灯关闭  LED.STATE_BLINKING 使状态灯闪烁 在调用 setState 之前,最好先通过 LED.isSupported 方法判断当前设备是否支持状态灯变 化,判断是否支持状态灯变化的语句为 if(LED.isSupported(LED.LED_TYPE_STATUS)) 另外要注意,setState 方法和 isSupported 方法只能接受固定的几个参数,如果开发人员 没有控制好代码,传入了一些不能接受的参数,该方法会抛出 IllegalArgumentException 异常。 下面为打开状态灯的代码片段: if (LED.isSupported(LED.LED_TYPE_STATUS)) { LED.setState(LED.STATE_ON); } 通过主屏幕标记提醒用户 在 BlackBerry 手机上可以通过主屏幕的标记提醒用户。如下图所示,在电池图标右边的 是主屏幕标记,第一个是系统的邮件提示,表示有 1 封未读邮件,第二个也是系统提示,表 示有两条新短信。开人员可以根据应用需要添加应用自己的提示,如图中第三个标记是由程 序添加的标记,开人人员可以指定标记的图标图片,可以指定什么时候显示该标记,还可以 指定标记旁边的数字。 16 主屏幕标记通过 ApplicationIndicatorRegistry 和 ApplicationIndicator 进行操作。首先要通 过 ApplicationIndicatorRegistry 注册本应用的标记,然后通过 ApplicationIndicator 控制标记是 否显示,显示什么图标,显示什么数字。 具体的标记注册过程如下: 1. 通过 ApplicationIndicatorRegistry 的 静 态 方 法 getInstance() 获得 ApplicationIndicatorRegistry 的实例。 2. 调用 ApplicationIndicatorRegistry 实例的 register 方法注册标记。 ApplicationIndicatorRegistry 实 例 的 register 方 法 接 受 三 个 参 数 , 第 一 个 参 数 为 ApplicationIcon,用于指定标记所使用的图标,这个图标在以后可以通过程序更换;第二个 参数为布尔类型,指定该标记是否只有图标;第三个参数也是布尔类型,用于指定该标记是 否可见,标记是否可见在以后可以通过程序进行修改。 下面是注册标记的代码片段: EncodedImage readimage = EncodedImage .getEncodedImageResource("cn/searb/demo/icon/indicator.png"); ApplicationIndicatorRegistry indicatorRegistry = ApplicationIndicatorRegistry.getInstance(); ApplicationIcon icon = new ApplicationIcon(readimage); indicatorRegistry.register(icon, false, false); 在注册完标记后,应用程序在需要的时候就可以对标记进行操作,如修改图标,或者是 修改显示的数字等。标记的操作过程如下: 1. 通过 ApplicationIndicatorRegistry 的 静 态 方 法 getInstance() 获得 ApplicationIndicatorRegistry 的实例。 2. 通过ApplicationIndicatorRegistry 实例的getApplicationIndicator 方法获得当前应用的 ApplicationIndicator 实例。 3. 通过 ApplicationIndicator 实例的 setValue 方法设置要显示的数字 4. 通过 ApplicationIndicator 实例的 setIcon 方法设置要显示的图标 5. 通过 ApplicationIndicator 实例的 setVisible 方法设置该标记是否可见 其中第 3,4,5 步没有依赖关系,开发人员可以独立调用其中的一个或者是多个方法。 下面是标记操作的代码片段: ApplicationIndicatorRegistry indicatorRegistry = ApplicationIndicatorRegistry.getInstance(); ApplicationIndicator indicator = indicatorRegistry.getApplicationIndicator(); ApplicationIcon icon = new ApplicationIcon(readimage); 17 indicator.setValue(number); indicator.setIcon(icon); indicator.setVisible(true); 通过应用图标提醒用户 除了使用主屏幕标记以后,开发人员还可以使用改变应用图标的方式提醒用户。假如下 图为正常的应用图标: 开发人员可以通过 API 修改图标,提示用户该应用有新的事件发生,如下图: 修改应用图标相对比较简单,只需要调用 net.rim.blackberry.api.homescreen.HomeScreen 的静态方法 updateIcon 就可以更改本应用的图标,参数为一个 Bitmap 图片。 下面是修改应用图标的代码片段: Bitmap icon=Bitmap.getBitmapResource("cn/searb/test/unread.gif"); net.rim.blackberry.api.homescreen.HomeScreen.updateIcon(icon); 网络连接调整 BlackBerry 接口使用 j2me 标准的方法实现 Http 连接或者是 Socket 连接,在 MIDlet 中的 网络连接程序可以不加修改地在 BlackBerry 平台上运行。然而,因为 BlackBerry 提供更多的 网络连接选择,开发人员可以对网络连接代码进行调整,使用应用的网络连接更加稳定,更 加符合用户的网络选择。 因为 BlackBerry 需要支持标准的 j2me,所以还是通过 javax.microedition.io.Connector 类 实现各种连接。为了提供更多的网络选择,BlackBerry 平台在 Connector 的 open 方法中对 URL 进行了特殊处理,开发人员可以在 URL 后面加上一些特殊的参数以标明不同的网络连 接。比如,下面的语句在 URL 后面加上了“deviceside=false”,表明程序希望通过 BES MDS 连接网络: Connector.open(“http://www.myserver.com;deviceside=false”); 需要了解不同参数的详细情况,可以参考 API 文档中 javax.microedition.io.Connector 类 的说明。 下面我们根据不同的链路从整体上简单介绍 BlackBerry 上的不同连接方式。 18 BlackBerry Enterprise Server(BES)方式 这种方式通过使用 BES 的 BlackBerry MDS Services 来进行网络连接,BlackBerry MDS Services 负责处理所有的浏览器请求或者连接请求、并负责数据加密,这是黑莓手机的默认 连接方式,如下: (HttpConnection) Connector.open("http://www.testserver.com"); 以上代码会自动将 BlackBerry MDS Services 作为它的默认连接路径。实际开发中,如果 要确保应用程序使用 uses BlackBerry MDS Services 作为它的连接路径,需要在 URL 最后加上 参数“deviceside=false”,这也是我们推荐的方式,如下: (HttpConnection)Connector.open(“http://www.testserver.com;deviceside=false”); BlackBerry Internet Servie 方式 这种方式是为第三方提供的连接接口,它对数据不进行加密,用户可以通过使用 HTTPS 和 SSL 来进行安全的连接。注:目前仅对加入了 BlackBerry Alliance Program 的第三方开放合 作伙伴开放,详情参考:http://na.blackberry.com/eng/partners/alliance.jsp。 Direct TCP 方式 这种方式允许在没有使用BlackBerry MDS的黑莓手机上直接TCP连接。为了能启用direct TCP 方式,用户需要在手机的“选项-高级选项-TCP/IP”中设置 APN,以及相应的用户名和密 码。 运行在 iDEN 网络上的黑莓手机(包括 6510、7510、7520 和 7100i),如果不指定 deviceside 参数,默认的连接是 direct TCP;非运行在 iDEN 网络上的其他黑莓手机,如果不指定 deviceside 参数,默认的连接是 BlackBerry MDS。 如果连接时 BlackBerry MDS 不存在,黑莓手机也会自动采用 direct TCP 方式。 因此,如果要将 direct TCP 作为黑莓手机的默认连接方式,我们建议在 URL 中加入 “deviceside=true”参数,如下: (StreamConnection)Connector.open("socket://testserver:600;deviceside=true"); Wi-Fi 如果需要在 Wi-Fi 上创建网络连接,不需要在应用程序中考虑特别的底层逻辑,可以在 URL 中加入参数“interface=wifi”就能实现 Wi-Fi 连接,如下: (StreamConnection)Connector.open(“socket:// testserver:600;interface=wifi”); WAP 1.x 并不是所有的移动运营商都支持通过 WAP 网关进行连接的,所以如果要创建 WAP 连接, 19 开发者需要和移动运营商联系,获取是否提供这种支持、并且获取他们的 WAP 网管参数。 以下是一个基于 WAP 网关进行 HTTP 连接的例子: (HttpConnection)Connector.open("http://wap.google.com;WAPGatewayIP=127.0.0.1;WAPGatew ayAPN=carrier.com.gprs"); 其中 WAPGatewayIP 和 WAPGatewayAPN 这两项参数必须指定,参数之间用“;”隔开, 以下是所有的 WAP 参数列表,实际开发时请根据运营商提供的信息决定哪些参数需要设置。 WAP 2.0 如果手机上的 Service Book 上有 WAP2.0 的网关记录,可以通过 APN=的方法 指定网关,系统会从 Service Book 上找到对应的信息连接网关。不过,对于不同的运营商, WAP 2.0 网关的名称是不同的,如果在程序里通过网关名称指定所需要连接的网关,则有可 能在不同的运营商环境中出现连接问题。有一个相对通用的做法是通过程序遍历手机上的 Service Book,找到 WAP 2.0 的网关记录,然后通过该记录的 UID 进行连接,因为在 BlackBerry 上可以在 RUL 后面加上 ConnectionUID=的方式指定网关。 具体的做法是先通过 ServiceBook 类的 getSB 方法获得 Service Book,然后通过 ServiceBook 实例的 findRecordsByCid 方法查找在 Cid 中带“WPTCP”字样的记录。这些记录 里可能包含 Wifi 或者是 mms 对应的记录,所以要排除这些记录,使用的方法是判断在 Uid, 查找 Uid 里不带“Wifi”和“mms”字样的记录。 代码片段如下: ServiceBook sb = ServiceBook.getSB(); ServiceRecord[] records = sb.findRecordsByCid("WPTCP"); String uid = null; 参数 描述 WapGatewayIP 网关地址 WapGatewayAPN APN 的名称 WapGatewayPort 网关端口号,如果使用端口号 9203 的话,会自动启用“Wireless Transport Layer Security ” (WTLS),除非指定了 WapEnableWTLS=false 参数。 WapSourceIP Wap 服务器的地址。 WapSourcePort Wap 服务器的端口号。 TunnelAuthUsername APN 的用户名,当网关启用 Password Authentication Protocol (PAP) 或 者 是 Challenge Handshake Application Protocol (CHAP) 时才需要提供。 TunnelAuthPassword APN 密码,当网关启用 PAP 或者是 CHAP 时才需要提供。 WapEnableWTLS 指定是否启用 WTLS,如果没有指定该参数,当端口号为 9203 时会自动应用 WTLS。 20 for (int i = 0; i < records.length; i++) { if (records[i].isValid() && !records[i].isDisabled()) { if (records[i].getUid() != null && records[i].getUid().length() != 0) { if ((records[i].getUid().toLowerCase().indexOf("wifi") == -1) && (records[i].getUid().toLowerCase() .indexOf("mms") == -1)) { uid = records[i].getUid(); break; } } } } if (uid != null) { // 在参数中指定ConnectionUID Connector.open(_url + ";ConnectionUID=" + uid); } else { // 没找到Wap 2.0的servicebook,做其它处理. } 21 数据存储与共享 使用 RMS 进行存储 j2me 提供了 RMS 存储机制,MIDP 规范定义了持久的、基于记录的存储功能,叫记录 管理存储(RMS)。一个 MIDlet 套件可以使用 RMS 创建一个或多个记录存储,每个记录由一 个唯一的名字标识。在 javax.microedition.rms 包中可以找到必要的类和接口对 RMS 进行操 作,其中关键是的 RecordStore。RecordStore 提供了打开,关闭,读取,写入和更新操作, 也提供方法删除单个记录或者整个存储。这个包包含各种接口用于列举、排序和筛选 RMS 内容。 BlackBerry 平台提供了对标准 j2me 的支持,所以也支持标准的 RMS 存储机制,开发人 员在 MIDlet 中使用的 RMS 存储代码可以不加修改直接在 BlackBerry 上运行。 在 MIDP1.0 时候,每个 RMS 存储只属于创建它的 MIDlet 套件,不能用于不同程序之间 的数据共享。MIDP2.0 规范给 RMS 包增加了一个非常有用的能力:它允许一个 MIDlet 套件 和另一个 MIDlet 套件共享记录存储。 共享一个 RMS 记录存储需要两个或者多个参与者:一个拥有者和一个或者多个消费者。 拥有者负责创建和命名存储,建立授权模式指定存诸为“共享”或者“不共享”,同时可以 指定它的访问模式“可写”或者“不可写”。消费者通过名字获得记录存储的访问。消费者 不能访问没有共享的存储,也不能修改不可写的存储。 一个共享的 RMS 被一个三元组标识(提供者名字,MIDlet 套件名字,记录存储名字):  提供者名字是 JAD 或者 manifest 文件里面的 MIDlet-Vendor 属性的值。  MIDlet 套件名字是 JAD 文件里面 MIDlet-name 字段的值  记录存储名字是一个 1 到 32 位长的区分大小写的 Unicode 字符串,是当你创建记 录存储时候用的名字。 为了支持共享,MIDP2.0 标准在 javax.microedition.rms 中添一些方法: 第一个方法用于打开一个已经存在的记录存储,或者创建一个新的记录存储并设置它的 授权和可写特性。 static RecordStore OpenRecordStore (String recordStoreName, boolean create, int authmode, boolean writable); 参数说明如下:  recordStoreName 设置记录存储的名字  create,是否强制创建,如果为 true,存储不存在时会强制创建  authmode,用于指定授权模式: 使用 RecordStore.AUTHMODE_PRIVATE 话表示不允 许共享 ,使用 RecordStore.AUTHMODE_ANY 则允许共享,如果存储已经存在这个 参数会被忽略。  writeable,是否可写,如果为 true, 指定其他 MIDlet 套件可以修改这个记录存储, 如果存储已经存在这个参数会被忽略。 第二个方法用于打开共享记录存储: static RecordStore openRecordStore( String recordStoreName, String vendorName, String 22 suiteName) 参数说明如下:  recordStoreName 是要打开的共享记录存储的名字  vendorName 是拥有者 MIDlet 套件的 MIDlet-Vendor 属性的值  suiteName 是拥有者 MIDlet 套件的名字 仅当记录存储的拥有者设置了 automode 为 AUTHMODE_ANY 时这个方法才能成功打开 记录存储。需要注意的是你不能直接检测存储的可写属性。要发现一个记录存储是否可写的 唯一方法是试着向记录存储写入,如果不可写就捕获相应的异常。 记住在 JAD 或者 manifestMIDlet-Version,并不在参数列表中。这意味着拥有者套件无法 在影响消费者的前提下改变共享的存储的记录格式。 第三个方法用于修改存诸的属性: Void setMode( int authmode, boolean writable ); 参数说明如下:  authmode 指定记录存储的新的授权模式,可以设置为 AUTHMODE_PRIVATE 或者 是 AUTHMODE_ANY。  writable 指定存储的信息是否可写。 注意只有存诸的拥有者才能改变 authmode 和 writable 属性。另外,没有方法能够用 来查询这些设置。 如上所述,BlackBerry 平台提供 j2me 支持,所以开发人员可以在 BlackBerry 平台上使用 RMS 方法完成数据的存诸和共享。 使用 RunTimeStore 进行存储 相对于静态的RMS的繁琐配置和不灵活,BlackBerry提供了RunTimeStore(运行时存储), 使用非常灵活和方便。 RunTimeStore 在平台级别提供了数据存储方式和共享方式,它提供了一个中间区域让 不同应用可以共享对象,任何通过数字签名的应用程序都可以访问运行时存储。 对于 RunTimeStore 的使用需要注意的是:  对象可以添加到运行时存储或者从运行时存储中替换掉  运行时存储必须以一个独一无二的 ID 创建  任何类型的对象都可以放到运行时存储中  设备掉电的时候 RuntimeStore 中的数据会丢失。 下面是创建运行时存储和写入简单数据的代码示例: class CreateStoreDemo extends UiApplication { //通过包名生成的唯一的长整型ID public static long STORE_ID = 0x23ad23489a243L; public CreateStoreDemo() { String msg = “Shared text for another application”; RuntimeStore store = RuntimeStore.getRuntimeStore(); try { store.put(STORE_ID, msg); } catch (Exception ex) {} } } 23 创建好了以后就可以通过相应代码获取其中的数据,下面是读取 RunTimeStore 的代码示例: class ReadStoreDemo extends UiApplication { public ReadStoreDemo() { RuntimeStore store = RuntimeStore.getRuntimeStore(); try { //cast the returned object to a string String msg = (String)store.get(CreateStoreDemo.STORE_ID); } catch (Exception ex) { //handle exception } } } 使用 Persistent Store 进行存储 在 BlackBerry 平台上还可以通过 PersistentStore 存储数据,这种方法可以将对象直接保 存在设备内存中,需要使用的时候可以通过 API 从设备内存中直接读取出来,读取出来看获 得的是一个 object 对象,需要开发人员对该对象进行强制转换。 相比 RunTimeStore,PersistentStore 的好处是可以持久保存数据,即使设备掉电数据也 不会丢失。不过,使用 PersistentStore 要求被保存的数据必须实现 Persistable 接口。如下面 代码所示,如果希望将 MyData 实例保存在 PersistentStore 中,MyData 类需要实现 Persistable 接口: public class MyData implements Persistable { //其中为MyData的方法和属性定义。 } 在 PersistentStore 中保存的对象以一个长整型的 ID 作为标记,保存或者是获取该对象都 以这个长整型的 ID 作为参数。为了保证所保存的对象和其它应用保存的对象不冲突,可以 通过 hash 算法通过包名生成一个长整型 ID。BlackBerry Eclipse Plug-in 环境也提供了一个方 法将字符串转换成长整型,选中某一行字符串,点击右键,选择“Convert String to long”, 可以将选中的字符串转换成长整数。 无论用什么方式生成长整数,对象 ID 的定义语句都类似于以下代码: public static long PersistentID = 0x815402392d453a9dL; 定义了 PersistentID 后,可以通过 PersistentStore 的静态方法 getPersistentObject 获得所 保存的持久化对象,getPersistentObject 方法只有一个参数,为保存对象的 ID,本例使用上 面定义的 PersistentID。 获得持外化对象以后,可以通过该实例的 getContents 方法获得真正保存在设备内存的 对象,可以通过 setContents 将内存中的对象保存到持久化对象中。在获得对象的过程中记 得要将返回的 Object 实例强制转换为你使用的类。在保存对象时记得要调用 PersistentSotre 的 commit 方法完成保存动作。 PersistentStore 使用的代码片段如下: PersistentObject persistentStore; 24 persistentStore = PersistentStore.getPersistentObject(PersistentID); synchronized (persistentStore) { if (persistentStore.getContents() == null) { myData = new MyData(); persistentStore.setContents(myData); persistentStore.commit(); } else { myData = (MyData) persistentStore.getContents(); } } } 使用 SQLite 进行存储 在 BlackBerry 5.0 以上的平台上提供了对 Sqlite 的支持,使开发人员可以在 BlackBerry 手机上使用关系型数据库。对于将 MIDlet 移植到 BlackBerry 上的开发人员而言,这无疑是 一个好消息,使用关系型数据库保存数据可以让程序更加简单有效。 当然,决定是否将数据存储方式由之前的 RMS 转变为关系型数据库,具体要看应用的 规模和数据类型。一般而言,如果应用规模不大,数据类型更接近树状的文档结构,则不建 议使用关系型数据库。反之,如果应用规模较大,而且需要存储的数据是大批量的规整的数 据,则使用关系型数据库比较有利。 从具体实现上讲,在 BlackBerry 平台上有 DatabaseFactory 可以用于创建或者是连接数 据库,所创建的数据库以一个文件的形式保存在设备中,可以是设备内存,也可以是媒体卡。 创建或者是连接数据库以后获取了 Database 实例,可以通过 Database 实例的 createStatement 方法创建一句 SQL 语句,通过 Statement 实例的 prepare 方法准备执行,然 后通过该实例的 execute 方法执行。如果是查询语句,可以在 prepare 方法执行后,通过 getCursor 获得查询结果的光标,再通过光标操作获得所查询的内容。 下面是创建或者是连接数据库的语句: String dbLocation = "/SDCard/databases/SQLite Demo/"; URI uri = URI.create(dbLocation + "mydb"); Database db = DatabaseFactory.openOrCreate(uri, new DatabaseSecurityOptions(false)); 下面是执行查询语句的代码片段: Statement statement = _db.createStatement("SELECT * FROM Category"); statement.prepare(); Cursor cursor = statement.getCursor(); 下面是查询后遍历结果的代码片段: Row row; int id; String name; while(cursor.next()) { 25 row = cursor.getRow(); id = row.getInteger(0); name = row.getString(1); } 使用完之后记得要将 Statement 实例和 Cursor 实例关闭,如: statement.close(); cursor.close(); 对于 SQLite 的使用,关键点是 SQLite 是一个轻量级的 SQL 数据库,不能支持所有的 SQL 功能。一方面是在数据格式上只支持简单的类似 Integer,Text 这样的类型,另一方面是所支 持的 SQL 语句也有限,不支持复杂的查询操作。 有关 SQLite 的更多信息可以在下面的网站中找到: http://www.sqlite.org/ 使用全局事件来进行应用交互 对于应用通讯,只能共享数据是不足够的,在共享数据之后需要通知相应的应用,让目 标应用可以对数据变化进行响应。 BlackBerry 平台提供了事件模型,用来在不同的应用之间通信。BlackBerry 上的任何应 用程序都可以发布或者监听全局事件,发布全局事件的时候需要指定一个事件 ID 号,一个 应用所发布的事件会被所有监听全局事件的应用获取,不同的应用需要通过 ID 号判断是否 对该事件进行处理。另外,在发送全局事件的过程中可以同时发送一些简单的数据,使用起 来会更简单一些。但是对于大量的数据,一般采用的方式是先将数据保存到特定的共享空间 中,然后通过事件通知目标应用。 首先需要定义一个全局事件,对于全局事件的定义,BlackBerry 有自己的定义规范:  定义一个 ID 变量  把 ID 变量定义为静态的,从而使得其他的类也可以引用到  通对包名做 HASH 产生 ID,使 ID 变得独一无二 示例代码如下 public static long GLOBAL_ID = 0xba4b84944bb7429eL; 定义了全局事件后可以将该事件发布,通过把事件 ID 传递到 postGlobalEvent() 方法中 , 我们可以发布一个全局事件 BlackBerry 提供了有四种不同的方法来发布一个事件,简单的方法是只传入事件 ID 不带 数据,相对复杂的方法在发布事件时可以传入两个整数作为随事件发布的内容,更复杂的可 以在发布事件时传入对象作为发布的内容。开发人员可以根据自己程序的复杂程序决定使用 什么方法发布一个事件。 最简单的发布事件的示例代码如下: ApplicationManager.getApplicationManager() .postGlobalEvent(GLOBAL_ID); 对于事件接受者来说,需要考虑和实现的关键点如下  全局监听应用程序必须要是一个自动启动应用程序  监听程序需要有类实现一个 GlobalEventListener 接口 26  监听程序需要添加 GlobalEventListener 实例 实现 GlobalEventListener 和添加 ClobalEventListener 的示例代码如下: class GlobalEventListenerApp extends UiApplication implements GlobalEventListener { public GlobalEventListenerApp() { addGlobalEventListener(this); } } 对于实现 GlobalEventListener 接口的类,必然实现 eventOccured 方法以响应全局事件的 发生。需要注意的一点是不管事件由谁发布,也不管事件是系统事件还是程序发布的事件, 只要有事件发生,一个注册过的 GlobalEnventListener 的 eventOccured 都会被调用,这意味 着开发人员需要自己通过事件 ID 进行判断,如果事件 ID 属于需要处理的范围才对事件进行 响应。示例代码如下: public void eventOccured(long guid, int data0, int dat1, Object object0, Object object1) { //注意这里需要检查事件ID是不是需要响应的事件。 if (guid == GlobalEventFiringApp.GLOBAL_ID) { //在这里完成事件响应 } } 接收推送数据 数据推送是 BlackBerry 平台的一大优势,当服务器端有数据更新时,应用服务器可以将 数据推送到手机上,不需要手机上的应用通过轮询的方式检查服务器上是否有需要更新的数 据。有关数据推送的基本架构与服务器的推送代码,请参考 BlackBerry 推送的相关文档,文 小节只描述如何在 MIDlet 应用中加上手机上侦听推送数据的方法。 应用自启动 如果希望在手机端侦听从服务器上推送的数据,一般而言需要自动启动该侦听应用,否 则有可能该侦听应用没有启动,导致推送数据没有被客户端程序接收。 在 BlackBerry 平台上要自动启动一个应用程序比较简单,可以直接通过设置完成,在 BlackBerry 项目中双击打开“BlackBerry_App_Descriptor.xml”,在左边“General Information” 栏下方选中“Auto-run on startup”就可以让该项目中的应用在手机启动过程中自动启动。 27 需要注意的是选项“Auto-run on startup”只有在应用类型为“BlackBerry Application” 时可用,也就是说如果你选择应用类型为“MIDlet”的话就不能使用“Auto-run on startup” 选项。 所以需要创建一个 BlackBerry 应用进行侦听,而不是使用标准的 MIDlet。 有关 BlackBerry 应用项目的创建在这里不做详细描述,需要了解具体的步骤请参考相关 文档。创建了 BlackBerry 项目后首先要考虑应用的启动代码,BlackBerry 应用与普通的 java 应用一样以 main 方法作为入口。 为了更好地对应用实例进行控制,这里采用单例模式,为类 PushedDataListener 创建一 个静态的单例获取方法,在 main 函数中进行调用。 public static void main(String[] args) { PushedDataListener.waitForSingleton().start(); } 用于获取单例的静态方法实现如下,一如普通的单例获取方法,该方法的返回值为 PushedDataListener 本身。在该方法中,使用了 RuntimeStore 将实例保存起来,如果在 RuntimeStore 中已经有实例的话则从 RuntimeStore 中获取,没有则创建一个新的实例并返 回。 public static PushedDataListener waitForSingleton() { // make sure this is a singleton instance RuntimeStore store = RuntimeStore.getRuntimeStore(); Object o = store.get(RTSID_MY_APP); if (o == null) { store.put(RTSID_MY_APP, new PushedDataListener()); return (PushedDataListener) store.get(RTSID_MY_APP); } else { return (PushedDataListener) o; } } 28 推送侦听 从上一节的启动代码可以看到,在应用实例得到后调用了 start 方法。PushedDataListener 类本身不是一个线程,所以这个 start 方法需要自己实现,在这个方法中创建了一个 ListenerThread 的实例,并启动该线程。 ListenerThread 实例的实现主要是创建侦听连接,并通过一个不结束的循环不断从该连 接中获取服务器上推送下来的数据。 首先需要定义 StreamConnectionNotifier 实例,StreamConnectionNotifier 用于创建连接。 其次需要定义 StreamConnection 和 InputStream,从连接中获取到输入流,用于侦听数据侦 听。 StreamConnectionNotifier notify = null; StreamConnection stream = null; InputStream input = null; 定义 StreamConnectionNotifier 实例后通过 Connector 的 open 方法打开连接。Connector 是一个标准的类,用于连接不同连接,如 http,socket 等。连接不同网络的时候都是使用了 open 方法,在参数中传入 URL 打开指定的地址。网络的不同类型通过 URL 参数的不同进行 区分,如 http 的 URL 以“http://”开头,而 socket 的 URL 是以“socket://”开头。推送数据 不属于其它标准的协议,所以它的 URL 格式比较特殊,格式为:“http://:”。虽然推送 的 URL 以“http://”开头,但是它不是一个 http 请求,其中的“”为端口号,需要和 服务器推送端约定使用同一个端口号。下面是打开边接的代码片段,其中“LISTEN_URL”为 定义的字符串变量,值为“http://:911”,其中 911 为约定的端口。 notify = (StreamConnectionNotifier) Connector.open(LISTEN_URL); 获得连接后需要将连接强制转换成 StreamConnectionNotifier 。 获 得 StreamConnectionNotifier 后就通过一个循环不断侦听获取推送数据。 循环的第一句为 notify.acceptAndOpen,注意这一句语句执行后该线程会开始等待,不 再执行,直到有推送数据到达。 推 送 数 据 到 达 后 acceptAndOpen 方 法 返 回 一 个 StreamConnection 实 例 , 通 过 StreamConnection 实例的 openInputStream 方法可以获得输入流,然后通过输入流获得推送 数据。 注意操作完成后需要将输入流和 StreamConnection 关闭,关闭后重新开始侦听,等待下 一个推送数据。 侦听的代码片段如下: for (;;) { stream = notify.acceptAndOpen(); input = stream.openInputStream(); //在这里通过对input的操作获得推送数据 input.close(); stream.close(); stream = null; } 另外需要注意的是异常处理,因为连接的建立和输入流的处理可能会因为一些原因抛出 异常。一种方法是在循环内处理异常,处理完了继续循环,但是这无法对 Connector.open 方法进行处理。所以需要在循环外对异常进行处理,将 Connector.open 包含进来,本文的例 29 子就是使用这种方法。但是,实际而言这种处理方式也有问题,当输入流处理有问题的时候 会跳出循环,不再侦听推送数据。 最终建议的方法是建立两重循环,在两重循环都加上异常捕获。外层循环中的异常处理 负责处理 Connector.open 的异常,出现异常的话处理后重新通过 Connector.open 打开连接。 内层循环对输入流异常进行处理,处理后继续侦听数据,而不需要重新通过 Connector.open 打开连接。具体代码请参考开发环境附带的样例。本文为简化代码没有采用双重循环的方式。 侦听程序与主程序的交互 获取侦听数据后需要做的工作是通知主程序有新数据到达,由主程序对数据进行处理。 因为侦听程序为后台程序,不负责界面更新等操作,界面更新的操作由 MIDlet 主程序完成。 可以看到关键是作为侦听程序的 BlackBerry 应用与作为主程序的 MIDlet 如何交互,这可以 使用上一章节介绍的方法,通过 GlobalEvent 通知主程序,然后将数据写入共享数据中。主 程序在接收到事件通知后共享数据中获得推送数据再进行数据处理和界面更新处理。具体方 法不再详细描述。 推送侦听的完整代码 下面是推送数据侦听的完整代码,不包括应用交互部分: package cn.searb; import java.io.IOException; import java.io.InputStream; import javax.microedition.io.Connector; import javax.microedition.io.StreamConnection; import javax.microedition.io.StreamConnectionNotifier; import net.rim.device.api.system.RuntimeStore; import net.rim.device.api.ui.UiApplication; public class PushedDataListener extends UiApplication { public static final long RTSID_MY_APP = 0x68b31bd292413108L; private static final String LISTEN_URL = "http://:911"; // the listen port private ListenerThread myThread; public static void main(String[] args) { PushedDataListener.waitForSingleton().start(); } public PushedDataListener() { 30 myThread = new ListenerThread(); } public static PushedDataListener waitForSingleton() { // make sure this is a singleton instance RuntimeStore store = RuntimeStore.getRuntimeStore(); Object o = store.get(RTSID_MY_APP); if (o == null) { store.put(RTSID_MY_APP, new PushedDataListener()); return (PushedDataListener) store.get(RTSID_MY_APP); } else { return (PushedDataListener) o; } } public void start() { invokeLater(new Runnable() { public void run() { myThread.start(); } }); this.enterEventDispatcher(); } class ListenerThread extends Thread { public void run() { System.out.println("DemoOA BackGroundThread -- running"); StreamConnectionNotifier notify = null; StreamConnection stream = null; InputStream input = null; try { sleep(1000); } catch (Exception e) { } try { notify = (StreamConnectionNotifier) Connector.open(LISTEN_URL); 31 for (;;) { stream = notify.acceptAndOpen(); input = stream.openInputStream(); // stream.close(); stream = null; } } catch (IOException e) { System.err.println(e.toString()); } finally { try { if (stream != null) { stream.close(); } } catch (Exception ex) { } try { if (notify != null) { notify.close(); } } catch (Exception ex) { } } } } } 小结 通过本文的介绍读者可以发现,将 MIDlet 程序移植到 BlackBerry 上并不困难,同时开 发人员也可以根据项目的时间要求和应用的特点决定移植的程度。 不过,无论如何,移植的 MIDlet 程序都不能够充分地发挥 BlackBerry 平台的优势,如 果希望最大地发挥 BlackBerry 平台的优势,还是需要根据 BlackBerry 平台的特点重新对应用 进行实现。
还剩30页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

BlackBerry

贡献于2010-09-16

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf