百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

两年的Android SDK开发经验,分享给大家(android sdk platform tools)

toyiye 2024-04-04 11:35 19 浏览 0 评论

在公司做了两年多的 SDK 开发,结合自己的所知所学,分享一些 SDK 开发的经验。

1SDK 是什么

相信做 Android 开发的朋友,一定使用过第三方的 SDK,比如推送 SDK、分享 SDK 等。SDK 的全称是 Software Development Kit,翻译为“软件开发工具包”。SDK 通常是为辅助开发某类软件而编写的特定软件包、框架集合等。


SDK 可以分为系统 SDK 和应用 SDK。所谓系统 SDK 是为使用特定的软件框架、硬件平台等所开发的工具集合。而应用 SDK 则是基于系统 SDK 开发的独立于具体业务、拥有特定功能的工具集合。


SDK 的使用者主要是 B 端客户,最终交付产品是代码、示例和文档,客户接入 SDK 也是和 SDK 提供方交流的过程,对外沟通的成本比对内更高,遇到的问题也会更多。所以 SDK 开发对开发者的要求比对应用开发更高。能开发好 SDK 一定能开发好应用,但能开发好应用,未必能开发好 SDK。


2SDK 实现目标

SDK 的实现目标,概括来说:简洁、稳定、高效。


简洁

对于用户而言,一款好的产品应该是简洁易用的,不该让他们花费太长的时间学习。SDK 也当如此,它不该出现复杂繁琐的对接工作,使用者通过阅读代码和文档,花费很少的时间就能做好 SDK 的对接。


比如当开发者需要使用 SDK 的服务时,只需要在代码中新增一行即可。在项目中初始化 SDK 只要一行代码,开发者不用关心 GLContext,内部已做好处理,也不用关心同步或异步问题。



public class FURenderer {  
  // 定义     
  public static void setup(Context context) {  
      //...  
  }
}
// 一行代码调用
FURenderer.setup(context);

稳定


站在 SDK 使用者角度来看,我们期望第三方 SDK 的服务是稳定高效的,体现在提供稳定可靠的服务,同时运行时性能要高效。这就要求我们在设计实现 SDK 时要尽可能做到以下几点:


  • 对外提供稳定的 API。SDK 的 API 一旦确定,除非特殊情况不可更改,提供方变更 API 的成本非常大。
  • 对外提供稳定的业务。在提供了稳定的 API 后,必须要有稳定的业务作为支撑。
  • 运行时的稳定。确保 SDK 自身稳定运行,不能出现因为接入了 SDK 而导致宿主应用不稳定的情况。
  • 版本稳定更新。SDK 版本迭代非常缓慢,要尽可能对使用者屏蔽迭代过程,避免带来不必要的适配成本。

高效


无论是普通的应用开发还是 SDK 开发,都应该考虑到性能问题,SDK 设计者要着重考虑以下问题:


  • 更少的内存占用。一般 SDK 和 App 运行在同一进程,此时 SDK 要管理好自己占用的内存,合理分配,注意释放。
  • 更少的内存抖动。在占用更少内存的前提下,SDK 设计者必须减少频繁 GC 造成的内存抖动问题。
  • 更少的电量消耗。低电量消耗和高性能表现之间很难做到权衡,可以从 CPU 计算量、屏幕刷新帧率等角度考量。

3SDK 架构设计


SDK 的架构实现决定了后续的维护难度,所以最好能够结合实际业务确定合适的方案。以项目中的模块化开发为例,讲讲架构设计的原则。


遵循面向对象开发的几大原则,目的是达到三个目标:可维护性、可重用性和可扩展性。具体来讲:


  • 根据单一职责原则,将系统拆分为多个小模块,每个模块保持相对独立,降低实现类的复杂度。
  • 根据接口隔离原则,为每个模块定义契约接口,接口的粒度要小,功能要细,越细小越易维护。
  • 模块之间通过协议或接口通信,避免直接相互依赖,以降低耦合,互相了解最少,体现了迪米特法则。
  • 根据开闭原则,定义各个模块的公共行为,通过模版方法设计模式提供骨架实现,易于功能扩展。
  • 根据组合优于继承的原则,当多个模块功能叠加时,使用类的组合保证设计的灵活性。


