本文内容已经开源,包括:j2objc 工具链,Android库移植

在移动端众多的跨平台方案中,j2objc 方案是一种比较特殊的存在。它不像 Flutter 那样几乎完全重新搞了一套语言和框架,也不像 React Native、Weex 等通过 JS 脚本引擎与原始功能对接。那 j2objc 是怎么样实现跨平台的呢?

J2objc 简介

首先,j2objc 是一个语言转换工具,它将我们用 java 语言实现的代码,转换为 Objective-C (OC) 语言的代码,同时提供了一个基于 iOS Foundation 实现的 Java 运行时环境(JRE.framework)。使得我们用 java 实现的功能可以在 iOS 上运行(或者被执行)。

所以,j2objc 本质上是一个工具,并没有提供完整的跨平台开发框架。我们需要在他的基础上搭建一个跨平台,这也是本文重点介绍的内容。

相比其他跨平台方案,使用 j2objc 实现跨平台,有它的优缺点。

优点有:

  • 在 Android、iOS 平台都是通过原生语言构建,不需要引入额外的引擎。这意味着我们省去了很多的针对新框架的基础设施方面的工作(如日志、网络、奔溃上报等),同时也不需要为跨语言 RPC 烦恼。
  • 在 Java 中,存在很多优秀的开源方案(如 okhttp、gson 等),都可以通过 j2objc 移植到 iOS 平台,能够解决 iOS 基础模块不足的问题,并且充分发挥使用 java 语言开发的优势。
  • 在 Android、iOS 平台都是使用原生的 UI、渲染、动画实现,提升体验的同时,也保证了稳定性

缺点有:

  • 不能提供完整的一次开发,双端运行的框架,涉及 UI 交互、视觉部分的工作,仍然需要双端开发人员的参与
  • 只能在 Android、iOS 平台实现代码共享,无法迁移到更多的平台

结合团队人力资源的配置,当团队有双端开发人员,且 iOS 人员相对短缺时,最适合引入 j2objc 跨平台方案。这样能够充分利用现有的开发资源,也基本不需要引入其他领域的人才。

下面我们就来介绍,如何使用 j2objc 进行跨平台开发。

跨平台应用架构

首先我们要明确跨平台开发的整体架构目标。

在移动端应用开发中,我们主要的任务是:

  • 实现基础开发库
  • 搭建开发框架
  • 打造基础功能组件
  • 开发业务组件

以上任务存在一种层层递进的关系,但是不管是基础层,还是功能层、业务层,都有跨平台的需要。

模块化、组件化是软件架构的基本形式,跨平台开发只能加强,而不能破坏这样的软件架构。

不是所有的功能都能够通过 j2objc 实现跨平台,所以在跨平台的模块中可能既包含跨平台的代码,也包含平台各异的代码。这是一种混合开发,但是又不存在跨语言边界。

下面我们通过业务开发框架来说明 j2objc 跨平台开发的主要形式。

跨平台业务开发

在业务开发中,主要面对网络、数据库,以及各种各样的业务逻辑。这通常被认为是业务开发框架。

下面是我们认为的 Android 业务开发的最佳实践:

这里主要包括:

  • 对后端网络服务的访问:通过 OkHttp + Retrofit + Gson 实现
  • 访问数据库:通过 greendao 或者 ROOM 生成数据库访问接口
  • 异步处理:通过 RxJava 管理异步逻辑
  • 具体的业务逻辑:基于 MVVM 的业务开发框架,实现具体的业务处理逻辑

这样的一个业务开发框架,包括网络、数据库、异步管理的基础部分,都需要整体移植到 iOS 平台,形成一个跨平台的业务基础库。

业务基础库也包含平台相关的部分,比如:

  • 针对主线程调度的抽象(Scheduler)
  • 针对交互页面生命期管理的抽象(Lifecycle)
  • 日志输出的抽象(Logger)
  • 平台信息(如设备Id 等)

在进行具体业务开发时,基于业务基础库,实现具体的业务逻辑。在 MVVM 模型中,主要是实现 Model、ViewModel 的逻辑,这部分可以跨平台。

最后剩余的只有 MVVM 模型中的 View 层了,需要我们在 Android、iOS 平台各自实现。

因此,同一个模块,既包含 Android 平台的代码(Java、Kotlin),也包含 iOS 平台的代码(OC、Swift),以及跨平台共享的代码 (Java)。我们的模块项目,可以在 Android Studio、Xcode 中分别开发调试,跨平台的代码双端开发人员都可以修改,并通过 git 快速同步给其他开发人员。

