2006年8月28日

不要把非项目文件扔在workspace里面

workspace,当然就是eclipse的工作区。之前一直都这么做的,资源、其它脚本,统统都在workspace里面,尽管其中相当一部分根本不受eclipse管理。

一次偶然的机会,eclipse在build workspace的时候(这种操作eclipse会在很多情况下进行,比如refresh某个项目的时候)死掉了。进度永远是0%,而且无法正常退出,只能强制杀掉进程。

最开始以为是windows几天没关,又犯病了,重启之后,故障依旧。

于是留意eclipse在build workspace时候给出的提示。原来是说几个非项目的文件夹 的路径和eclipse记录的不符。eclipse还善意的提示我将这几个“项目”按正确路径重新导入即可……

一不做二不休,把几个非项目文件夹请出了workspace。重启eclipse,故障解决。

看来,还是不要太相信IDE!!

手机 Java 之怪现象

ok,我承认题目是用来吸引眼球的。但是,不能不承认的是,这篇文章会很有用,很有用……尽管目前可能只是对我有用……因为我记性不够好……

下面记载的都是手机 java 实现中各种奇怪的毛病,bug,或者……特性,是根据某项目的开发经验总结出来的。但是涵盖的手机型号还是有限。因此很有可能某些“特性”会存在于更多的采用了相同 JVM(比如平台相同、生产厂商)的手机上。

== 早期 S60 的内存泄漏 ==
这个 bug 可以上溯至 2003 年,甚至更早。表现为 java 应用中如果使用了 Class.getResourceAsStream("本地文件") 无法释放其占用的内存,是的,没有任何办法,无论是调用获得的的 InputStream 实例的 close() 或将其设为 null,甚至显式强制 System.gc(),都没有效果。结果就是至少和本地文件同尺寸的内存成为了无法回收的垃圾。这个问题还影响到以 Class.getResourceAsStream() 为基础的 Image.createImage()(这个是最要命的,如何能够不使用图片资源呢!)。

这个 bug 据说在新的 S60 上已经解决了。但是 Nokia 3230(4.0526.2ch)、Nokia 7610(6.0525.0ch)都存在这个问题。对于这些个有问题的机型,在 java 程序中是无法完美解决这个问题的,只能尽量避免。比如集中、统一载入资源,永不释放(也就是说,尽量控制泄漏的次数)。当然,这会对已有代码造成很大影响。毕竟手机 java 应用是内存受限系统的典型,大多数情况下,珍贵的内存中应该只保留需要的资源。

== 键盘响应事件 ==
在 MIDP1 中,获取键盘事件只能自己实现 Canvas.keyPressed()。但是 Motorola E398 和 SonyEricsson K700c 的实现却很奇怪。表现为左右软键有可能在这个方法中捕获不到。而是否能够成功捕获,取决于 keyPressed() 方法中代码的行数……

我承认我没彻底搞清楚这其中的玄机。鬼知道 Motorola 和 SonyEricsson 是怎么实现的 JVM。我只知道把 keyPressed 中的所有代码提取到另外一个函数中,在 keyPressed 只把参数传递给新函数,问题就消失了……

== 超慢的 drawRegion ==
除了 N-Gage QD,几乎所有的 Nokia S60 手机都实现了对 MIDP2 的支持。MIDP2 中,最为重要的几个特性之一就是 Graphics.drawRegion。这个 API 可以方便的将图片旋转、剪切之后画到画布上。

但是,这个 API 在 Nokia 3230、Nokia 7610 等手机上的实际性能表现让人实在不敢恭维。于是,这个最重要的 API 成了摆设……没什么怎么办,只能延用 MIDP1 的做法,自己实现剪切和旋转,或者像我一样懒,直接要求美工把旋转之后的图片全都做出来……

== 诡异的内存容量 ==
按照官方 Spec,Java 在 Nokia 3125 上的可用内存(即 Java Heap Size)为 512k。但是实际测试的结果是,Nokia3125 只有 412k 左右的实际内存,相差整整 100k。不过好在 Nokia 3125 并不是种市场保有量很高的型号。但是它是我正在使用的型号……

== 无法 repaint ==
这个问题只存在于 SonyEricsson K700c。表现为在 keyPressed() 中调用 repaint() 进行屏幕重画没有任何反映。

解决办法是,在 keyReleased() 中补一个 repaint()……

== UTF8 ==
还是 SonyEricsson K700c 的问题。问题存在于 new String(byte[], charset) 上。也就是说,当获得了某个 byte[],并希望用 UTF8 作为字符集将其转换为字符串的时候,使用上述方法在 SonyEricsson K700c 上会出现丢失字符的现象。这个现象很诡异,以至于我目前没有搞清楚什么情况下会丢失字符(我甚至专门写了个测试程序在真机上跑,得出的结论是丢失字符的原因可能会很复杂,简单的拿被丢掉字符附近的一个子串来测没有任何问题)。