比如项目第三方 demo 的功能模块借鉴了 Java 集合框架的架构,分为契约接口、抽象类和具体实现三部分。


  • 首先定义 IEffectModule 作为特效的契约接口,包括创建、设置参数、销毁等各个功能模块的公共操作。
  • AbstractEffectModule 作为 IEffectModule 的骨架实现,实现了共同使用的方法,定义了公共的成员变量。
  • 定义美颜 IFaceBeautyModule 接口,其继承 IEffectModule 接口,包括额外的设置参数操作,FaceBeautyModule 作为其实现类,同时继承 AbstractEffectModule,复用基类的代码。
  • 美妆美体模块类似,先定义契约接口,然后定义具体实现,接口间相互隔离,接口内高度内聚。
  • FURenderer 实现 IFURenderer 渲染接口和 IModuleManager 模块管理接口,组合各个功能模块。



4SDK 设计规范


API 设计在任何开发中都非常重要,许多时候软件的质量好坏体现在 API 的设计上。


在普通的应用开发中,API 只会在开发人员间流通,不会暴露给非本应用开发的其他人员。但是 SDK 作为一种服务,需要向开发者暴露一部分 API,这样才能使用 SDK 的服务。


下面列出一些应该重点关注的原则。


1. 方法名表明其用途


好的方法名最直观表明它的功能,名字是自解释的,不需要额外的文档,这样做会减少不必要的沟通成本。对于开发者而言,还有什么比直接读代码更直观呢?《重构》一书中讲到,要像给自己孩子起名一样给每个变量命名,这个要求不算过分吧。


2. 参数的合法性检验


如果程序运行时出现异常,会破坏使用者的体验,影响非常不好。我们采用“防御式编程”的思想,能够避免非法输入对系统的破坏性。


当合法性校验不通过时,针对方法权限不同分别对应不同不同的处理策略:


  • 对于公开方法显式检查抛出异常,并使用 @throw 来说明抛出异常的原因
  • 对于私有方法通过断言的方式来检查参数的合法。
  • 检查构造方法的参数的合法性,以使对象处在统一状态。


需要注意的是,如果检查的代价太大,那就需要综合考量。


3. 方法只实现单一功能


一个方法应该具有单一的功能,尽可能做更少、更专的事情,这也是单一职责原则的体现。“阿里巴巴代码规约”规定一个方法 最好不要超过 80 行,对庞大的方法要拆分成更小的。


另外注意,宁可提供小而美的方法也不要提供大而全的方法,大而全的方法往往经常发生变动,产生风险的可能性更高。因此不如提供更小的方法以便组合使用,小而美的方法更易做到代码复用。


4. 访问权限控制


包括类方法的权限和变量的权限,能声明私有的不要公开,外部知道得越少越好。能声明静态的方法就用静态,静态方法天然线程安全,体现继承关系的用 protected 修饰,确保公开的方法和变量是安全可靠的。


5. 避免过长参数


过长的参数会造成记忆上困难,还有调用传参容易出错,应当尽力避免。在无法避免过长参数的情况下,考虑其他的方法进行解决:


  • 通过使用 Builder 模式来实现
  • 通过将多个参数封装成类对象


例如,项目里有个方法,参数非常多。



public class VideoFrame {
  private int width;  
  private int height;  
  private byte[] data;  
  private byte[] readback;  
  private int readbackWidth;  
  private int readbackHeight;  
  private int pixelFormat;  
  // ...
}
int onDrawFrameSingleInput(VideoFrame videoFrame);

重构后,把参数封装成对象,调用方法只用构造一个对象传入,避免大量参数带来不好的体验感。



public class VideoFrame {
  private int width;  
  private int height;  
  private byte[] data;  
  private byte[] readback;  
  private int readbackWidth;  
  private int readbackHeight;  
  private int pixelFormat;  
  // ...
}
int onDrawFrameSingleInput(VideoFrame videoFrame);

6. 慎用方法重载


滥用重载容易让开发者感到疑惑,在需要重载方法的时候,可以使用不同方法名来代替。对于构造函数,可以通过静态工厂来代替重载。


