正文
.
build
();
Fabric
.
with
(
this
,
crashlytics
);
对于 Fabric SDK,我们需要一个 API key 作为连接我们网络服务器的验证密钥。这是我们要开发人员处理的事情,但需要尽量减少所需的工作量或者说繁琐度。我们的标准方法是:通过我们的构建插件提供的方式,并将其注入到清单(manifest)文件中。这里是一个例子,使用 metadata 在清单文件中插入数据:
package="con.example.SDK">
android:value="01235813213455"
android:name="com.fabric.ApiKey" />
当 Fabric 在初始化的时候,我们可以通过 package manager 获取到我们插入在清单文件中的 API key 并且继续后续工作。另外,我们可以允许其他的方法来管理这个 API key 的值,对于开源项目这么做可能会更好(保护 key 的值,因为有时我们对项目进行开源,但 key 不想开源)。例如,您可以创建一个属性文件,然后我们将在运行时读取该目录文件内容。
除了我刚才提到的实施细节,我们喜欢在设计 API 时考虑这些特点:第一个是 直觉。如果一个接口调用的行为恰好是开发人员预期的方式,而无需参考文档。
我们发现,在你的 SDK API 中使用 一致 的命名,也是有助于使用者理解。使用平常的表达语言来命名你的方法,以及类似的设计模式。并且遵循各个平台约定俗成的命名规则,比如 iOS 和 Android 平台,它们各有不同的命名规则。
最后,如果 API 很难被误用,将可以防止一些错误的发生。验证输入的参数,和书写明确的文档,将使得开发者在使用的时候,能够有信心和避免错误。也会带来一个更愉快的体验。
让我们看一个反直觉的例子:
URL url1 = new URL("http://foo.example.com");
URL url2 = new URL("http://example.com");
url1.equals(url2)
这一个感觉,将影响很深,也是 API 中最难的部分。当使用一个伟大的 API 时,我们可以猜测它是如何表现的。在这个例子里,我们将期望 equals 执行某种标准化的字符串比较。
但是实际上反直觉的是,equals 代表如果这两个 URL 解析到相同的IP地址,在 Java 中的实现,将返回 true,这里的原因是这个 API 的实现十分有趣:它发射同步的 DNS 请求。谁会想到?阻塞调用线程是一个意外行为的例子,在 API 中应该是非常明确指出的。
举一个例子,Fabric 和 Crashlytics 的初始化方式便都是一致的。在初始化 Fabric 或 Crashlytics,两个不同的二进制依赖库文件,正如我们之前看到的我们允许它们使用同一模式建造。用户可以使用无参数构造函数,或定义辅助方法来设置默认值,另外,这两者都提供了一个可用于重定义对象的生成器(builder)。
最后该讲到如何防止误用了。例如,从 Fabric Builder 的构造函数我们可以得知,Context 对象是必须的,而其它一些 setter 是可选的。一旦我们在构建的阶段中创建实例,这些可选参数也就一并被初始化。
这样设计的话,开发人员使用 API 将不能不提供 context,但可以使用其它 setters 在另外的闲暇时间。我们相信这样就很难被误用了。
我们如何才能设计出高品质的 API 呢?让我们来看看我们的设计流程。设计 API 是很难的,它通常不只是一个工程师独自坐在一个黑暗的房间,决定该是什么样子,它需要整个团队付出大量的工作。
我们在 Fabric 的 API 设计上第一个重点就着眼于我们将支持的几个平台。我们创建一个设计文档之前,任何实施工作都是这样做的,进行讨论在这些平台上,不同的方法的优点和缺点。
有一句话我很喜欢:一个 API 就像一个婴儿。他们很有趣,但他们需要18年的支持。任何 API 我们都必须要长期地支持,所以我们要让大家感觉到,我们正走在正确的路上,才能才久坚持支持下去。
最后,即使我们可以让 iOS 或者 Android 开发中愉快地使用我们的 API 了,我们还需要建立相关的平台,首要的就是让开发者们感到最舒服的。
现在我们已经设计了一些很容易使用的东西,让我们来讨论一下我们如何能获得开发者的信任,相信这是非常重要的。因此,确保软件开发工具包是可靠的,他们不影响应用程序本身的稳定性。大家都知道,相比开发应用程序,开发一个 SDK 需要更高的稳定性要求。让我们来看看如果产生了一个错误将会有什么影响。
如果一个应用程序有一个关键的错误,阻碍了它的用户使用,它可能仅仅需要发送一个新版应用程序给顾客进行更新即可。而如果是我们 SDK 发现了一个漏洞,我们很快修复它,它可能还需要一个月才能到达你的用户,在此期间,你的用户就会有很不好的体验了。
显然,如果一个 SDK 有一个严重的 bug,它的修复更新到达时间要长得多。这可能需要几个月,用你的SDK应用程序的用户才能得到错误修正。应用程序开发人员可能需要数周才能注意或升级您的 SDK 版本,并进行修复、测试 bug。所以说确保一个 SDK 的稳定性是我们的最高优先事项之一。
作为开发人员我们可以做什么,以确保尽可能高的稳定性?有一些事情是我们开发过程中的关键。首先,代码审查是非常重要的,必须得认真对待它们。然后,通过不断地问自己“这个代码有什么问题吗?”我们可以这样试着去问自己,以达到尽可能的防守。
如果能够自动获得一些基本的正确性保证,也可以在早期帮助捕捉错误,所以单元测试是非常有用的。
另一方面,人们经常忽略的是:在用户使用初次使用进行测试时候,使它能够运行你的一些 SDK 代码,这样做他们可以在你的 SDK 集成时进行捕捉 bug。
最后,持续整合(译者注:维基百科词条 -
持续整合
)和”吃你自家的狗粮”(译者注:维基百科词条 -
Eating your own dog food
)也都可以作为你的保护层,可能有助于早期快速识别问题。
使 SDK 具备可测试性和可模拟性
(10:16)
有一些技巧可以让你的 SDK 具备更好的可测试性。其中,为了测试,有时我们需要进行模拟,模拟(mock)类作为真实类的仿制类,它没有真实操作,并且允许被重写调用和验证方式。
通过避免静态方法,您可以允许在模拟实例上进行操作任何方法的调用。如果您将使用静态方法,需要确保它可以被隔离,并且您将提供所有的依赖关系,并且没有基于任何状态。
许多 mocking libraries 对于 final 的类也会产生许多问题,所以要考虑你的类扩展。在你的模拟类中不应该存在 public 属性,所以需要被访问的一切都应该通过一个访问的方法来运行。
在你的 API 中使用接口。如果您的输入点使用接口,设置类来测试将更容易。该接口允许开发人员进行重写的行为,比如契合模拟服务器或在内存中存储,来替代真实场景真实存储的开销。