Worktile 移动团队如何使用 C++ 完成跨平台应用开发<之一>

动机

“好的代码值得共享”

“快速完成第一版的原型,快速迭代”,如同许多初创 SaaS 公司一样,我们也同样信奉这个时代快速迭代的开发方式,按照设计,我们第一个上线的版本是我们的 Web 版以及 iPhone 端,紧接着,我们面临着需要做 Android 移植开发的需求,这时摆在我们面前的有几个纠结:  如果开放 iPhone 的源码给 Android 的同学 “翻译” 一遍 iPhone 的工程了话可能会面临同样的一个 Bug 被 duplicate 了一遍,然后 iPhone 的改了,再建个任务给 Android 的同学 - “Hi, 我们这里改了一个 Bug, 你们也改改吧”,然后每做一个模块,再各自产生一堆的 Bug,再疯狂的改 Bug - 好的代码没有被复用,只是 Bug 被一次又一次的提上日程。而且在快速迭代中,我们需要用很少的人力(1 iOS, 1 Android)来完成初期版本的开发,而且在快速的迭代过程中,我们需要频繁的面对诸如接口变动,某个类添加了一个字段这种需求,所以我们自然开始探索起一份跨平台的解决方案。

在探索了当前主流的跨平台技术路线中,runtime 方式跨平台的(如嵌入一个 WebView)方式由于其性能有明显的滞后感并没有列入我们的考虑范围,我们主要考虑了使用 C++ 这种各个平台都支持的方案以及“翻译系”的方案(j2objc 等)  后,针对我们的背景: 开发人员比较少,而且之后招聘到的人员应当是以原生平台语言为主 (这点在事后被证明创业公司非常需要考虑,比如做 iOS 开发的招到一个会 Swift 的也会 Java 的相对来说还是很困难的),最终决定使用 C++ 11 作为核心库的语言 (核心库指的是 MVC 中 Model 层的所有代码)。

在1年半的实践中,C++ 11 带来的好处主要有
稳定: C++ 的方法几乎不会出现 deprecated 的状况,相对于我们 iOS 使用 Swift 几乎每次 Swift 都有代码迁移的工作, C++ 的代码部分极其稳定
性能: 这点其实在对性能要求相对较低的 App 开发领域,其实不是特别特别需要考虑的问题
容易搜索: 作为一门历史悠久的成熟语言,大部分的问题都可以 Google 出来 (这点其实也是非常重要的)
单元测试: 依赖于 gtest,单元只需要写一遍, 并且可以运行于所有的平台之上 (单元测试是我们目前发现的唯一一种可以保证代码质量的方法)
有现成的代码规范: 基于 Google C++ Code Style 以及其提供的 lint 检测工具,我们可以很大程度上保证 C++ 代码的可读性,这份 Code Style 很好的解决了 C++ 内存管理部分难以判断的问题

方法

“C++ 11 是一门全新的语言”

C++ 在 iOS 上可以通过 Objective-C++ 的形式进行几乎原生的调用,并且由于 C++ 11 Smart Pointer 的出现,几乎不需要进行额外的内存管理。

在 Android 平台上,C++ 是通过 NDK 进行的调用,对于 Java 的开发者来说, C++ 仍然是一门有着比较陡峭的学习曲线的语言,但是在工程开始初期,团队只需要1名 C++ 的编写者,所以还是有比较充分的学习时间留给 Android 开发的同学。

在成功的 Hello World 之后接下来就是考虑一些常用模块的迁移 (这里也是一个极其重要的需要预先调研的地方,如果一个功能的第三方库没有找到很好的 C++ 的替代品,那么用 C++ 自行实现一次有时候会是一个非常巨大的成本)

对于我们常用的网络链接、缓存等功能,都已经有一些现成的成熟第三方库可以满足我们,对于 App 开发,常见的有:

HTTP 网络连接: (libcurl)

SQLite 持久化存储(easySqlite)

JSON 解析 (json11)

XML 解析 (tinyxml2)

Websockets (libwebsockets)

Key-value 缓存 (levelDB 或者用 SQLite)

结构

如上图所示,我们将所有复杂的核心逻辑都写在了 C++ Core 这层,Objective-C++ 和 Java Wrapper 只是简单的将 C++ 的方法以 Objective-C 和 Java 的形式暴露给了 App Developer 进行调用 (Win C++ 可以直接调用标准 C++,只需要 dll import/export 好了对应的代码就行)。对于 UI 层的界面代码,我们仍然使用 Swift 以及 Java 进行书写,这样可以保证我们最大可能的能获取原生 App 带来的好处,以及对系统自身的 API 访问的便利性,而且原生的 App 开发者在进行产品布局等改动的时候也能最大程度的获得原生平台的大量的第三方库的帮助。

示例代码

本篇我们将提供最简单的 iOS 以及 Android 示例代码 来阐述我们的结构。

下一节

下一节中我们将以 Worktile OpenAPI 介绍我们是如何搭建一个包含网络请求、缓存的应用。

再下一节

下一节中我们将介绍我们如何使用代码自动构建工具构建出我们的 Model 层代码以节约反复编写同样结构代码的时间。