可以看出,基于 j2objc 的跨平台开发,其最大的特点是:在 Android 平台,现有的开发实践,不需要做什么大的调整,该使用的开源模块仍然可以使用,在他们基础上搭建的 Java 开发框架只需要做一些基础的平台兼容性抽象,在业务开发中基于 MVVM 实现的 Model、ViewModel 只要严格遵循隔离原则(与 UI 视图层隔离),那么,大部分代码就是可以跨平台的。

所以,在搭建基于 j2objc 的跨平台开发框架时,我们的主要处理 iOS 平台的快速开发需求。

J2objc 工具链

工欲善其事,必先利其器。要提升开发效率,一个好的工具链是必不可少的。

在 iOS 平台,我们需要基于 j2objc,结合其他工具,打造一个 Xcode 上的混合开发工具链。

工具链就是将持续的实践经验,积累下来,形成一套充分自动化的、容错的、可重复部署的工具集合。

我们的 J2objc 工具链需要包括下列功能:

  1. 管理 java 源代码文件;将 java 文件导入到 Xcode 项目,加入 java 文件构建的规则
  2. 管理 java 模块之间的依赖;在构建过程中,为依赖项目准备依赖的 jar(class)以及其他构建配置的依赖
  3. 管理 j2objc 代码转换;将 java 代码转换为 objc 代码,检测 java 文件的变化,更新构建缓存中的 objc 代码文件
  4. 协助打包 Framework 库;在最终 Framework 中包含 j2objc 生成的 Header (.h)文件,jar 文件,以及其他 j2objc 相关文件

我们从使用介绍上,来对工具链做一个初步的认识:

研发阶段

使用方式

创建 Xcode project

配置依赖的 Framework (可能包含 java),这一步不是工具链必须的,但是工具链会处理对部分 Framework(包含 jar) 的依赖

配置 java 文件目录,按照 AS 的文件组织,一般是 src/main/java

执行 prepare 脚本,更新依赖的 Framework,同步 java 目录

在 Modular 头文件中包含 (import) XXX-J2objc.h,其中 XXX 是 Xcode target 名称

开发构建阶段

不定期执行 prepare 脚本,同步 java 文件列表

修改 java 文件,然后直接构建

调试、运行阶段

工具链会潜入代码符号位置信息,可以在 java 文件中设置断点,单步调试

发布阶段

通过执行 publish 脚本,将 Framework 发布出去,供其他模块引用

在准备(prepare)阶段,工具链完成下列工作:

  1. 在 Xcode project 中增加处理 java 文件的构建规则 (Build Rule)和构建阶段(Build Phase)
  2. 收集所有 java 文件,导入到 Xcode project 中;再次执行时,会同步 java 文件的增删
  3. 在 Xcode project 中配置 J2OBJC_HOME、JAVA_HOME 等环境变量

在构建阶段,工具链主要完成了下列工作(如下图):

  1. 搜集所有 java 文件,与上次状态(结合时间、Md5)对比,如果没有变化则结束
  2. 搜集依赖的 Framework,将其中的 classes.jar 加入到 j2objc 转换过程中的 classpath
  3. 搜集依赖的 Framework,将其中的 prefixes.txt 与自己的 prefixes.txt 合并,作为 j2objc 转换过程中的 prefixes 输入
  4. 调用 javac/jar 生成自己的 classes.jar,后续提供给依赖模块使用
  5. 调用 j2objc 生成 objc 文件(包括 .m、.h 文件)
  6. 修正生成文件中的 include 路径,采用 <Module/Path/Subpath.h> 形式
  7. 拷贝 j2objc 生成的 .h 文件到 Framework/Headers 目录中
  8. 生成 XXX-J2objc.h,包含(import)所有 j2objc 生成的 .h

在实践中,我们面临几个重大问题,但是都逐步解决了:

问题

详情

方案

Java 增量构建

Java 没有变化时,不需要j2objc转换,部分文件变化,只增量编译变化的文件

比较文件修改时间,如果不一样,再比较 Md5,通过两级过滤,尽量减少不必要的构建

支持 Swift Modular 开发

Swift 引用 Java 类,需要 Publish Modular Header,但是 Xcode 不能管理这些动态生成的头文件