Java 中提供的 ObjectOutputStream 类就是个很好的示范:它的 write 对于每个基本类型都有一个变形,比如写出字符、写出 boolean 等操作。设计者并没有使用重载将其设计成 write(Long l)、write(Boolean b),而是将其设计为 writeLong(l)、writeBoolean(b)。


例如,项目对外的处理方法全部是重载,只能根据参数区分,迷惑性非常大。修改为不同的方法名后,看到名字就知道要调用的方法,清楚了不少。


// 重构前
int onDrawFrame(byte[] img, int tex, int w, int h);
int onDrawFrame(byte[] img, int w, int h);
// 重构后
int onDrawFrameDualInput(byte[] img, int tex, int w, int h);
int onDrawFrameSingleInput(byte[] img, int w, int h, int format);


7. 避免方法直接返回 null


对于需要返回数组或集合的方法,不要返回null。比如我们去买糕点店买面包,面包没了是一种正常状态,就不应该返回 null,而是返回长度为 0 的数组或集合。Java 提供了 Collections.emptyXXX() 表示空集合。


8. 避免引入第三方库


GitHub有许多开源的第三方库,比如网络请求 OkHttp、图片加载 Glide 等,但在 SDK 开发中,遵循的基本的原则是:


  • 最小可用性原则,即用最少的代码,如无必要勿增实体。
  • 最少依赖性原则,即用最低限度的外部依赖,如无必要勿增依赖。


引入第三方库可能带来下面几个问题:


  • 宿主应用的第三方库和 SDK 依赖的版本不一致,容易引起冲突,增加对接的成本。
  • 开源库不断更新,SDK 也要及时更新,增加额外的维护工作量。
  • 由于引入开源库,出现问题难以排查。


9. 保证兼容性


SDK 是不断迭代的,每次发布都会有新功能和 bug 修复。对于使用者来说,升级版本不该有太大的改动,一般直接替换库文件或者修改远程依赖库的版本号就够了。避免直接对公开接口的重命名,如果旧接口废弃,要通过 @Deprecated 关键字标明,并给出替代方案和废弃的时间。


10. 减少入侵性


要保证较少的代码侵入主要在对外提供服务时,充分考虑开发者的使用场景来设计优良的 API。一套优良的 API 在定义时要满足绝大数开发者预期的方式——语义上要求通俗易懂,使用上要求简单可靠。具体的表现是,在正常情况下能够稳定可靠地运行,在异常情况下及时地反馈错误信息。


比如使用 Gradle 下载依赖库,AAR 包中有不必要的 bundle 资源,我们提供了打包 apk 时的构建配置,自由选择要打包的 bundle,减少了对宿主应用的侵入性。

applicationVariants.all { variant ->

variant.mergeAssetsProvider.configure {

doLast {

delete(fileTree(dir: outputDir,

// 删除不必要的 bundle 文件

includes: ['model/ai_face_processor_lite.bundle',

'model/ai_gesture.bundle',

'graphics/controller.bundle',

'graphics/tongue.bundle']))

}

}

}


5SDK 交付


封装包


Android 平台通常使用 jar 和 aar 发布 SDK,区别是 jar 只包含代码,aar 可以包含代码、资源和动态库。一般而言 aar 是最合适的交付方式,把它上传到 maven 服务器,使用者就可以一行代码集成。对于需要灵活定制的客户,我们也会提供 SDK 的源码,弊端就是升级困难,要改动很多的代码。


对于代码混淆,公开接口和 native 使用的接口不要混,内部的实现细节可以混淆,以减少 SDK 包的大小。


接入文档


接入文档用来告诉 SDK 使用者,如何使用 SDK、详细使用步骤和可能发生的问题。文档内容包括:更新记录、基本信息、API 说明、集成步骤、FAQ等。好文档的标准就是清晰明了,通俗易懂。一个完全不懂 SDK 的开发者看着文档就能对接,对于经常遇到的问题要逐条列出,专业名词要有对应的解释。


Demo 示例


集成 Demo 通常是一个简单的 App,用来展示如何快速地接入 SDK。Demo 的源码托管到 GitHub,方便使用者参考,其版本变更策略和 SDK 版本的变化保持一致。尽管是个 Demo,它的开发原则也要与 SDK 一致,确保高质量的交付。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码