手机 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……没办法,如果不改上述代码的话,就自己实现一个继承类吧。

评论