設(shè)備兼容性

  java老鳥(niǎo)們大多應(yīng)該對(duì)java的所謂寫(xiě)一次到處運(yùn)行留下了深刻的印象,我們已經(jīng)習(xí)慣了所謂按照某種規(guī)范開(kāi)發(fā),然后寫(xiě)一次,到處調(diào)試的的模式。好吧,非常不幸的是,kjava設(shè)備的差異性要遠(yuǎn)高于 j2ee中各種應(yīng)用服務(wù)器和平臺(tái)之間的差異,現(xiàn)在的規(guī)則可能會(huì)變成,寫(xiě)一次, 到處修改。搞不好工作量的三分之一都會(huì)花費(fèi)在這個(gè)上面。
  大的廠(chǎng)商,比如nokia和索愛(ài), 會(huì)提供定制版本的模擬器以及相關(guān)文檔,在定制版本模擬器上運(yùn)行可以節(jié)約一部分工作量,雖然很遺憾的是,他們的速度通常不怎么樣,小問(wèn)題也比較多。 而其他一些廠(chǎng)商可能完全沒(méi)有模擬器和文檔說(shuō)明,特別是wm上的第三方kvm實(shí)現(xiàn),那就只能自求多福了。

  你必須認(rèn)識(shí)到
   * 模擬器和真機(jī)永遠(yuǎn)是有差異的, 只有在真機(jī)上運(yùn)行過(guò)才知道后果,尤其是運(yùn)行速度,網(wǎng)絡(luò)連接方面。
   * 不同手機(jī)的kvm實(shí)現(xiàn)之間是有巨大差異的,規(guī)范對(duì)這種差異基本上不做控制,UI的實(shí)現(xiàn)尤甚。

  作為企業(yè)應(yīng)用開(kāi)發(fā)的一個(gè)慣例在j2me上也適用,盡早的確定設(shè)備型號(hào),并一直持續(xù)在這些型號(hào)上進(jìn)行測(cè)試。不要到交付用戶(hù)的時(shí)候再最后做兼容性測(cè)試。

內(nèi)存管理

  大部分手機(jī)都是嚴(yán)格的內(nèi)存受限設(shè)備,可用內(nèi)存通常在256k-2m之間。 好吧,老java程序員的懷舊時(shí)光又開(kāi)始了,你會(huì)發(fā)現(xiàn)在hotspot vm出現(xiàn)之前的一些概念也同樣對(duì)kvm有效,主要是關(guān)于內(nèi)存回收部分的代碼,你需要打破你臭美的代碼風(fēng)格,強(qiáng)制設(shè)置一些已經(jīng)在j2se世界拋棄的東西。
   *  對(duì)需要回收的資源,需要強(qiáng)制將變量設(shè)置為null, 這樣gc會(huì)優(yōu)先回收。所以對(duì)于個(gè)頭稍微大點(diǎn)的變量,都記得找個(gè)地方設(shè)置為null。
   這事情比較極端的做法是甚至你確定不需要某個(gè)資源了,給變量重新賦值之前也需要設(shè)置為null。比如
   
       Boy boy = new Boy("good");
       boy = null; //這樣gc才會(huì)知道先回收good
       boy = new Boy("bad");
   

   *  必要的時(shí)候調(diào)用System.gc進(jìn)行強(qiáng)制回收, 信不信由你,在很多機(jī)型上它確實(shí)很有效。在內(nèi)存消耗比較大的操作之后,調(diào)用這個(gè)效果很明顯。
   順便說(shuō)一句的是,在j2se的hotspot 的實(shí)現(xiàn)里,這樣做的結(jié)果往往是讓系統(tǒng)呆住。
   *  盡量不要一次使用大型數(shù)組,盡可能拆分成小數(shù)組操作。
     尤其是對(duì)圖像的操作,諸如旋轉(zhuǎn)之類(lèi),很容易內(nèi)存死光光。
 
