隨著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解析器 |
JSR 75 | JSR 75(PDA Optional Packages for the J2METM Platform)中定義了兩個可選包: |
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 | 代表手機中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」 |
[火星人 ] 開發高可移植性J2ME的軟體已經有829次圍觀