在早于 Swift 构建阶段,将头文件拷贝到 Framework 的 Headers 目录,并且用一个总的头文件 import 所有从 java 转换得到的头文件

Module 规范化

J2objc 生成的代码,使用 C 风格的 include,在外部使用中,需要配置头文件搜索路径

通过 awk 脚本修正 include,使用 <Module/Path/Subpath.h>形式,并且将 JRE 也 Module 化

Java 文件管理

手动添加 java 文件效率低且容易遗漏

使用 xcodeproj 工具(源于 CocoaPods 项目),实现一个 ruby 脚本,自动同步 java 文件到 Xcode 项目中

与 Pods 的配合

使用 Pods 管理依赖比较方便,J2objc 的依赖管理不应该自创一套

充分利用 Xcode 中的 Frameworks 依赖信息,以及 Frameworks 搜索路径,基本做到 Pods 之后零配置

模块移植

搭建基于 j2objc 的跨平台开发框架,另外的一个大的任务是将 java 模块移植到 iOS 平台。

通过 j2objc 移植一个模块,常见的任务是:

任务

详情

方案

包名映射

默认转换后类型名称带有包全名,使用起来代码太,如:Okhttp3InternalHttp1

Http1ExchangeCodec

编辑 prefixes.txt 映射文件,将包名替换为缩写形式,工具链会将 prefix 映射传递给依赖模块,遵循统一的缩写规则

反射优化

大部分运行时类型信息是不需要的,通过优化可以减少输出产品的体积

编辑 reflects.txt 文件,在其中列出需要反射信息的 java 文件,工具链通过二次转换生成这些类的反射信息;其他文件默认没有反射信息

引用泄露

在 java 中,循环引用不会导致泄露,但是转换为 objc 代码,就会存在引用泄露问题

通过 cycle_finder 查找循环引用,然后将部分引用,通过特定注解替换为弱引用,j2objc 支持外部输入注解

这里,引用泄露需要特别小心地处理,大部分模块都存在这个问题。

除了上述常见任务,每个模块都会有一些移植问题。常见的问题有:

  • 平台依赖

有些模块依赖 Android 平台的 API,比如 Okhttp 的 DefaultTaskExecutor 依赖 Android 的 mainLooper,需要在 iOS 平台重新实现。

  • TLSSocket

HTTPS SSL/TSL 支持,在java 中,ssl 功能由外部 provider 提供。在 ios 上没有相应的实现。

后来有人简单实现了 IosSslSocket,并加入到 j2objc 框架中

在应用层使用下面的方式获取

SSLContext.getDefault().getSocketFactory()
  • 代码体积

包名压缩、反射优化能够优化一些代码体积,但是有些模块(比如 RxJava)功能太多,生成的 objc 库体积过大,还需要进一步优化。

我们的方案是:针对性的,将需要使用的功能列举出来,通过 proguard 生成删除代码清单,然后传给 j2objc 处理。

我们已经移植了下列模块,满足日常开发需要。

模块

集成挑战

说明

gson

循环引用泄漏

okio

okhttp

TSL 实现

RxJava

回调线程问题,代码体积过大

retrofit

回调线程问题

lifecyle

livedata-core,livedata

databinding

common,runtime

protobuf

管理proto文件的构建

使用 javalite,protoc3.0.0

在日常开发中,有几个问题需要考虑:

  • 代码文件管理

跨平台组件应该只有一个代码仓库,虽然它里面包含多种语言、多个平台的代码,但是整体上是一个组件;从产品角度看,它在各个平台的功能以及外部表现应该是一致的。

所以我们强烈建议用一个代码仓库管理所有相关代码,而不是 Android、iOS 平台分开管理。

采用同仓库管理,可以让代码修改更快的同步到各个平台;可以使得代码修改记录的轨迹更清晰;更能够促进双端的开发人员都参与到公共逻辑的维护中。

  • 代码目录管理

在组件开发中,应该尽量在独立目录中管理跨平台部分的代码。建议使用 src/shared/java 目录;当然也可以在特定的包中管理跨平台代码。

  • 代码版本管理

因为构建的链条较长,一旦出现运行时问题,如何追踪问题对应的代码状态,如何重构问题是需要关注的。

我们将每次构建对应的 git commit id,写入代码中,在通过编译、链接后仍然保留这个 id,最后在运行的时候,输出到运行日志中,保证问题是可追踪的。

以上就是我们在跨平台开发上的经验分享,后续我们还将整理发布“J2Objc 工具链”和“开源模块的 iOS 移植”,欢迎大家使用并提出宝贵的意见!

