Parse SDK:里面到底有什么宝贝?
Parse SDK 已经是,也是一直以来手机在 Parse 开发方面很重要的一部分。作为移动 Parse 的开发者,你应该已经从公共 API 处获得了 Parse 的 SDK,但今天我们开源了我们的 SDK,这样你最终有机会能够看一下其内部是如何运作的。
在这篇文章中,我们将分解我们在构建 Parse SDK 过程中的一些最具挑战性的方面——构建异步的API,解耦架构已经实现 API 的一致性。在接下来的几周里,我们将发表一系列的博客文章来深入介绍我们 SDK 的方方面面。
异步 API
一些 Parse SDK 中的重要功能包括网络通信,数据存储,以及返回数据给开发者用于更新其 UI。所有这些都必须在异步中进行,分离于主线程并行。知道了这些你应该不会惊讶于我们的 SDK 中最重要的部分居然是如何做异步编程。去年,我们发布了 Tasks,作为 Bolts 的一部分,这是一个基于约定的拼装起来的库,其简化了并行性与并发性。我们提到这个是因为我们在内部使用其来解决一些并发问题,但你现在可以看它的扩展部分。
我们所有内部的 API 几乎都是基于任务的。我们用其来简化异步操作的串行执行,比如在服务器上保持一个 ParseObjects 的依赖链,同时简化并行异步操作,比如保持非关联 ParseObjects 的分支结构。 这已经强大到我们能够将这两个拼接到单个异步操作中了。
/** * Saves a collection of objects in serial batches of leaf nodes. */public Task<Void> deepSaveAsync(List<ParseObject> objects) { if (hasCycle(objects)) { return Task.forError(new RuntimeException(“Unable to save a ParseObject with a relation to a cycle”)); } Task<Void> task = Task.forResult(null); List<ParseObject> remaining = new ArrayList<>(objects); while (remaining.size() > 0) { List<ParseObject> batch = collectLeafNodes(objects); remaining.removeAll(batch); // Execute each batch operation serially, awaiting until // the previous has completed. task = task.onSuccessTask((t) -> { return saveAllAsync(batch); }); } return task;}/** * Saves batches of objects in parallel. */public Task<Void> saveAllAsync(List<ParseObject> objects) { if (objects.size() > MAX_BATCH_SIZE) { // The collection of objects is too big for a single batch, // so partition the collection and execute each batch // in parallel. List<List<ParseObject>> partitioned = Lists.partition(objects, MAX_BATCH_SIZE); List<Task<Void>> tasks = new ArrayList<>(); for (List<ParseObject> partition : partitioned) { tasks.add(saveAllAsync(partition); } return Task.whenAll(tasks); } return executeBatchCommand(objects);}// * Abridged, for clarity
通过 Tasks 编写异步 API 是一件轻而易举的事情,并且我们非常建议您深入了解 Bolts 框架:Android, iOS/OS X 以及我们的SDKs:Android,iOS/OS X。
解耦架构和 API 的一致性
尽可能的延续 Parse SDK 的简单易用的传统是我们最优先的选择。但是,这样做很难既添加新的功能,还要让我们的 SDK 更加稳定而不去改变传统。此外,由于我们代码级的增长,为了给我们的开发者发布最稳定的版本,我们需要确保我们的代码可测试。
为了解决所有的这些问题,我们采取了解耦架构模块,包括我们的公开的API对象实例,对象状态,控制者和REST协议。每一块都被封装了,是为了确保关注分离和允许我们添加新功能的不同实现而不用修改太多的代码。这是所有模块如何在一起工作的示例图
我们得解耦架构模块
对象实例
对象实例是允许我们保持一个易用的和不变的 API 的片段,对于 ParseObject 来说是包涵属性的 set,get 方法和保存,获取和删除方法的 API 表面层,只要我们保持这样的完整性,我们可以在底层重构和添加新的功能而没有任何破坏性的改变。
状态
状态涉及对象的内部状态的组合,对 ParseObject 来说,状态是指当前自身在服务器上的表示和在本地运行的变化的集合,也指在本地的状态的当前表示的缓存.
这些状态实例也定义了在对象实例和控制层交互里的接口。对象实例传递它的状态给控制器,控制器返回一个新的状态,然后对象实例用这个新的状态更新自身。
控制器
控制器定义了可在每个 Parse 类型上执行的所有行为:ParseObject 可以被保存、获取与删除,ParseQuery 可以被查找与计数,ParseFile 可以被保存与获取。
基础控制器将序列化或反序列化对象状态至公共的 REST 格式并且传送所有的请求给内部逻辑网络。 这就防止我们因为实现不必要的序列化和反序列化,而使得实例与状态实现得更加复杂,同时提供了更好测试的代码插入。
我们也设计了控制器的可扩展性来使除了 Parse 通信的其它功能能被添加。其中的实例就是局部数据存储。对于这个新特性,我们可以创建另一个 ParseQueryController 的实现,而不是通过 Parse 在网络上通信。这就需要这些对象在本地设备上。