開發(fā)高可移植性J2ME的軟件
隨著MTK的流行,使現(xiàn)在的J2ME虛擬機(jī)市場(chǎng)上品牌眾多,除了索愛,Nokia S40,Moto,三星,LG等國(guó)際大品牌的虛擬機(jī),更是有MTK,展訊內(nèi)置的一些不知名的虛擬機(jī),因此當(dāng)初Write Once,Run AnyWhere變成了Write Once,Debug AnyWhere了。對(duì)于一個(gè)沒有經(jīng)驗(yàn)的J2ME程序員來說,開發(fā)一個(gè)兼容性高的軟件變成了噩夢(mèng),不斷的在不同手機(jī),不同平臺(tái)上打log,在這臺(tái)手機(jī)上解決了這個(gè)問題,跑到另外一臺(tái)機(jī)器上問題有重新了,噢,my god!我不干了。別急!我寫這篇文章的目的就是要告訴大家,對(duì)于這種狀況,我們也不是束手無策的。下面就等我慢慢的道來解決之道。
本文主要適合那些有經(jīng)驗(yàn)的J2ME程序員在優(yōu)化軟件,或者是需要考慮軟件兼容性時(shí)的參考文檔。
Jblend 平臺(tái)
JBlend 是一家日本的嵌入式虛擬機(jī)廠家生產(chǎn)的J2ME虛擬機(jī),此虛擬機(jī)大量的用于低端手機(jī)平臺(tái),本人發(fā)現(xiàn)有使用此虛擬機(jī)的平臺(tái)有,MTK,MOTO。
官方網(wǎng)站:http://www.aplixcorp.com/chs/index.html 。
索尼愛立信平臺(tái)
索愛的虛擬機(jī)平臺(tái)是:Java Platform。最新版本是8。索愛的平臺(tái)在性能上,程序的穩(wěn)定性方面要優(yōu)于其他虛擬機(jī)平臺(tái)。而且APIs方面的bug也很少,在網(wǎng)絡(luò)支持方面也很優(yōu)秀?;旧喜粫?huì)因?yàn)槟阃涥P(guān)閉連接而導(dǎo)致連接泄漏。
官方網(wǎng)站:http://developer.sonyericsson.com/site/zhcn/docs_and_tools/p_docs_and_tools.jsp
S40平臺(tái)
S40平臺(tái)是Nokia針對(duì)S60智能操作系統(tǒng)推出適應(yīng)低端手機(jī)的手機(jī)操作系統(tǒng),相對(duì)其他虛擬機(jī)平臺(tái)來說,S40虛擬機(jī)對(duì)J2ME的支持相對(duì)比較完善,而且穩(wěn)定些,不過網(wǎng)絡(luò)環(huán)境這塊,S40對(duì)網(wǎng)絡(luò)資源泄漏特別關(guān)注,具體不同的手機(jī),對(duì)同時(shí)打開多個(gè)連接有限制,這里建議大家做個(gè)測(cè)試,就不再累贅了。
官方網(wǎng)站:http://www.forum.nokia.com/
S40平臺(tái)詳解:http://tech.sina.com.cn/mobile/n/2006-09-22/1053107637.shtml
S60 平臺(tái)
Nokia 智能機(jī)平臺(tái)下的J2ME虛擬機(jī)。相對(duì)S40來說,S60支持的特性比較多,而且有些比較特殊的用法,比如獲取系統(tǒng)相關(guān)屬性的時(shí)候就是其中之一。
什么是JCP?
JCP(Java Community Process) 是一個(gè)開放的國(guó)際組織,主要由Java開發(fā)者以及被授權(quán)者組成,職能是發(fā)展和更新Java技術(shù)規(guī)范、參考實(shí)現(xiàn)(RI)、技術(shù)兼容包(TCK)。Java技 術(shù)和JCP兩者的原創(chuàng)者都是SUN計(jì)算機(jī)公司。然而,JCP已經(jīng)由SUN于1995年創(chuàng)造Java的非正式過程,演進(jìn)到如今有數(shù)百名來自世界各地Java 代表成員一同監(jiān)督Java發(fā)展的正式程序。JCP維護(hù)的規(guī)范包括J2ME、J2SE、J2EE,XML,OSS,JAIN等。組織成員可以提交JSR(Java Specification Requests),通過特定程序以后,進(jìn)入到下一版本的規(guī)范里面。所有聲稱符合J2EE規(guī)范的J2EE類產(chǎn)品(應(yīng)用服務(wù)器、應(yīng)用軟件、開發(fā)工具等),必須通過該 組織提供的TCK兼容性測(cè)試(需要購(gòu)買測(cè)試包),通過該測(cè)試后,需要繳納J2EE商標(biāo)使用費(fèi)。兩項(xiàng)完成,即是通過J2EE認(rèn)證(Authorized Java Licensees of J2EE)。
什么是JSR?
JSR是Java Specification Requests的縮寫,意思是Java 規(guī)范請(qǐng)求。是指向JCP(Java Community Process)提出新增一個(gè)標(biāo)準(zhǔn)化技術(shù)規(guī)范的正式請(qǐng)求。任何人都可以提交JSR,以向Java平臺(tái)增添新的API和服務(wù)。JSR已成為Java界的一個(gè)重要標(biāo)準(zhǔn)。
下面是J2ME JSR規(guī)范列表
名稱 |
內(nèi)容 |
JSR 118 |
MIDP 2.1 規(guī)范。定義了MIDP 相關(guān)的接口,高級(jí)UI,低級(jí)UI,RMS,網(wǎng)絡(luò)相關(guān)的APIs |
JSR 82 |
定義了藍(lán)牙接口相關(guān)的APIs |
JSR135 |
Mobile Media API,定義了多媒體相關(guān)開發(fā)的組件APIs |
JSR 172 |
1. 一個(gè)輕量級(jí)的標(biāo)準(zhǔn)XML解析器 |
JSR 75 |
JSR 75(PDA Optional Packages for the J2METM Platform)中定義了兩個(gè)可選包: |
JSR 177 |
安全APIs |
JSR 211 |
Content Hander 內(nèi)容處理APIs,可以調(diào)用此API打開相應(yīng)的文件,比如你可以打開jar安裝文件,打開mp3。 |
JSR 239 |
Open GL@ES。主要用于圖形相關(guān)操作 |
JSR 179 |
Location APIs 主要是用于LBS服務(wù) |
JSR 180 |
SIP APIs SIP是一個(gè)應(yīng)用層的信令控制協(xié)議。用于創(chuàng)建、修改和釋放一個(gè)或多個(gè)參與者的會(huì)話。這些會(huì)話可以好似Internet多媒體會(huì)議、IP電話或多媒體分發(fā)。會(huì)話的參與者可以通過組播(multicast)、網(wǎng)狀單播(unicast)或兩者的混合體進(jìn)行通信。 |
JSR 184 |
Mobile 3D Graphics APIs,3D圖形開發(fā)。 |
JSR 229 |
手機(jī)支付APIs |
JSR 234 |
手機(jī)高級(jí)多媒體支持,可以支持更豐富的多媒體操作 |
JSR 238 |
國(guó)際化支持APIs |
JSR 248 |
JSR 248: Mobile Service Architecture MSA 移動(dòng)服務(wù)架構(gòu)。 MSA for CLDC規(guī)范定義了移動(dòng)電話上的下一代Java平臺(tái),當(dāng)然是基于CLDC的J2ME平臺(tái)。 MSA for CLDC的目的是為了減少J2ME平臺(tái)的API分裂,為開發(fā)者定義一個(gè)高操作性的應(yīng)用程序和服務(wù)環(huán)境。 JTWI(Java Technology for Wireless Industry,JSR 185)定義了一系列的規(guī)范來強(qiáng)制實(shí)現(xiàn)JTWI規(guī)范的設(shè)備必須實(shí)現(xiàn)某些JSR,例如MIDP2.0,WMA和MMAPI等。MSA for CLDC可以認(rèn)為是JTWI的第2版,它規(guī)定了一個(gè)高度集中的J2ME平臺(tái)運(yùn)行環(huán)境。 |
#p#
檢查JSR支持
檢查JSR的支持簡(jiǎn)單的方式有兩種:
1. 是通過System.getProperty("property_name")的方式進(jìn)行判斷,一般如果存在相關(guān)的APIs支持,它會(huì)返回一個(gè)非null字符串。
檢測(cè)代碼
System.getProperty(property_key); |
2. 通過Class.forName(clase_name)的方式。
private boolean hasClassExit(String aClassName) { |
上面的檢測(cè)代碼相對(duì)比較簡(jiǎn)單,而且也容易理解,關(guān)鍵是那些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 |
依賴于底層實(shí)現(xiàn) | |
microedition.locale* |
JSR 37 |
依賴于底層實(shí)現(xiàn) |
microedition.commports |
依賴于底層實(shí)現(xiàn) | |
microedition.hostname |
localhost | |
microedition.profiles |
MIDP2.0 | |
file.separator |
文件分割符 |
依賴于底層實(shí)現(xiàn)(/,\) |
microedition.pim.version |
JSR 75 |
1.0 |
microedition.smartcardslots |
JSR 177 |
依賴于底層實(shí)現(xiàn) |
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 |
依賴于底層實(shí)現(xiàn) |
wireless.messaging.mms.mmsc |
JSR 205 |
依賴于底層實(shí)現(xiàn) |
CHAPI-Version |
JSR 211 |
JSR 211 |
Nokia的一些系統(tǒng)參數(shù) | ||
com.nokia.network.access |
網(wǎng)絡(luò)參數(shù) |
pd - GSM pd.EDGE - EDGE pd.3G - 3G pd.HSDPA - 3G csd - GSM CSD/HSCSD bt_pan - Bluetooth PAN network wlan - WIFI na - 無任何網(wǎng)絡(luò) |
com.nokia.mid.dateformat |
日期格式 |
Yy/mm/dd |
com.nokia.mid.timeformat |
時(shí)間格式 |
hh:mm |
com.nokia.memoryramfree |
動(dòng)態(tài)內(nèi)存分配 Note: S60 第3版不支持 |
|
com.nokia.mid.batterylevel |
電池狀態(tài) |
|
com.nokia.mid.countrycode |
城市代碼 |
|
com.nokia.mid.networkstatus |
網(wǎng)絡(luò)工作狀態(tài) |
|
com.nokia.mid.networkavailability |
網(wǎng)絡(luò)是否激活狀態(tài) |
|
com.nokia.mid.networkid |
網(wǎng)絡(luò)ID |
返回2個(gè)值 Network ID 網(wǎng)絡(luò)簡(jiǎn)稱 |
com.nokia.mid.networksignal |
||
com.nokia.mid.cellid |
Cellid |
基站信息ID |
com.nokia.mid.imei |
Imei號(hào) |
手機(jī)唯一標(biāo)識(shí)號(hào) |
com.nokia.mid.imsi |
應(yīng)用程序?qū)傩?/STRONG>
應(yīng)用程序?qū)傩灾凳窃趹?yīng)用程序描述符文件或者M(jìn)ANIFEST文件中定義的,當(dāng)我們部署應(yīng)用程序的時(shí)候可以定義應(yīng)用程序?qū)傩?。比如下面是一個(gè)典型的JAD文件內(nèi)容。
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就是應(yīng)用程序?qū)傩灾?,我們可以通過MIDlet的成員方法getAppProperty()來得到它,代碼片斷如下:
import javax.microedition.midlet.*; |
屬性值對(duì)大小寫是敏感的,如果屬性值在底層系統(tǒng)、JAD文件和Manifest文件中都沒有定義的話,那么將返回Null。
#p#
簡(jiǎn)單的Demo
下面是簡(jiǎn)單的測(cè)試環(huán)境的代碼,有經(jīng)驗(yàn)的朋友可以很容易就就跑起來。
代碼片段
/**
* 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
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 |
代表手機(jī)是否支持混音(同時(shí)播放多個(gè)Player),返回值為“true”或“false” |
supports.audio.capture |
代表手機(jī)是否支持聲音捕獲(錄音),返回值為“true”或“false” |
supports.video.capture |
代表手機(jī)是否支持視頻捕獲(錄像),返回值為“true”或“false” |
supports.recording |
代表手機(jī)是否支持記錄(record),返回值為“true”或“false” |
audio.encodings |
代表手機(jī)支持的聲音格式,返回值格式為“encoding=audio/wav”,多個(gè)格式之間使用至少一個(gè)空格進(jìn)行間隔 |
video.encodings |
代表手機(jī)支持的視頻格式,返回值格式為“encoding=video/3gpp”,多個(gè)格式之間使用至少一個(gè)空格進(jìn)行間隔 |
video.snapshot.encodings |
代表手機(jī)使用getSnapshot方法獲得的視頻快照格式,返回值格式為“encoding=png”,多個(gè)格式之間使用至少一個(gè)空格進(jìn)行間隔 |
streamable.contents |
代表手機(jī)支持的流媒體格式,返回null代表不支持 |
表2 Wireless Messaging API屬性
屬性名稱 |
屬性作用 |
wireless.messaging.sms.smsc |
代表手機(jī)發(fā)送短信時(shí)的短信服務(wù)中心號(hào)碼 |
表3FileConnection API
屬性名稱 |
屬性作用 |
fileconn.dir.photos |
代表手機(jī)中存儲(chǔ)照片和其它圖片的目錄,例如“file:///c:/My files/ Images /” |
fileconn.dir.videos |
代表手機(jī)中存儲(chǔ)視頻的目錄,例如“file:///c:/My files/Video clips/” |
fileconn.dir.tones |
代表手機(jī)中存儲(chǔ)聲音的目錄,例如“file:///c:/My files/Tones/” |
fileconn.dir.memorycard |
代表手機(jī)中存儲(chǔ)卡的根目錄。例如“file:///d:/” |
fileconn.dir.private |
代表手機(jī)中MIDlet的私有工作目錄,例如“file:///c:/System/MIDlets/[1015f294]/scratch” |
fileconn.dir.photos.name |
代表手機(jī)中圖片目錄的名稱,例如“Images” |
fileconn.dir.videos.name |
代表手機(jī)中視頻目錄的名稱,例如“Video clips” |
fileconn.dir.tones.name |
代表手機(jī)中聲音目錄的名稱,例如“Sound clips” |
file.separator |
代表手機(jī)中的文件分隔符,例如“/” |
fileconn.dir.memorycard.name |
代表手機(jī)中存儲(chǔ)卡的名稱,例如“Memory card” |
【編輯推薦】