基于 J2objc 的跨平台组件化开发实践相关推荐

  1. Vue.js组件化开发实践

    Vue.js组件化开发实践 前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了一下的内容.首先会对使用Vue进行开发的一 ...

  2. Android组件化开发实践(九):自定义Gradle插件

    本文紧接着前一章Android组件化开发实践(八):组件生命周期如何实现自动注册管理,主要讲解怎么通过自定义插件来实现组件生命周期的自动注册管理. 1. 采用groovy创建插件 新建一个Java L ...

  3. VUE.JS 组件化开发实践

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模 ...

  4. vue组件化开发实践

    前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了一下的内容.首先会对使用Vue进行开发的一些前期需要的技术储备进行简单 ...

  5. 美团前端组件化开发实践

    随着前端开发复杂度的日益提升,组件化开发应运而生,并随着 FIS.React 等优秀框架的出现遍地开花.这一过程同样发生在美团,面临业务规模的快速发展和工程师团队的不断扩张,美团历经引入组件化解决资源 ...

  6. iOS组件化开发实践

    目录: 1.组件化需求来源 2.组件化初识 3.组件化必备的工具使用 4.模块拆分 5.组件工程兼容swift环境 6.组件之间的通讯 7.组件化后的资源加载 8.OC工程底层换swift代码 9.总 ...

  7. Android进阶——组件化开发实践(一)

    一.组件化的意义 随着Android 项目代码和结构逐渐复杂,维护成本会指数型上升,通常我们会利用Android Studio自带的Module去拆分项目代码.但这种拆分显然需要基于一定逻辑和结构,目 ...

  8. 基于 MVP 的 Android 组件化开发框架实践

    一.背景 当我们的项目变得越来越大,代码变得越来越臃肿,耦合会越来越多,编译速度越来越慢,开发效率也会变得越来越低,怎么办?这个时候我们就需要对旧项目进行重构,即是模块的拆分,官方的说法就是组件化. ...

  9. 基于 MVP 的 Android 组件化开发框架实践 1

    一.背景 当我们的项目变得越来越大,代码变得越来越臃肿,耦合会越来越多,编译速度越来越慢,开发效率也会变得越来越低,怎么办?这个时候我们就需要对旧项目进行重构,即是模块的拆分,官方的说法就是组件化. ...

最新文章

  1. XDP/eBPF — eBPF
  2. NMF和SVD在推荐系统中的应用(实战)
  3. 【漫画】程序猿 996 会猝死,而企业家 007 却不会?
  4. 亚马逊aws 服务器删除_AWS推出Apache Airflow全托管工作流MWAA
  5. 编程方式重启 ASP.NET Core 网站
  6. pythonhash加密_Python字符串hashlib加密模块使用案例
  7. linux软件安装管理
  8. c语言实现路由功能,前端路由的两种实现方式,内附详细代码
  9. 软件开发python方向_2020年软件开发7大学习方向!
  10. centos6.5里用yum简单安装配置lamp
  11. 期末作业代码网页设计代码——蛋糕甜品店铺(11页) HTML+CSS+JavaScript 关于美食甜品的HTML网页设计
  12. 数据分析--优化模型
  13. UDS(ISO14229)诊断服务功能及描述完结篇
  14. LaTeX论文排版参考文献格式转换
  15. 只有程序猿才能看懂的段子,不笑你拿小拳拳捶我!!!
  16. java订单超时取消设计_订单超时30分钟自动取消
  17. ‘lengths‘ argument should be a 1D CPU int64 tensor, but got 1D cuda:0 Long tensor
  18. Java虚拟机知识点快速复习手册(上)
  19. Vue.js中的v-model指令(双向绑定)
  20. pycharm注册码,之前的已经过期

热门文章

  1. 两个html如何连接,两个路由器怎么连接图解设置
  2. 是什么样的奔驰销售经理逼出一个“博士后”女车主?
  3. NTFS文件系统的DBR与DBR的备份
  4. Gamma分布与逆Gamma分布
  5. 百度被判歌词搜索侵权
  6. IT龙门阵161期预告:金山办公软件副总裁章庆元谈WPS的互联网
  7. 前端vue项目-关于下载文件pdf/excel
  8. 下载单张图片到本地相册
  9. af_netlink_netlink socket编程实例解析
  10. Oracle sqlplus 上下键失灵解决方案