以上這三條已經(jīng)可以讓你的代碼具有典型的異教徒風(fēng)格了。另外一些final,static,訪(fǎng)問(wèn)方法和實(shí)例變量的差別,確實(shí)有一些書(shū)籍介紹如何如何,但是據(jù)我閱讀的某本書(shū)籍中的測(cè)試,這些都是跟機(jī)型和kvm實(shí)現(xiàn)相關(guān)的,沒(méi)有絕對(duì)標(biāo)準(zhǔn)。

   
異常處理
  j2me的異常處理和j2se迥然不同。 一方面是因?yàn)樵O(shè)備通常沒(méi)有什么控制臺(tái)和文件輸出的概念,約束你隨意的打堆棧, 另外一方面,midp中的異常類(lèi)的規(guī)范并不要求異常附帶堆棧信息,只要求異常的堆棧包含類(lèi)名而已。 這2條讓異常很難準(zhǔn)確定位。

 少許幸運(yùn)的是,一般模擬器都有debug的模式,這種模式一般有附帶不完整的異常堆棧信息,這也是你可以在pc上開(kāi)發(fā)看到一些異常堆棧的原因。但是在手機(jī)上則看不到,除非你安裝debug版本的kvm。即便是這種debug模式,也不會(huì)附帶常在j2se中出現(xiàn)的文件和行列信息, 問(wèn)題定位也要繁瑣一些。即便如此,你也應(yīng)該慶幸可以在模擬器上發(fā)現(xiàn)大部分問(wèn)題。

  最后歸結(jié)于設(shè)備兼容性的問(wèn)題, 初次在設(shè)備上運(yùn)行時(shí),發(fā)生各種runtimExcepiton的概率非常高,而一個(gè)類(lèi)名很難讓你準(zhǔn)確定位問(wèn)題,所以編程習(xí)慣必須有所改變。

  我的做法是
  *  盡可能的對(duì)方法中的參數(shù)和變量進(jìn)行檢查。 防止空指針和索引之類(lèi)的異常出現(xiàn)。
  在j2se中我們一般只強(qiáng)調(diào)對(duì)入口參數(shù)的檢查,不會(huì)太強(qiáng)調(diào)對(duì)過(guò)程中變量值的檢查。這樣畢竟會(huì)增加代碼量。
  *  盡可能的對(duì)一些入口出,網(wǎng)絡(luò)部分, ui部分這些和設(shè)備依賴(lài)比較高部分的代碼還有一些關(guān)鍵代碼做runtimeException的捕獲,方便定位調(diào)試。

  而在j2se中的開(kāi)發(fā)中,根據(jù)Effective java的表述, 對(duì)runtimeException,我們應(yīng)該視為編程性錯(cuò)誤,應(yīng)該不做處理,讓其盡早的暴露。問(wèn)題是在j2me的環(huán)境很難暴露準(zhǔn)確位置,所以還是自力更生比較現(xiàn)實(shí)一點(diǎn),這樣好過(guò)你到時(shí)候滿(mǎn)世界的去懷疑異常是從何處代碼而出的。

  *  盡可能多的做單元測(cè)試也是個(gè)好路子。 要比在手機(jī)上去找問(wèn)題節(jié)約時(shí)間。
 
== 日志 ==

 受限于設(shè)備的特性, j2me中沒(méi)法直接把日志輸出到控制臺(tái)或者文件. 而j2me的調(diào)試?yán)щy度決定了一個(gè)合理有效的日志機(jī)制顯然是必須的,而且應(yīng)該在程序開(kāi)發(fā)初始就必須認(rèn)真考慮。

 實(shí)現(xiàn)日志的方式大致有3種
  1. c/s模式, 將日志發(fā)送回后臺(tái)存儲(chǔ), 在jdj中有篇文章介紹這種模式,優(yōu)點(diǎn)是查找問(wèn)題非常方便,在后臺(tái)看就好了,缺點(diǎn)當(dāng)然是資源消耗問(wèn)題,還有有些錯(cuò)誤有可能發(fā)生在聯(lián)網(wǎng)之前,或者聯(lián)網(wǎng)處理中。
  2. 將日志輸出到 rms中,  事后通過(guò)程序查看,但是注意rms一般都有大小限制,從幾十k到幾百k不等
  3. 將日志輸出到一個(gè)屏幕對(duì)象, 可以是form也可以是canvas,需要的時(shí)候調(diào)出來(lái)看。也可以和2結(jié)合使用。

 我選擇了第三種方式,將日志輸出到一個(gè)日志屏幕,日志屏幕附帶了一個(gè)sms發(fā)送日志的功能,這樣出了問(wèn)題,可以方便讓用戶(hù)直接把日志信息以短信的方式發(fā)送給我定位,呵呵, 至于短信費(fèi)用,哪不是我考慮的問(wèn)題:)。
 另外為了方便內(nèi)存的跟蹤, 在測(cè)試階段,日志應(yīng)該可以自動(dòng)生成一些內(nèi)存使用情況信息。

  有一些第三方的log4j實(shí)現(xiàn) 比如 Jadabs Log4 J2ME,log4jMini, Microlog4j。 有興趣也可以參考一下。