Twitter 的 Fabric 是知名的注重质量的 SDK,并已部署在数十亿的设备。在此次 Øredev 演讲中,来自 Twitter 的 Ty Smith,揭示了 Fabric 团队建立他们 Fabric 的各类原则,特别是在 Android 方面。经过深刻参与技术决策团队,Ty 了解到不少信息,他展现了团队在建立这个 SDK 过程当中,学到的各类经验心得,关于稳定性、性能、SDK 体积控制、以及对于一些特殊状况的处理这些方面。不管你如今或未来想要建设一个 SDK,经过此次演讲你应该能收益不少关于设计 SDK 的伟大想法。javascript
做为开发者,咱们中的大多数人不得不使用 SDK 和 API,但常常地,咱们会遇到一些使人沮丧的或设计很差的东西。要使得开发伟大的应用程序更加轻松,做为核心的软件开发工具包还有很长的路要走。在 Fabric 上,咱们很关心开发者的经验,咱们花了大量的时间,使咱们的 SDK 更容易和更有趣。个人名字是 Ty,在 Twitter 的 Android Fabric 工程师团队。我想与大家分享一些咱们在开发 SDK 过程当中学到的经验。java
那么 Fabric 到底是什么呢?它是一套 Twitter 开发的模块化开发工具包,Twitter 共享了一个共同的移动和Web平台,用于减小占用空间和提供一致性的解决问题方案,旨在提高第三方移动 App 的质量。咱们去年发布了 Fabric,这是彻底免费的。咱们相信这是一个伟大的方式来引导你的移动应用程序。就在上个月,咱们宣布了多个外部合做伙伴,如今正在基于Fabric 进行开发,将来将有更多高质量的内容提供给你们。node
咱们创建 Fabric SDK,咱们保持了几个目标,帮助引导咱们进行开发。这些原则决定了咱们开发 API 和作决策的选择。这些想法能够融入你本身的SDK或甚至你的应用。咱们很高兴可以看到你们离开这里后,在将来开发的大家的 SDK 中采纳和咱们一致的想法。android
在咱们进入 library 或 SDK 编码以前,咱们有必要考虑几个方面。git
首先要考虑的是要找出你在 library 里的实际服务的对象是谁,是内部开发人员仍是公共开发人员?谁会使用它?它带来的新价值是什么?市场上已经有了一个解决办法吗?若是是这样的话,你应该是去对其进行开发和贡献而不是从新创造一个“轮子”。github
考虑开源与闭源是一个大问题。开源一般会让你更好地经过社区,得到更稳定的软件,以及更热心的内部工程师。然而,须要思考的事,你的 SDK 仅仅是集中于一个工程点呢?仍是说它是一个完整的产品,但有一个后台服务呢?设计模式
所以,仔细考虑你将采用哪种许可证(开源协议)。例如,若是你使用 GPL 许可,那么将会使得用了你的 SDK 或 library 的人也必须得使用 GPL 开源协议。更灵活的许可证多是 Apache 2 或 MIT 许可。api
特别是对于 Android,打包你的代码并不必定是简单的。你有三个问题须要考虑。首先是对于一个标准的库项目,开发者将他们包含到他们的代码中,而且由 IDE 帮忙链接它们,它是很是灵活的,可是若是他们须要分叉(fork),他们得如何保持更新?缓存
在 Java 的世界 jar 包是另外一个好例子——很标准的二进制包装。不幸的是,对于 Android,它们不能捆绑打包本身的资源文件,因此对于一些和视图相关的 library 通常都不能采用 jar 方式打包。安全
最后,在 Android 世界还有一种打包方式即 aar,它是谷歌如今支持二进制打包方式。这是一个压缩的容器,包含了编译的源代码以及资源文件,它能够经过 Gradle Maven 依赖源从而很是方便快捷地分发给开发者。
最后的考虑是在哪里托管你的打包结果。Maven Central,是标准的仓库。然而,他们都须要开源许可,由于他们想保护他们的服务的用户,他们不但愿有人会隐式地拉下来一个二进制包,并选择一个他们没有获得审查的服务条款。若是使用了另外的资源库(若是你有一个专有的二进制文件),则开发人员必须手动添库到编译脚本中。
在创造 Fabric SDK 的工做是一个梦幻般的学习过程。咱们的目标就是涵盖这五大方面:易用、稳定、轻巧、灵活,很好的支持。咱们相信伟大的 SDK 要实现这些得走很长的路。
其中的一个关键就是可用性。咱们认为产品应该是易于使用的。
那么所谓的易用究竟是什么呢?咱们想创造一种最简单的方式,让人们在他们的应用中开始使用 Fabric. 若是它是易用的,它应该是不须要侵入太多你的代码或者你须要作不少繁琐的集成工做。只要在你的代码中新增一行咱们的代码,就可使用它了,相似这样:
Fabric.with(this, new Crashlytics());
但易用的同时,有时还得可以定制,许多开发者可能但愿更多的定制。要作到这一点,咱们使用的 Builder 生成器模式设置一些选项,好比设置一个监听器好让程序在应用程序崩溃以前通知你。
Crashlytics crashlytics = new Crashlytics.Builder() .delay(1) .listener(createCrashlyticsListener()) .pinningInfo(createPinningInfoProvider()) .build(); Fabric.with(this, crashlytics);
对于 Fabric SDK,咱们须要一个 API key 做为链接咱们网络服务器的验证密钥。这是咱们要开发人员处理的事情,但须要尽可能减小所需的工做量或者说繁琐度。咱们的标准方法是:经过咱们的构建插件提供的方式,并将其注入到清单(manifest)文件中。这里是一个例子,使用 metadata 在清单文件中插入数据:
<manifest package="con.example.SDK"> <application> <meta-data android:value="01235813213455" android:name="com.fabric.ApiKey" /> </application> </manifest>
当 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 具有更好的可测试性。其中,为了测试,有时咱们须要进行模拟,模拟(mock)类做为真实类的仿制类,它没有真实操做,而且容许被重写调用和验证方式。
经过避免静态方法,您能够容许在模拟实例上进行操做任何方法的调用。若是您将使用静态方法,须要确保它能够被隔离,而且您将提供全部的依赖关系,而且没有基于任何状态。
许多 mocking libraries 对于 final 的类也会产生许多问题,因此要考虑你的类扩展。在你的模拟类中不该该存在 public 属性,因此须要被访问的一切都应该经过一个访问的方法来运行。
在你的 API 中使用接口。若是您的输入点使用接口,设置类来测试将更容易。该接口容许开发人员进行重写的行为,好比契合模拟服务器或在内存中存储,来替代真实场景真实存储的开销。
最后,须要考虑到测试人员不须要构造多个层次深度的模拟。这个鼓励测试的原则应该被写入你的指引文档,并提供更稳定的测试框架。
有一些 class 很难被模拟,好比 final 类型的,它将建立它本身的依赖关系,而且是一个基于状态的单例。这在 Java 中是很常见的,虽然它一般是一个反模式。这使得它在隔离测试中很是具备挑战性。那么,咱们能作什么来解决它?其实只要有一些小修改,咱们可使这些难解的点变得更容易测试一些。
public class Tweeter { private Network network; private public Tweeter(Network network) { this.network = network; } public List<Tweet> getTweets() { return getNetwork.getTweets(); } }
与其把它做为一个单例,为何不把它作为一个实例?开发者能够本身去重用它,缓存,或者作其它的事情。删除在 class 中的 final Mockito 或其余框架就能够模拟它了,同时也能让 SDK 在初始化、构造时候管理它的依赖。依赖注入不只仅是一个框架,它仍是一个帮助组织代码的设计模式,使得代码更具备模块性和可测试性。
开发者常常是容易不耐烦的,因此有一些错误越尽早抛出就越好。若是你一直在使用 Gradle 你应该会明白个人意思,一些错误若是在 build 期间不能经过老是好于 build 完成以后5分钟才出现错误。你应该 把一些能够预期的异常抛出,以便于开发者可以尽快知道这些异常,好比在这种状况下,开发者试图设置一个 null 的 logger 到咱们的 builder 里,咱们得立刻抛出一个异常,这样他们就能够很快知道并解决他们的错误。
然而,你得保证你的 SDK 在生产环境中毫不会出错,让你的代码持续运行在他们的应用中,是你保证开发者们信心的惟一方法。他们的应用程序每每是他们生计的依赖,因此他们不会喜欢去赌着使用一个常常崩溃的库。因此当他们在调试过程当中出现问题的时候,你能够提供额外的信息,写清楚这个 Exception,但要隐藏在生产过程当中可能出现的问题,这样容许他们的应用程序的其他部分继续运行。你的 SDK 的出现问题可能对你来讲是一个大问题,但并非世界末日。
做为一个开发者,你使用 SDK,你的应用程序应该增长价值,而最糟糕的事情就是你引进了某物反而使得本来多价值下降了甚至彻底破坏用户体验。开发者们,包括我本身,不须要任何人来帮助咱们写一个糟糕的 App。
除了稳定,用户不太可能下载大的应用程序,这意味着安装包的大小是一个关键内容。下载软件产生的流量须要用户去付钱,因此即便你的应用是彻底免费的,用户也得为下载它付出流量。在许多新兴市场,由于下载速度太慢,因此不少用户不爱下载大型应用程序;在某些市场,用户主动选择更新的应用仅仅基于更新日志和添加新的特性是否值得,由于他们须要支付每千字节流量费用。
让咱们来讨论一些 Fabric 用于保持轻量的技术吧。有一些伟大的第三方库,能够真正给予贡献于你的应用程序,但当涉及到 size 规模和影响时,他们会他们显得不自由。例如,图像加载方面有各类不一样体积大小的图片加载库。其中 Fresco,是比其余任何一个第三方库都还大量级的一个库。然而,它对于旧设备有更好的支持,加载速度快并且内存友好,并支持渐进式JPEG。
做为一个 SDK 你应该努力平衡你的尺寸与功能。所以,要注意引入第三方库,以确保它们只知足所需的内容。
使用开源库有很大的优点,由于这些库每每通过不少的测试了,你们都使用得很好,而且他们有按期向他们提供更新的社区。这一般提供了一个更好的方案。在咱们的 Twitter SDK,咱们利用 Square 的 RetroFit 这个库做为一个依赖来简化咱们的 API,并且也能使咱们提供更好的可扩展性,这是值得的。
task reportSdkFootprint << { def sdkProject = project(':clients:SdkProject') def nonSdkProject = project(':clients:NonSdkProject') def footprint = getSizeDifferent( new File("$sdkProject.buildDir}.../Sdk.apk"), new File("${nonSdkProject.buildDir}.../onSdk.apk")) println footprint }
这里有一个很好的减小你的库大小的方法,就是若是你天天都称量本身的体重,你就可以减肥。咱们采起这种方法来关注和减少咱们的 SDK 大小。咱们积极地监控每一个 build,使咱们可以衡量的真实影响开发者的 APK 大小状况。没有硬性和快速的规则来解决大小增长,但一直关注它显然是有帮助的。
不少人应该都会遇到过 Dalvik 65K 的限制吧?不过大家可能不熟悉这个错误的具体缘由,对于方法的调用,能够经过在 Android 一个 DEX 文件,而它的引用的数量有限。问题的关键是,DEX 的工具,在编译时,试图把全部的方法引用到必定的空间中,但引用数目大于空间所能容纳的数目,致使分配失败。
如今你可使用 multidex 来解决这个问题,但这增长了在较旧的设备上应用程序的加载时间,它的初始化时间显著增长,它并不适合一些较新的设备用户之外的人。它甚至某些流行的三星设备上会致使出错,并致使应用程序崩溃。这些特定的设备有数以百万计的设备,在欧洲和亚洲都是很流行的,因此说这个解决方案真是无可奈何,能不触及尽可能不要触及。
但若是开发者遇到这个问题,在他们去使用 multidex 或相似的东西以前,他们常常决定审核他们的第三方库,选择是否能够减小应用程序方法数量的库。因此咱们的目标和建议是尽量地使你的库模块化和精益。
咱们用一个伟大的库被称为 dex-method-counts 在 Gradle 中,它包装了一些Android 构建工具对每一个第三方库的方法数量并给出了详细的数据分析。这让咱们快速洞察到咱们的库大小和咱们的依赖的大小。
咱们想要模块化开发人员须要的特定功能。咱们这样作是经过指定一个树的传递依赖关系。因此,咱们有两个例子展现如何初始化 Fabric:
容易整合的:
Fabric.with(this, new Twitter());
更多控制的:
Fabric.with(this, new TweetUi(), new TweetComposer());
第一个例子让你立刻开始,其次是一个拥有更多定制的版本,在这里你能够选择须要的特定组件,而后它们才生效。
让咱们的 SDK 尽量小,咱们着眼于模块化来设计咱们的架构。就像咱们讨论过的,在 Android 上二进制文件的大小和方法计数是很是重要的,因此这使咱们可以尽量高效。由于若是咱们利用 AAR(标准的经过 Maven 提供标准的库),咱们可使用分解依赖来知足咱们的需求。
在架构 Twitter SDK 堆栈,全部的一切都创建在 Fabric 上,它提供共享的 common 代码给全部咱们的 SDKs. 咱们创建了 TwitterCore 层,它提供了登陆和 API 客户端和其余一些核心功能。
而后,咱们才有基于特征的 SDKs,像咱们的 Tweets,咱们的 composer,咱们的短信登陆基础设施;这些特色均可能是你使用咱们 SDK 的理由。最后,咱们提供了一个接口,经过传递依赖关系将其包装起来。这容许应用程序开发人员根据他们须要的层次结构选择组件。
除了注意 SDK 自己的大小以外,咱们建议你在“压线”以前进行压缩。咱们能够明显看到,不一样格式之间有压缩大小差别,如 XML 和 JSON,一块儿使用jzip压缩格式,或二进制格式 Protobuf。有些人可能不熟悉 Protobuf,它是一个二进制的压缩格式,在服务器和客户端接口使用一致的预期协议,使得不须要将全部字段都进行传输。它能够很是高效地进行序列化和反序列化。
有效地发射是避免消耗过多电源功率的关键。Android 有三种典型的能量状态:全功率、低功耗、空闲状态或称待机状态。
在高功耗的时候进行统一的网络传输,比在空闲的时候断断续续进行屡次传输来得节约时间和节约电源。
对于在典型的3G设备上每个数据链接,网络接收模块会产生约20秒的活跃时间。这意味着若是你每分钟有三个网络链接,咱们将会保持这个网络接收模块不断地处于激活。经过批处理这三个链接,咱们能够在这里减小20秒的启用速率,以及至少40秒的待机或空闲时间。
不过对于这个方面,有一个例子,说的是天真统计分析 SDK,它们有时会不停地 ping 你的服务器,大约20秒一次,仅仅是为了告知服务器你的应用当前处于前台。这么作的后果就是会形成网络接收模块一直处于激活状态,而且把电源耗尽又没有传输什么实质上的数据内容。
保持 Fabric 的轻量部分方式是,咱们应该清楚地知道什么时候在主线程作的工做什么时候在后台线程工做,咱们建议在你的应用程序启动时候初始 Fabric,所以它的初始化将在主线程,咱们从咱们周围不少顾客那里听到你们都是比较关注启动时间的。
为了缓解这一点,咱们作了很是有限的一些同步工做,而后立马返回到开发者的 application,同时在后台继续作一些毕竟耗时的运行工做,以保持您的应用程序下次可以快速启动。
有些事情须要用同步作的一个例子:若是你使用在使用咱们的 Crashlytics ,你得当即使用它的 crash handler,由于一旦崩溃异常发生在异步初始化 crash handler 完成以前,就会捕捉不到这个异常。
一个 SDK 的开发者没有像应用开发者那么多的选择权。你不能选择你的设备,API level,或客户。你须要支持更大范围的设备,应用程序开发者并不局限在选择你的SDK,因此提供最大程度的灵活性是很重要的。
灵活性的一个体现是,可让开发者选择不一样的依赖管理器或者构件工具来引入或集成你的库。
咱们提供一些主要的开发工具插件支持,包括 Gradle, Maven, 和 Ant. 咱们还为一般的 IDE 提供 GUI 插件,以及一个帮助开发 Mac 和 iOS app 的应用。尽管咱们这是主要在讲 Android 方面开发 SDK 的内容,但我仍是忍不住想告诉你们一个好消息就是咱们最近完成了使人兴奋的 CocoaPods 支持,这将很是方便于 iOS 开发者使用咱们的 SDK.
灵活性的关键是了解您的 SDK 用户的需求。而后作出须要支持的最低系统版本的决定。咱们很但愿咱们的 SDK 可以尽量支持更多的系统设备。对于这一点,下降支持最低操做系统版本是颇有必要尽力去作的。
但另外一方面,兼容低版本也是要付出代价的。并无什么直接的法则可以告诉咱们如何才能在繁琐度和更好的兼容性上肯定平衡。支持旧的操做系统版本,一般意味着不利用更加好用的新接口,同时还要面对一些旧版本存在的问题。初次以后,你还要花费更多精力去测试你的代码之正确性。
通常来讲,SDK 会比应用软件在支持的最低系统版本的选择上,更近保守。例如,Crashlytics 提供了 Android 2.2 的支持,由于这是一个还基本实用的 Android 应用程序。而不是像一些应用程序,它们仅支持某一操做系统的版本,它们关注用户数量而去选择最佳系统版本。
另外一个重要的部分是可以检测出你正在运行的 Android 版本,因此你才能知道,哪些是能够调用的方法。一般,SDK 支持更老版本的 Android SDK。这对咱们来讲是很是重要的,由于咱们想提供最大限度的设备支持。
在 Android 系统上,运行时兼容性是灵活性的重要组成部分。Android 开发者都知道,一个应用程序的元数据(metadata)在 manifest 文件中声明,一个 apk 一个 manifest 文件。
此外,每个你包含进来的 aar 都会带一个 Manifest 文件,只是在 build 的过程当中,Android 工具会自动帮它们合并到一个 Manifest 里面。这意味着,若是一个 AAR 的 Manifest 会影响最终 Manifest 的生成,好比新增一个权限声明,这对于开发者来讲是很难注意到的。
权限是任何 Android 应用程序的关键。在 Crashlytics 咱们利用 wifi state permission 能够更好地管理上传崩溃日志。在 Android 的棉花糖系统上,这是属于正常水平的权限,这意味着它还须要在 manifest 文件中声明并赞成。然而,咱们没有在咱们的清单中声明这个权限,因此,若是使用了咱们 SDK 的开发者想要上传崩溃日志,就必须得在他的清单文件中声明上这条权限。若是开发者声明了那条权限,咱们在下面的代码中就可以知道拥有这个权限了。
protected boolean canCheckNetworkState(Context context) { String permission = Manifest.permission.ACCESS_NETWORK_STATE; int result = context.checkCallingOrSelfPermission(permission); return (result == PackageManager.PERMISSION_GRANTED); }
咱们只须要检查上下文对象中的权限,以判断是否已授予该权限。若是没有得到该权限也没办法了,咱们不能为了权限而可能影响到安全异常。一个 SDK 能够在运行时检查是否被容许使用某权限,若是可用再调用相应的 API.
不少时候咱们都须要回退机制,可是在这种状况下,若是咱们不知道WiFi或互联网的状态,咱们必须假定它老是链接尝试,让超时发生。
有不少 Android 设备的存在,同时有不少各式各样的特性或者功能可能其它机器设备并无,好比有的有 Kindle Fire 而有的没有,有的设备甚至尚未摄像头。
若是你正在构建一个基于相机的 SDK,你会在清单文件中声明使用相机。这就要求商店不要将应用程序展示给一个没有摄像头的设备。我鼓励你,把这个功能列为可选的,并容许你的库在运行时检测和修改它的行为。
在运行时检测硬件功能很是简单。您只须要查询 package manager 该特定功能是否存在便可。这样之后你的应用程序能够肯定哪些功能可使用了。以咱们的相机库示例,您可能还容许用户浏览照片和上传照片,只是他们不可以在没有相机的设备上拍照就是了。
有不少很好的第三方库能够节约你的开发时间以及帮助你开发更好的 SDK. 当咱们提供 SDK 给用户,可能须要检测用户是否有引入这些可选的第三方库,好比 RxJava 或者 Square 公司的 OkHttp,若是有,咱们就能够利用它们提供相应的支持。但你不必把这些包括到你的 SDK 当中,由于咱们前面说了,保持轻量是最好的。
private boolean hasOkHttpOnClasspath() { try { Class.forName("com.squareup.okhttp.OkHttpClient"); return true; } catch (ClassNotFoundException e) { } return false; }
在这个例子中,你能够经过 class path 来检测 OkHttp 是否存在。你能够告诉用户能够引入这个第三方库进行更好的支持,但没有必要主动把它们包括到你的 SDK 当中。若是有,你能够利用,若是没有,你也能够有你的另外选择。
除了运行时检测,咱们不能知足每一个开发者的需求。咱们的 Twitter SDK 提供了易于使用和流行的一些特性让用户去发现。咱们预先就会准备好,帮你简化签名操做,咱们使用持续的 token,签署了全部的输出请求。
可是,若是你想使用一个咱们 SDK 目前没有提供的 Twitter API 功能怎么办呢?若是是这样,你能够继承 Twitter API client,而且提供你的 retroift 接口,咱们能够接受它并帮你进行签名。
正如咱们所知道的,开发人员有不少工具,有不少的选择,他们须要在他们的代码中保持灵活性。这是很现实的。咱们提供可扩展的接口,使用 Fabric,使开发人员能够利用扩展他们想要的功能接口。
这里还有一个例子是日志记录。有许多不一样的库来使用,咱们提供了日志接口,以便它能够在 Fabric 开始前提供实现,而后咱们将尊重并使用开发人员的日志记录需求。
但若是开发者选择不配置他个性的日志内容,咱们提供一个健全的默认日志,即是标准的 Android Logger.
另外,对于 Java 以外的方面,咱们也支持让用户自定义他们想要的界面风格,好比开发者能够修改 color 的值,这些值将被应用于 TwitterUI SDK.
这里有一个须要注意的点,由于 dex 合并点时候不支持在 name 中使用空格符号,因此不要使用相似”background color”这样的 name.
制定灵活代码的一部分,就是容许开发人员选择监听一些事件发生并得到通知。在咱们的 builder,咱们能够设置同步或异步任务结束的时候进行回调,也能够设置若是出错了能够获得回调通知。它容许开发者根据本身的状态和所获取的信息进行定制决策。
当你完成了你的 SDK 开发的时候,并不表明着你的 SDK 真正完成了,你还须要有不少开发以外的内容要作,要创建开发者交流的社区,还要有 Apple 文档、Java 文档,以及 README 文档,另外还有很重要的就是要有使用你的库的示例教程。
添加注释 给你的全部 public 的内容,以及顺带说明一些使用案例。
发布简明的 示例(sample)代码,让使用的人能够遵循着你的代码进行初次尝试。但切记不要把你的示例代码写得太复杂或者在可有可无的内容上纠结太多,否则会致使用户花费更多时间去学习你的示例代码,而且使用到他们的项目中时候,产生了不少疑问或者 bug.
记得思考一些你想废弃的旧版本和方法。大家有多少人没有回头看旧的代码?除了去重写重构这些代码和方法,你能够须要对于旧的不要的代码进行 废弃 注解提醒。
尊敬版本更新日志,这是一个你和开发者经过你的 SDK 沟通的方式,你能够去知道他们须要什么,而他们从大家的 SDK 更新日志中得到他们是否须要你这个新版本的信息,若是值得,开发者们就会决定更新到目标的这一个版本。
最后,使用开发者预期的、经常使用的 交流社区或称交流方式。若是这是一个开源库,应该是使用 GitHub 的 Issues,关注 Stack Overflow,Freenode.
虽然咱们认为全部的这些观点都很是重要,但它们确实是伟大的库/SDK建设中的冰山一角。咱们但愿你喜欢咱们做为内部开发人员在 Fabric SDK 开发中的思考,那你以为有用的建议和特性,或许就将出如今你如今手头或者未来要开发的 SDK 身上。谢谢!
Ty is an Android tech lead at Twitter, focusing on developer tools on the Fabric team. He architected the Fabric Platform and Twitter SDKs for Android, lead the open source initiatives on Digits and the Twitter SDK, and contributes to the larger Twitter Architecture Group. He has been specializing in Android for 7 years and is a member of the GDE (Google Developer Expert) program. He speaks regularly at international conferences on Android development and organizes the SF Android Meetup group, the Droidcon SF conference, and has taught Android development at Missionbit. Prior to Twitter, Ty worked on the Evernote Android App where he lead the SDK and partner integrations work, lead a messaging platform for Sprint under the company One Louder Apps, and built Zagat for Android prior to its Google acquisition.