随着MTK的流行,使现在的J2ME虚拟机市场上品牌众多,除了索爱,Nokia S40,Moto,三星,LG等国际大品牌的虚拟机,更是有MTK,展讯内置的一些不知名的虚拟机,因此当初Write Once,Run AnyWhere变成了Write Once,Debug AnyWhere了。对于一个没有经验的J2ME程序员来说,开发一个兼容性高的软件变成了噩梦,不断的在不同手机,不同平台上打log,在这台手机上解决了这个问题,跑到另外一台机器上问题有重新了,噢,my god!我不干了。别急!我写这篇文章的目的就是要告诉大家,对于这种状况,我们也不是束手无策的。下面就等我慢慢的道来解决之道。 本文主要适合那些有经验的J2ME程序员在优化软件,或者是需要考虑软件兼容性时的参考文档。 Jblend 平台 JBlend 是一家日本的嵌入式虚拟机厂家生产的J2ME虚拟机,此虚拟机大量的用于低端手机平台,本人发现有使用此虚拟机的平台有,MTK,MOTO。 官方网站:http://www.aplixcorp.com/chs/index.html 。 索尼爱立信平台 索爱的虚拟机平台是:Java Platform。最新版本是8。索爱的平台在性能上,程序的稳定性方面要优于其他虚拟机平台。而且APIs方面的bug也很少,在网络支持方面也很优秀。基本上不会因为你忘记关闭连接而导致连接泄漏。 官方网站:http://developer.sonyericsson.com/site/zhcn/docs_and_tools/p_docs_and_tools.JSP S40平台 S40平台是Nokia针对S60智能操作系统推出适应低端手机的手机操作系统,相对其他虚拟机平台来说,S40虚拟机对J2ME的支持相对比较完善,而且稳定些,不过网络环境这块,S40对网络资源泄漏特别关注,具体不同的手机,对同时打开多个连接有限制,这里建议大家做个测试,就不再累赘了。 官方网站:http://www.forum.nokia.com/ S40平台详解:http://tech.sina.com.cn/mobile/n/2006-09-22/1053107637.shtml S60 平台 Nokia 智能机平台下的J2ME虚拟机。相对S40来说,S60支持的特性比较多,而且有些比较特殊的用法,比如获取系统相关属性的时候就是其中之一。 什么是JCP? JCP(Java Community Process) 是一个开放的国际组织,主要由Java开发者以及被授权者组成,职能是发展和更新Java技术规范、参考实现(RI)、技术兼容包(TCK)。Java技 术和JCP两者的原创者都是SUN计算机公司。然而,JCP已经由SUN于1995年创造Java的非正式过程,演进到如今有数百名来自世界各地Java 代表成员一同监督Java发展的正式程序。JCP维护的规范包括J2ME、J2SE、J2EE,XML,OSS,JAIN等。组织成员可以提交JSR(Java Specification Requests),通过特定程序以后,进入到下一版本的规范里面。所有声称符合J2EE规范的J2EE类产品(应用服务器、应用软件、开发工具等),必须通过该 组织提供的TCK兼容性测试(需要购买测试包),通过该测试后,需要缴纳J2EE商标使用费。两项完成,即是通过J2EE认证(Authorized Java Licensees of J2EE)。 什么是JSR? JSR是Java Specification Requests的缩写,意思是Java 规范请求。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。 下面是J2ME JSR规范列表 名称 | 内容 | JSR 118 | MIDP 2.1 规范。定义了MIDP 相关的接口,高级UI,低级UI,RMS,网络相关的APIs | JSR 82 | 定义了蓝牙接口相关的APIs | JSR135 | Mobile Media API,定义了多媒体相关开发的组件APIs | JSR 172 | 1. 一个轻量级的标准XML解析器 2. Web Services的远程调用API 其中这个JSR172实现的轻量级的XML解析器是JAXP1.2(Java API for XML Processing)的一个子集。我们可以查看WTK提供的API看到j2me-xml提供的类一共只有12个,这说明这个轻量级的XML解析器是适合 在移动电话这种资源受限设备上运行的。 | JSR 75 | JSR 75(PDA Optional Packages for the J2METM Platform)中定义了两个可选包: PIM (The Personal Information Management)API,提供对个人信息数据的访问,一般包括名片夹,日历项,和待办事项。 FC(The FileConnection) APIs,提供对本地文件系统的访问。 | JSR 177 | 安全APIs | JSR 211 | Content Hander 内容处理APIs,可以调用此API打开相应的文件,比如你可以打开jar安装文件,打开MP3。 | JSR 239 | Open GL@ES。主要用于图形相关操作 | JSR 179 | Location APIs 主要是用于LBS服务 | JSR 180 | SIP APIs SIP是一个应用层的信令控制协议。用于创建、修改和释放一个或多个参与者的会话。这些会话可以好似Internet多媒体会议、IP电话或多媒体分发。会话的参与者可以通过组播(multicast)、网状单播(unicast)或两者的混合体进行通信。 | JSR 184 | Mobile 3D Graphics APIs,3D图形开发。 | JSR 229 | 手机支付APIs | JSR 234 | 手机高级多媒体支持,可以支持更丰富的多媒体操作 | JSR 238 | 国际化支持APIs | JSR 248 | JSR 248: Mobile Service Architecture MSA 移动服务架构。 MSA for CLDC规范定义了移动电话上的下一代Java平台,当然是基于CLDC的J2ME平台。 MSA for CLDC的目的是为了减少J2ME平台的API分裂,为开发者定义一个高操作性的应用程序和服务环境。 JTWI(Java Technology for Wireless Industry,JSR 185)定义了一系列的规范来强制实现JTWI规范的设备必须实现某些JSR,例如MIDP2.0,WMA和MMAPI等。MSA for CLDC可以认为是JTWI的第2版,它规定了一个高度集中的J2ME平台运行环境。 |
检查JSR支持 检查JSR的支持简单的方式有两种: 1. 是通过System.getProperty("property_name")的方式进行判断,一般如果存在相关的APIs支持,它会返回一个非null字符串。 检测代码 System.getProperty(property_key); public String getInfo(String info) { if (info == null) { return "<unknown>"; } else { return info; } }
2. 通过Class.forName(clase_name)的方式。 private boolean hasClassExit(String aClassName) { try { Class.forName(aClassName); return true; } catch (Exception e) { return false; } }
上面的检测代码相对比较简单,而且也容易理解,关键是那些JSR 支持的属性名称,或者APIs的写法。 下面是部分属性名称,仅供参考。 System property | Description | Value | microedition.platform | | Defined in CLDC 1.0 and CLDC 1.1. | microedition.encoding | | Always returns ISO-8859-1. | microedition.configuration | | Defined in CLDC 1.0 and CLDC 1.1. | microedition.profiles | | 依赖于底层实现 | microedition.locale* | JSR 37 | 依赖于底层实现 | microedition.commports | | 依赖于底层实现 | microedition.hostname | | localhost | microedition.profiles | | MIDP2.0 | file.separator | 文件分割符 | 依赖于底层实现(/,) | microedition.pim.version | JSR 75 | 1.0 | microedition.smartcardslots | JSR 177 | 依赖于底层实现 | microedition.location.version | JSR 179 | 1.0 | microedition.sip.version | JSR 180 | 1.0 | microedition.m3g.version | JSR 184 | 1.0 | microedition.jtwi.version | JSR 185 | 1.0 | wireless.messaging.sms.smsc | JSR 205 | 依赖于底层实现 | wireless.messaging.mms.mmsc | JSR 205 | 依赖于底层实现 | CHAPI-Version | JSR 211 | JSR 211 | | | | Nokia的一些系统参数 | com.nokia.network.Access | 网络参数 | pd - GSM pd.EDGE - EDGE pd.3G - 3G pd.HSDPA - 3G csd - GSM CSD/HSCSD bt_pan - Bluetooth PAN network wlan - WIFI na - 无任何网络 | com.nokia.mid.dateformat | 日期格式 | Yy/mm/dd | com.nokia.mid.timeformat | 时间格式 | hh:mm | com.nokia.memoryramfree | 动态内存分配 Note: S60 第3版不支持 | | com.nokia.mid.batterylevel | 电池状态 | | com.nokia.mid.countrycode | 城市代码 | | com.nokia.mid.networkstatus | 网络工作状态 | | com.nokia.mid.networkavailability | 网络是否激活状态 | | com.nokia.mid.networkid | 网络ID | 返回2个值 Network ID 网络简称 | com.nokia.mid.networksignal | | | com.nokia.mid.cellid | Cellid | 基站信息ID | com.nokia.mid.imei | Imei号 | 手机唯一标识号 | com.nokia.mid.imsi | |
应用程序属性 应用程序属性值是在应用程序描述符文件或者MANIFEST文件中定义的,当我们部署应用程序的时候可以定义应用程序属性。比如下面是一个典型的JAD文件内容。 MIDlet-1: HttpWrapperMidlet,httpwrapper.HttpWrapperMIDlet MIDlet-Jar-Size: 16315 MIDlet-Jar-URL: HttpWrapper.jar MIDlet-Name: HttpWrapper MIDlet-Vendor: Vendor MIDlet-Version: 1.0 MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-1.0 Which-Locale: en 其中Which-Locale就是应用程序属性值,我们可以通过MIDlet的成员方法getAppProperty()来得到它,代码片断如下: import javax.microedition.midlet.*; public class MyMIDlet extends MIDlet { private String suiteName; private String which_locale; public MyMIDlet(){ suiteName = getAppProperty( "MIDlet-Name" ); which_locale = getAppProperty("Which-Locale"); } //这里省略了其他代码 }
属性值对大小写是敏感的,如果属性值在底层系统、JAD文件和Manifest文件中都没有定义的话,那么将返回Null。 简单的Demo 下面是简单的测试环境的代码,有经验的朋友可以很容易就就跑起来。 代码片段 /** * getSysInfo */ private void getSysInfo() { addInfo( "Microedition Configuration: ", getInfo(System.getProperty( "microedition.configuration"))); addInfo( "Microedition Profiles: ", getInfo(System.getProperty( "microedition.profiles"))); addInfo( "microedition.jtwi.version:", getInfo(System.getProperty( "microedition.jtwi.version"))); addInfo( "microedition.platform:", getInfo(System.getProperty( "microedition.platform"))); addInfo( "microedition.locale:", getInfo(System.getProperty( "microedition.locale"))); addInfo( "default encoding:", getInfo(System.getProperty( "microedition.encoding"))); addInfo( "microedition.commports", getInfo(System.getProperty( "microedition.commports"))); addInfo( "microedition.hostname", getInfo(System.getProperty( "microedition.hostname"))); // microedition.smartcardslots addInfo( " microedition.smartcardslots", getInfo(System.getProperty( " microedition.smartcardslots"))); addInfo( "com.nokia.network.access", getInfo(System.getProperty( "com.nokia.network.access"))); addInfo( "com.nokia.mid.dateformat", getInfo(System.getProperty( "com.nokia.mid.dateformat"))); addInfo( "com.nokia.mid.timeformat", getInfo(System.getProperty( "com.nokia.mid.timeformat"))); addInfo( "com.nokia.memoryramfree", getInfo(System.getProperty( "com.nokia.memoryramfree"))); addInfo( "com.nokia.mid.batterylevel", getInfo(System.getProperty( "com.nokia.mid.batterylevel"))); addInfo( "com.nokia.mid.countrycode", getInfo(System.getProperty( "com.nokia.mid.countrycode"))); addInfo( "com.nokia.mid.networkstatus", getInfo(System.getProperty( "com.nokia.mid.networkstatus"))); addInfo( "com.nokia.mid.networksignal", getInfo(System.getProperty( "com.nokia.mid.networksignal"))); addInfo( "com.nokia.mid.networkid", getInfo(System.getProperty( "com.nokia.mid.networkid"))); addInfo( "com.nokia.mid.networkavailability", getInfo(System.getProperty( "com.nokia.mid.networkavailability"))); addInfo( "com.nokia.mid.cellid", getInfo(System.getProperty( "com.nokia.mid.cellid"))); addInfo( "com.nokia.mid.imei", getInfo(System.getProperty( "com.nokia.mid.imei"))); addInfo( "com.nokia.mid.imsi", getInfo(System.getProperty( "com.nokia.mid.imsi"))); String[] timeZoneIDs = java.util.TimeZone.getAvailableIDs(); StringBuffer timeZonesBuffer = new StringBuffer(); for (int i = 0; i < timeZoneIDs.length; i++) { timeZonesBuffer.append(timeZoneIDs[i]).append('n'); } addInfo( "Total memory:", Long.toString(Runtime.getRuntime().totalMemory()) + " bytes"); addInfo( "Free memory:", Long.toString(Runtime.getRuntime().freeMemory()) + " bytes"); addInfo( "Available TimeZones:", timeZonesBuffer.toString()); addInfo( "Default TimeZone:", java.util.TimeZone.getDefault().getID()); addInfo( "com.siemens.mp.lcdui.Image", hasClassExit("com.siemens.mp.lcdui.Image") + ""); addInfo( "com.motorola.phonebook.PhoneBookRecord", hasClassExit("com.motorola.phonebook.PhoneBookRecord") + ""); addInfo( "com.motorola.Dialer", hasClassExit("com.motorola.Dialer") + ""); addInfo( "com.jblend.util.Case", hasClassExit("com.jblend.util.Case") + ""); addInfo( "com.samsung.util.AudioClip", hasClassExit("com.samsung.util.AudioClip") + ""); addInfo( "com.mot.iden.multimedia.Lighting", hasClassExit("com.mot.iden.multimedia.Lighting") + ""); } private boolean hasClassExit(String aClassName) { try { Class.forName(aClassName); return true; } catch (Exception e) { return false; } } public String getInfo(String info) { if (info == null) { return "<unknown>"; } else { return info; } } public void addInfo(String name, String value) { iForm.append(new StringItem(name, value)); }
代码片段2 public void collectInfos(TestClient midlet, Display display) { try { Class.forName( "javax.microedition.media.control.VideoControl"); addInfo( "MMAPI: ", "yes" ); addInfo( "MMAPI-Version: ", getInfo(System.getProperty("microedition.media.version")) ); } catch (ClassNotFoundException e) { addInfo( "MMAPI: ", "no" ); } try { Class.forName( "javax.wireless.messaging.Message"); addInfo( "WMAPI 1.1: ", "yes" ); try { Class.forName( "javax.wireless.messaging.MultipartMessage"); addInfo( "WMAPI 2.0: ", "yes" ); } catch (ClassNotFoundException e) { addInfo( "WMAPI 2.0: ", "no" ); } } catch (ClassNotFoundException e) { addInfo( "WMAPI 1.1: ", "no" ); } try { Class.forName( "javax.bluetooth.DiscoveryAgent"); addInfo( "Bluetooth-API: ", "yes" ); try { Class.forName( "javax.obex.ClientSession"); addInfo( "Bluetooth-Obex-API: ", "yes" ); } catch (ClassNotFoundException e) { addInfo( "Bluetooth-Obex-API: ", "no" ); } } catch (ClassNotFoundException e) { addInfo( "Bluetooth-API: ", "no" ); } try { Class.forName( "javax.microedition.m3g.Graphics3D"); addInfo( "M3G-API: ", "yes" ); } catch (ClassNotFoundException e) { addInfo( "M3G-API: ", "no" ); } try { Class.forName( "javax.microedition.pim.PIM"); addInfo( "PIM-API: ", "yes" ); } catch (ClassNotFoundException e) { addInfo( "PIM-API: ", "no" ); } try { Class.forName( "javax.microedition.io.file.FileSystemReGIStry"); addInfo( "FileConnection-API: ", "yes" ); } catch (ClassNotFoundException e) { addInfo( "FileConnection-API: ", "no" ); } try { Class.forName( "javax.microedition.location.Location"); addInfo( "Location-API: ", "yes" ); } catch (java.lang.Throwable e) { addInfo( "Location-API: ", "no" ); } try { Class.forName( "javax.microedition.xml.rpc.Operation"); addInfo( "WebServices-API: ", "yes" ); } catch (ClassNotFoundException e) { addInfo( "WebServices-API: ", "no" ); } try { Class.forName( "javax.microedition.sip.SipConnection"); addInfo( "SIP-API: ", "yes" ); } catch (ClassNotFoundException e) { addInfo( "SIP-API: ", "no" ); } try { Class.forName( "com.nokia.mid.ui.FullCanvas"); addInfo( "Nokia-UI-API: ", "yes" ); } catch (ClassNotFoundException e) { addInfo( "Nokia-UI-API: ", "no" ); } try { Class.forName( "com.siemens.mp.MIDlet"); addInfo( "Siemens-Extension-API: ", "yes" ); try { Class.forName( "com.siemens.mp.color_game.GameCanvas"); addInfo( "Siemens-ColorGame-API: ", "yes" ); } catch (ClassNotFoundException e) { addInfo( "Siemens-ColorGame-API: ", "no" ); } } catch (ClassNotFoundException e) { addInfo( "Siemens-Extension-API: ", "no" ); } }
附表:属性表 表1 MMAPI属性 属性名称 | 属性作用 | supports.mixing | 代表手机是否支持混音(同时播放多个Player),返回值为“true”或“false” | supports.audio.capture | 代表手机是否支持声音捕获(录音),返回值为“true”或“false” | supports.video.capture | 代表手机是否支持视频捕获(录像),返回值为“true”或“false” | supports.recording | 代表手机是否支持记录(record),返回值为“true”或“false” | audio.encodings | 代表手机支持的声音格式,返回值格式为“encoding=audio/wav”,多个格式之间使用至少一个空格进行间隔 | video.encodings | 代表手机支持的视频格式,返回值格式为“encoding=video/3gpp”,多个格式之间使用至少一个空格进行间隔 | video.snapshot.encodings | 代表手机使用getSnapshot方法获得的视频快照格式,返回值格式为“encoding=png”,多个格式之间使用至少一个空格进行间隔 | streamable.contents | 代表手机支持的流媒体格式,返回null代表不支持 |
表2 Wireless Messaging API属性 属性名称 | 属性作用 | wireless.messaging.sms.smsc | 代表手机发送短信时的短信服务中心号码 |
表3FileConnection API 属性名称 | 属性作用 | fileconn.dir.photos | 代表手机中存储照片和其它图片的目录,例如“file:///c:/My files/ Images /” | fileconn.dir.videos | 代表手机中存储视频的目录,例如“file:///c:/My files/Video clips/” | fileconn.dir.tones | 代表手机中存储声音的目录,例如“file:///c:/My files/Tones/” | fileconn.dir.memorycard | 代表手机中存储卡的根目录。例如“file:///d:/” | fileconn.dir.private (Nokia S40不支持) | 代表手机中MIDlet的私有工作目录,例如“file:///c:/System/MIDlets/[1015f294]/scratch” | fileconn.dir.photos.name | 代表手机中图片目录的名称,例如“Images” | fileconn.dir.videos.name | 代表手机中视频目录的名称,例如“Video clips” | fileconn.dir.tones.name | 代表手机中声音目录的名称,例如“Sound clips” | file.separator | 代表手机中的文件分隔符,例如“/” | fileconn.dir.memorycard.name | 代表手机中存储卡的名称,例如“Memory card” |
(责任编辑:admin) |