幸亏还是有解决办法的。不用 new String 就完了,而要用更加麻烦的办法,比如像我一样,用 ByteArrayInputStream,外面套 InputStreamReader(bais, "UTF8"),然后用 StringBuffer 一个一个 char 读进来,最后再 toString()……

== 不可靠的 copyArea ==
这是 Motorola 机器上的问题,V3 和 E398 都有。copyArea 是 Graphics 的作整块屏幕像素 copy 的常用 API(2D 动态背景的游戏几乎是必不可少)。按照 Sun 官方的 Spec,手机厂商有义务来保证其 API 实现不存在覆盖冲突问题。但是 Motorola 显然做得不够好。在 Motorola 手机上使用这个 API 会随机产生贴图混乱的情况……

解决办法是自己实现另外一套机制。比如使用另外一张至少和屏幕同样大小的 Image 作为缓冲,用两次 drawImage 来替代 copyArea……不过这个方法显而易见的缺点是消耗了更多的内存(那可是不小于屏幕尺寸的 Image 啊!)。如果内存实在吃紧,只能退而再求其次,作为缓冲的 Image 继续缩水,drawImage 的次数继续增加……不过这个时候需要自己手工解决覆盖冲突……

== 无法安静下来的 3220 ==
不知道这个问题是不是在 S40 平台上都有,手里 S40 又支持 MIDI 的手机实在是太少了……

3220 的一个很明显的特征就是声音大。以至调用了 VolumeControl.setLevel(0) 之后还是有声音,和 Sun 官方的 Spec 完全不符……没办法,只能在需要静音的时候,再补一个 VolumeControl.setMute(true)。

== 永不 ready ==
这是一段手机 java 获取网络数据的常用代码:while(InputStream.ready()) { InputStream.read() }。

但是经测试,在 Nokia 3230 上,这个 ready 永远返回 false……没办法,如果不改上述代码的话,就自己实现一个继承类吧。

2006年8月27日

宁缺毋滥

越是现代人,恐怕越讨厌罗嗦。以至于无论内容是否正确,当重复的次数超过了受众的忍耐,都会引起习惯性的逆反。这个不能把责任推到受众的非理性一面,这明显跟心理学有关。对任何事物,绝大多数人类显然都有忍耐的极限。比如前些天某个大学调查的,欧洲人忍耐红灯的极限不过1分钟左右等等。中国人在这方面显然要更强一些。就在北京,我等过的最长的红灯恐怕要5分钟。而虽然大家都很不满,却习以为常。

不过今天要说的事情和红绿灯没关系。而是公交车上移动电视频道的广告。

这则广告相信大多乘坐北京公交的朋友百分之百见过。三维动画创作的范伟,愁眉苦脸,“酒不能喝,辣椒不能吃,车不能开,睡觉还得趴着”。这时同样三维动画创作的赵本山出现“咋的了?”“我痔疮又犯了”哦,到此时才恍然大悟,原来是治疗难言之隐的广告。之后便是“赵本山”的大锻独白,“北京东大……多项无痛微创技术……治疗各种内痔外痔混合痔肛瘘直肠息肉……治完就能走……北京人都知道”等等等等,诸如此类。

从技术的角度看,这则广告的制作水准相较国内同类还是相当之高。人物建模精细,过场安排紧凑幽默,画面与台词配合的也很有国内flash作者的风格,和脑白金的系列三维动画广告相当。

但是,在8月26日下午4点多的大概不到30分钟内,这则广告在移动电视上出现了不下六次……即使能够分辨广告制作水平如我,也已经在呕吐的边缘,更何况如我父辈的广大乘客,印象中,父亲无论何时何地,听到这类广告就会光火不已,认为这类广告,有伤大雅,上不了台面。于是那一路颠簸,是伴随着不断重复的“治疗各种内痔外痔混合痔肛瘘直肠息肉”过来的。下车前铁青着脸用怨恨的眼神最后瞥了那电视一眼,心里琢磨着不知道这医院给了移动电视多少钱。

倘若真的没什么可播放,请您欣赏,自然风光,动物世界,都是很好的内容。认为有东西总比没有好,就肆无忌惮的重复广告,简直是愚蠢至极的想法。

正如昨天在鼎好陪一个朋友装机,面对如苍蝇一般嗡嗡作响挥之不去的推销导购,我终于矜持不住斯文,“你们谁家我也不去,真讨厌!”