• 中文
    • English
  • 注册
  • 查看作者
  • 又一巨头从Java迁移到Kotlin:关键应用全部开始切换、安卓代码库超过千万行Kotlin代码

    Facebook 母公司 Meta 正在将其 Android 应用的 Java 代码迁移到 Kotlin。根据 Meta 的官方博客

    截至今天,其 Android 代码库已经有超过 1000 万行 Kotlin 代码
    ,旗下包括 Facebook、Instagram、Messenger、Portal 和 Quest 在内的应用都已经开始从 Java 转向 Kotlin。

    将代码库转换为 Kotlin

    Kotlin是一种更年轻的编程语言,也依赖于 Java 虚拟机。Kotlin 由软件工具制造商 JetBrains 创建,于 2011 年首次亮相,2016 年发布 1.0 版本。次年,它被 Google 采用为 Android 开发的一级语言,并由其基金会管理,该基金会由 JetBrains 和 Google 资助。

    到 2019 的Google I/O 大会,Google 正式
    ,Kotlin 编程语言已成为 Android 应用开发人员的首选语言,并在当年年底表示前 1000 个 Android 应用程序中有近 60% 包含 Kotlin 代码。

    从 Google 自身来看,明面上它说自己选择 Kotlin 的理由是它更简洁、更安全、支持结构化并发,能更轻松地编写异步代码,并且可以与 Java 互操作。不过,另一个业界推测是可能跟那宗与 Oracle 旷日持久的 Java 侵权案有关—— Oracle 花了十多年的时间追究 Google 在 Android 中使用 Java API 的侵权索赔,最终 Oracle 败诉。

    回到 Meta,Facebook 软件工程师 Omer Strulovich 对选择 Kotlin 如此解释道:“Kotlin 通常被认为是一种比 Java 更好的语言,在年度 Stack Overflow 开发人员调查中,
    ,”他还指出,由于近年来 Kotlin 已成为 Android 开发的流行语言,“因此,在努力使我们的开发工作流程更加高效的过程中,我们在Meta的安卓开发中转向 Kotlin 是非常合理的……”

    除了受欢迎之外,Meta 认为 Kotlin 拥有的主要优势包括可空性、函数式编程、代码更短、以及领域特定语言(DSL)等等。

    不过,Strulovich 指出,过渡到 Kotlin 也有一些不可忽视的缺点,比如混合代码库可能难以维护,以及 Kotlin 虽然流行,但与 Java 相比还是有比较大的差距,工具集还不够成熟。所有 Kotlin 工具都需要考虑 Kotlin 和 Java 的互操作性,这使得它们的实现变得复杂。

    但 Meta 最大的担忧还是构建时间。“我们从一开始就知道 Kotlin 的构建时间会比 Java 的要长。该语言及其生态系统更加复杂,Java 在优化其编译器方面领先了 20 年。由于我们拥有多个大型应用程序,较长的构建时间可能会对我们的开发人员体验产生负面影响。”

    为什么不只用 Kotlin 来写新代码

    Strulovich 没有透露 Meta 何时开始这种转变。Meta 本来可以选择只用 Kotlin 编写新代码,但它最终还是决定将所有的 Android 应用程序都转换过来。

    根据 Strulovich 的说法,如果是只使用Kotlin来编写新代码,继续保留大部分现有Java代码的话,工作量明显更低,但相应的也有两个缺点:首先就是要在Kotlin和Java代码之间实现互操作性,就需要引入Kotlin中的platform类型。Platform类型会导致运行时中的空指针取消引用,进而引发崩溃,这就破坏了纯Kotlin代码提供的静态安全优势。在某些复杂情况下,Kotlin的空检查省略可能会漏掉空值,意外引发空指针异常。例如,如果Kotlin代码调用由Java接口实现的Kotlin接口,就会发生这种情况。其他的问题还包括Java无法将类型参数标记为可空(最近才刚刚修复);Kotlin的重载规则考虑到了可空性,Java的重载规则却没有考虑到。

    第二个缺点是,这种方式要求对Meta已经开发的大多数软件进行代码修改。如果继续把大部分代码保留为Java形式,那开发人员就没法充分发挥Kotlin的优势。

    Kotlin迁移大法

    如今,Meta 旗下的Android 版 Facebook、Messenger 和 Instagram 应用都拥有超过百万行 Kotlin 代码,而且转换率也一路走高。纵观整个Android 代码库,其中的 Kotlin 代码量已经超过千万行。

    起步阶段

    事实上,在尝试为现有应用程序引入 Kotlin 时,Meta 遇到了不少麻烦。例如,团队得更新 Redex 才能支持 Java 无法生成的字节码模式。另外,其使用的某些内部库要求在编译期间进行字节码转换来获取更好的性能。而在将这些库纳入 Kotlin 编译过程时,这部分代码无法正常起效。为此,Meta 针对这些问题构建了专门的解决工具。

    Meta 还发现,现有工具之间存在不少冲突。例如,代码审查和wiki工具无法对 Kotlin 语法进行高亮显示。“我们还更新了之前使用的Pygments库,确保其体验与处理Java代码时一致。我们更新了一些内部代码修改工具,使其能够支持Kotlin。我们也构建了Ktfmt,一款基于google-java-format编码理念的确定性Kotlin格式化程序。”

    迁移加速阶段

    在工具准备齐全之后,Meta 现在已经能将代码中的任意部分转换为Kotlin。但每次迁移都需要大量样板设计工作,只能由员工们手动完成。J2K 是一种通用工具,并不会去理解所转换的代码是在表达什么。因此,某些特定部分就只能进行手动调整。

    最典型的例子就是Junit测试规则的使用。假设使用ExpectedException规则,来验证是否抛出了正确的异常:

    当J2K将这部分代码转换成Kotlin时,得到的就是:

    这段代码乍看之下与原先的Java代码等价,但由于Kotlin使用了site注解,所以其实际上等价于:

    尝试运行后,此测试会失败并返回一个错误:“The @Rule expectedException must be public”,这是因为Junit发现了一条带有@Rule注解的私有字段。这是个常见问题,论坛上面也已经有成熟答案:要么在字段中添加“@JvmField”;要么在注解中添加注解use-site,也就是“@get:Rule”:

    由于J2K无法(可能也不应该)感知JUnit的复杂性,所以没能正确完成转换。但即使JUnit不存在这个问题,J2K在处理其他小众框架的时候也肯定会掉类似的坑。

    例如,很多Android Java代码会使用android.text.TextUtils中的实用方法,例如isEmpty,来简化对某些字符串的检查。但在Kotlin中,其实是有内置的标准库方法String.isNullOrEmpty的。该方法之所以更好,是因为它能通过契约来告知Kotlin编译器如果它返回false,则被测试的对象不得再为null,并将其智能转换为String。

    Java 代码也有不少类似的辅助方法,也有很多库都实现了相同的基本方法。这一切都需要替换成标准的Kotlin方法,借此简化代码并保证编译器能正确检测出不可为空的类型。

    Strulovich 表示,内部发现了许许多多类似的小小修复实例。有些难度不大(例如替换isEmpty),有些则需要研究一番才能搞明白(例如JUnit规则)。还有一些其实属于J2K出的错,可能导致构建错误、运行时行为错乱等问题。

    为了解决这些问题,Meta 团队将 J2K 转换流程划分成三个步骤:

    1. 首先,取一个Java包并准备将其转换为Kotlin。这个步骤主要解决错误,并完成相应的内部工具转换。

    2. 第二步就是运行J2K。团队已经能够以无头模式运行Android Studio并调用J2K,由此将整个管道作为脚本来运行。

    3. 最后一步,对新的Kotlin文件进行后处理。具体包括大部分自动重构与修复步骤,例如将JUnit规则标记为@JvmField。在此步骤中,团队还应用了自动更新linter,并在无头模式下应用各种Android Studio建议。“当然,自动化并不足以解决所有问题,但至少能帮我们优先处理那些最常见的问题。”Strulovich 说。

    在Java重构方面,Meta 使用的是JavaASTParser等工具,它能帮助解析某些类型。而在Kotlin这边,团队还没有找到能够解析类型的好办法,所以选择使用Kotlin编译器API。

    Meta 还发布了一组自动重构方法(
    )。虽然不是很多,但希望能帮助更多开发者利用Kotlin编译器解析器高效完成工作。

    下一步

    平均而言,Meta 发现迁移后的代码行数减少了 11%。尽管
    ,但他们还是对这个数字感到满意。

    Strulovich 说,Meta 向 Kotlin 的迁移仍在进行中并在加速。“Kotlin仍然缺乏一些我们在使用Java时已经习惯了的工具和优化,但我们正在努力缩小这些差距。随着我们取得的进展和这些工具和库的成熟,我们也将努力把它们反馈给社区。”

    参考链接:

  • 0
  • 0
  • 0
  • 174
  • 请登录之后再进行评论

    登录
  • 任务
  • 实时动态
  • 发布
  • 单栏布局 侧栏位置: