如何处理J2ME中最常见的类缺失问题

13

我正在尝试编写一个应用程序,可以在不同的Java平台上运行,例如J2SE,J2ME,Android等。 我已经知道我将不得不为每个平台重新编写大部分UI,但希望重用核心逻辑。

保持此核心的可移植性涉及我所知道的三个缺点:

  1. 遵循旧的Java 1.4语法,不使用Java 5.0的任何有用语言特性
  2. 只使用已知适用于这些平台的外部库(也就是说:不使用JNI并且没有依赖于违反此规则的其他库)
  3. 只使用存在于所有这些平台上的类

我知道如何克服(1):以5.0风格编写代码,并自动转换为1.4 (retroweaver - 尚未尝试,但似乎可以)。

我认为(2)是一个我必须接受的问题。

现在我想知道(3)的最佳解决方法,特别是集合类,这是我最需要的。我能想到以下几种方法:

  • 我认识的大多数程序员都不使用SetMapList等,而是退回到Vector和普通数组。我认为这首先使代码变得丑陋。但我也知道,在TreeSet/HashsetLinkedList/ArrayList之间做出正确的选择对于性能至关重要,总是使用Vector和数组是不正确的。
  • 我可以编写自己的实现。这似乎是重复造轮子,而且我认为我不能像其他人一样做得那么好。
  • 由于Java是开源的,我可以在构建J2ME应用程序时获取J2SE集合框架的源代码并将其包含在我的应用程序中。但我不确定这是否是一个好主意。也许有很好的理由不这样做。
  • 也许已经有一些库可以重建集合框架的最重要特性,但针对低端系统进行了优化,可能通过不实现罕见使用的功能来实现。你知道有哪些吗?
  • 感谢您的答案和意见!

    编辑:我最终找到了一个(复杂但不错的)解决方案,并且我认为通过提供自己的答案并接受它,解决方案将变得可见。但事实上,我的答案仍然在最下面。

    4个回答

    13

    J2ME非常严苛,你必须做好没有其他平台上的某些优美特性的准备。要熟悉Hashtable和Vector,并在其上编写自己的包装器。此外,不要犯认为J2ME是标准的错误,因为每个制造商的JVM可能会以根本不同的方式执行操作。起初我不会太担心性能问题,因为在J2ME上实现正确性已经足够具有挑战性了。可以编写跨J2ME、J2SE和Android运行的应用程序,我已经完成了,但这需要大量的工作。我的建议是,编写应用程序核心逻辑并将其严格限制在java.lang、java.util和java.io中。在任何可能与平台交互的地方(例如文件系统或网络),您可以创建一个接口,使您的核心应用程序代码与之交互,针对不同的环境提供不同的实现。例如,您可以有一个包装HTTP内容的接口,在J2ME上使用javax.microedition.io.HttpConnection,在Android上使用java.net.HttpURLConnection。虽然很麻烦,但如果要维护在这三种环境下运行的应用程序,这样做是可行的。祝好运。


    12

    我问过这个问题一段时间了,也找到了一个好的、可行的解决方案,但后来忘记告诉你了。

    我的主要重点是Java集合框架,它是java.util包的一部分。

    最终,我获取了Sun Java 6.0的源代码,并将属于集合框架的所有类复制到自己的项目中。这是一个Java 6.0项目,但我使用J2ME的jar作为classpath。我复制的大部分类依赖于其他J2SE类,因此存在损坏的依赖项。无论如何,通过省略与序列化有关的所有内容(这不是我的首要任务)和进行一些小调整,很容易削减这些依赖项。

    我使用Java 6编译器编译了整个项目,并使用retrotranslator将结果字节码回转到Java 1.2。

    下一个问题是包名称,因为您无法使用J2ME应用程序提供java.util类并加载它们-引导类加载器不会查看应用程序的jar文件,其他启动加载器不允许加载具有该包名称的任何内容,并且在J2ME上您无法定义自定义类加载器。Retrotranslator不仅可以转换字节码,还可以帮助更改现有字节码中的名称引用。我不得不移动和重命名我的整个项目中的所有类,例如java.util.TreeMap变成了my.company.backport.java.util.TreeMap_

    然后,我能够在第二个Java 6.0项目中编写实际的J2ME应用程序,该应用程序引用通常的java.util.TreeMap,使用通用语法创建类型安全集合,将该应用程序编译为Java 6.0字节码,并运行它通过retrotranslator创建引用my.company.backport.java.util.TreeMap_的Java 1.2代码。请注意,TreeMap只是一个例子,它实际上可以应用于整个集合框架,甚至适用于参考该框架的第三方J2SE Jar。

    生成的应用程序可以打包成jar和jad文件,并在J2ME模拟器和实际设备上运行(在Sony Ericsson W880i上进行了测试)。

    整个过程似乎相当复杂,但由于我使用了Ant进行构建自动化,并且我需要retranslator,因此只需一次性设置集合框架后移植即可。

    如上所述,我大约一年前完成了这项工作,并且这篇文章主要来自我的头脑,所以希望没有错误。如果您对更多细节感兴趣,请给我留言。我有几页关于该过程的德语文档,如果有需求,我可以提供。


    你愿意提供你的集合框架后移版本吗?Retrotranslator很好用,但我不得不自己编写后移版本才能让自动装箱正常工作。 - Ralf
    你能分享这个回溯吗? - domino

    3
    我们在开发zxing时遇到了这种情况。如果J2ME是你的目标之一,那么这绝对是你的限制因素。我们的目标是MIDP 2.0 / CLDC 1.1。如果你有类似的要求,你需要坚持使用Java 1.2。Java 1.4语言特性肯定不存在(如assert),而且通常你在J2ME中找不到1.2之后的任何东西。
    我们没有使用外部库,但是你可以将它们打包到你部署的.jar文件中,几乎不会有什么问题。这会使得生成的.jar文件更大,这可能是一个问题。(然后你可以尝试像ProGuard这样的优化器/缩小器来缓解这个问题。)
    我最终重新实现了类似Collections.sort()和Comparator的东西,因为我们需要它们,而它们在J2ME中不存在。所以在必要的情况下,你可能需要考虑这样做。
    我们在J2ME中使用了Vector、Hashtable和数组,因为实际上没有其他选择。除非你有理由不使用它们,否则我建议仍然使用它们,而这个理由可能是性能。理论上,JVM制造商已经优化了它们的实现,但这并不意味着你不能做得更好...我想如果在绝大多数情况下这样做是值得的,那我会感到惊讶。在付出努力之前,请确保你真的需要这样做。

    2
    Java ME 的纠正:CLDC 使用 1.3 版本的代码,CDC 使用 1.4 版本的代码。我现在无法记起 1.2 和 1.3 之间有哪些更改,但 CLDC 使用的是 1.3 版本。 - Malcolm
    1
    从1.2到1.3没有语言上的变化,只有API和JVM的变化。当然,CLDC有一个完全不同的API子集和JVM。所以我想两者都可以,但考虑到涉及的日期,我想你的说法更正确。 - Sean Owen
    2
    哇,没想到这么老的评论还能得到回答。我的评论实际上得到了CLDC规范的支持,该规范指出“CLDC支持从Java Standard Edition 1.3.1版本派生出的许多类”。我同意尽管Java的1.2和1.3版本之间语言没有改变(第二个JLS适用于两者),但由于CLDC是一个非常有限的子集,所以没有太大的区别。 - Malcolm

    2
    为了回答你的问题,另一个集合库是Javolution,它可以构建为j2me。请参考Javolution

    嗨,谢谢,看起来很有趣。你知道我是否必须使用整个Javolution库(它确实很好,但可能会带来其他问题/缺点),还是我可以单独使用Javolution的集合类? - Lena Schimmel
    我不确定,我从未这样使用过,所以无法告诉你。 - BenM
    我也曾经想过这个问题,但是在尝试仅隔离几个集合类的经验中,我发现库本身存在许多相互依赖。 - Marc Novakowski

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接