您的位置:首页 > 编程语言 > C语言/C++

看Dropbox如何使用C++进行iOS和Android跨平台开发

2014-06-06 17:08 781 查看


本文由myshire(微博)翻译自How Dropbox Uses C++ for Cross-Platform iOS and Android Development

在数周前的UIKonf 2014中,令我印象最为深刻的就是Dropbox关于如何使用C++来开发iOS和Android非界面代码的演讲。(另外,UIKonf 2014是一个很棒的活动,明年你绝不能错过它)

接下来我要说的主要是总结一下两个演讲的笔记,一是Mailbox的开发者Steven Kabbes讲的如何将Mailbox移植到Android平台,另外一个是Stephen PolettoTina Wen演讲的关于在iOS和Android同步发行Carousel首个版本的故事。有些地方我可能理解的不对,如果有什么错误请Dropbox团队原谅我。

动机
"面对现实吧,Android不会离开",Stephen Polletto面对一屋子的iOS开发者如是说。然而,对好几个平台都进行native开发是既浪费时间又昂贵的选择,另外,对同一个问题还要针对不同平台作出不同的实现。因此同一个问题也会出错多次,也需要修改多次。在一个平台上出现的问题,另一个平台上一会出现,却有可能不被发现。本来同一个应用在不同平台要有相同的表现,但是由于实现方法的不同,因此它们的行为可能会有很大的不同。另外,同时在不同平台上推出新的特性也很困难。

当开始开发Android上的Mailbox应用时,他们决定将大部分非界面代码用C++而非Java来实现,这样他们就可以在iOS和Android之间共享这部分C++代码。当时iOS应用使用了Core Data,因此将这部分Core Data代码用C++库来实现也成了工作的一部分。两个平台对C++的支持都不错,同时团队成员相对Java更喜欢用C++。

因为在iOS和Android平台同时推出产品很重要,所有Carousel团队也选择了C++。

工具链
iOS原生支持C++,通过Objective-C++可以很简单的混用Objective-C和C++。

在Android平台,可以通过NDK来调用C++,然而据说不是很好用,Dropbox发现谷歌的构建系统gyp还是蛮好用的。同时你不得不接受:Java原生接口很不好用。但是上面的问题都不应该成为你的绊脚石,Steven希望谷歌或社区能在以后建立更好的工具链。

在Objective-C社区中,C++的名声并不好,然而Steven指出C++11已经有了很多改进,如果你近期没有使用它,那你绝对有必要再看一看。事实上,Objective-C的很多特性(比如blocks和ARC)都在C++11中有了类似的实现(lambdas智能指针)。当然,C++依然是一种很复杂的语言,对很多团队来说需要时间学习。

SQLite作为一种收到广泛支持的数据存储,似乎是一种必然的选择。尽管标准的SQLite C API有点笨拙,但存在很多C++库将其封装成一个面向对象的接口,就像Objective-C中FMDB那样。

架构
客户端-服务器设计
用户界面都是使用相应平台原生的API写的(iOS上的Objective-C/UIKit,Android上的Java),而大多数的"模型层"使用共享的C++库实现。与直接调用模型层不同,Steven将其设计成一个客户端-服务器的结构,服务器端(即C++共享库)一直在线且延迟为零。将UI代码和共享库库看成两个分开的实体有助于在两者之间设计清晰的接口和两者的解耦。

这种架构同时预定义了数据在UI和C++层两者之间传递的方式,它们不会访问相同的数据对象,而是通过消息机制在其间传递数据。

想要清楚地划分这两部分并不容易,你必须准备好犯一些错误。有的表面上看上去应该是在共享层的,却需要依赖平台特定代码来实现。例如网络(iOS上使用NSURLSession后台下载)、应用后台行为、文件系统访问(iCloud默认备份所有文件)等。这些地方,他们在共享库做了抽象,又使用平台原生的方式在客户端实现它。

对于其他的平台相关API,他们必须选择其他的方式。其中一个例子就是用于存储首选项和设置项(storing preferences and settings in Cocoa)的NSUserDefaults系统,由于不能再共享库中使用,Dropbox使用了谷歌的LevelDB来替代它。

重写Core Data
C++共享库的核心部分是一个查询存储框架,差不多和Core Data的作用类似。以我的理解来看,Mailbox团队并不想完全重写Core Data:首先,他们只需要一部分功能,其次,他们发现Core Data的一部分API并不能很好的适应Android。于是,他们基于SQLite开发了自己的框架。

Steven给出了Mailbox中这个架构的大致轮廓:
1.查询对象基本上代表了一个SQL语句的参数,执行一个查询返回一个结果集。
2.结果集会被转化成DataView,在形式上代表数据的view models可以被用户界面使用(比如在table view中)。正如你看到的,View模型也是C++共享库的一部分,iOS和Android使用相同的View模型。Dataview具有不可变性。
3.当查询结果发生变化时,会返回一个新的DataView。该框架会计算出一个ChangeSetthat来描述从旧形式到新DataView的变化。前端应用可以使用theseChangeSets来更新界面而不是重载所有数据,同时这也有利于使用动画来体现视图的变化,就像Core Data中NS?Managed?Object?Context?Objects?Did?Change?NotificationNS?Fetched?Results?Controller那样。
4.当界面想改变数据时,它会给服务器发一个消息,服务器会处理这个消息,然后客户端会通知新的ChangeSet的DataView,从而界面可以相应的作出动作。

对于每个平台,团队使用原生语言编写了简单封装类以便从UI代码中剥离C++类。在Objective-C中,借助Objective-C++很容易实现。Objective-C封装类中的大部分方法基本上都是直接传递参数,根据需要改成本地数据类型就行了。对于客户端来说,它并不关心服务器是如何实现的。

图像缓存
作为一个照片应用,Carousel应用在共享库中实现了图像缓存。用户界面层将大小和位置信息传递给C++层,C++层据此可以选择相应的图像。这种算法可以在iOS和Android之间共享,因为应用使用基本相同的视图布局。

并发性
客户端-服务器的架构简化了线程模型。由于UI和C++层不共享数据,他们可以独立地维护自己的线程。主线程控制UI,C++层基本上只是一个在后台运行的单线程。

这种设计使得锁的使用变少,从而简化了架构。同时由于多数设备都具有两个CPU核心,使用一个来运行主线程,另外一个来运行存储线程是很自然的。以后多核设备普及之后,设计或许会继续改变,但是设计初期保持简单是正确的决定。

存在的挑战
显然,Mailbox和carousel团队的做法不一定适合于所有的应用。而且,魔鬼总是存在于细节中。当你真正去实现类似于这样的架构时一定会遇到一些以前没有考虑到的问题,你将不得不调整自己的方法和流程。

写这篇文章的目的,在于抛砖引玉,社区应该多一些关于此话题的讨论。

示例代码
非常感谢Steven Kabbes写了这个文章的初稿,Steven还在git上发布了他演讲的笔记,同时慷慨的设立了一个git源来分享如何在iOS、Android和OS X上实现这个过程的代码。这会对我们很有帮助,至少帮你设置好了工具链。你们可以在GitHub上参与相关的讨论
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