Android SDK开发规范整理

5,190 阅读13分钟

关于SDK的解释

SDK是Software Development Kit的缩写,译为“软件开发工具包”,通常是为辅助开发某类软件而编写的特定软件包,框架集合等,SDK一般包含相关文档,范例和工具。

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

比如在进行Android 应用开发时,我们使用Google提供的系统SDK(Android SDK),而我们经常使用的友盟SDK、极光SDK则是基于系统SDK开发的.

背景

因本人在4399做了三年sdk开发,有一个感受:sdk开发对开发人员的要求比对应用开发更高,能开发好sdk一定能开发好应用,但能开发好应用,未必能开发好sdk。刚好目前公司有将现有项目抽离成独立sdk模块的业务需求,在这篇文章大致整理了一些规范与规则,希望对正在或者即将开发sdk的小伙伴有一点帮助。文章主要描述了如下规范:文档规范、开发规范、基本原则、代码与设计规范、项目管理规范,以及制定公司内部的sdk开发规则,主要有版本号规则、命名规则、打包原则。后续还会补充。有不足之处多指出,感激不尽。

一、文档规范

更新日志文档

  • 描述清楚相对上个版本的所有变更(优化项酌情考虑是否添加),如:
v2.23.2
修复xxx崩溃的偶发性bug
调整xxx列表页面ui
增加xxx的接口,详情看接口文档
  • 如果api变动较大,需提供变更对照文档

接口文档

一般文档结构如下:

  • 产品简介

    • 功能说明
    • 适配版本
    • 开发包内容
  • 接入准备

    • 相关key的申请等
  • 相关配置

    • manifest配置
    • 混淆配置
    • 依赖配置
    • ...
  • 接口调用

  • 接口详情

  • 示例代码

  • FAQ

    • 依赖冲突
    • ...

文档原则

  • 内容准确完整,一个优秀的SDK开发人员在编写文档前会做充分的接口场景调用验证,已保证内容的准确和完整。
  • 易读易用,SDK开发人员作为文档的第一个读者和使用者,在使用文档过程中应该有意识的降低自己的姿态,时常假想一个很low的开发者在阅读自己文档时候的样子,通过积极阅读和不断改进确保一个不是很擅长编程的开发者也能使用我们的SDK。
  • 精简文档,一个优秀的SDK开发人员会通过减少重复、避免冗余、整洁代码等措施来精简文档的内容,同时这也减少了文档的维护成本。

二、开发规范

开发人员必备精神

  • 为自己、组员、开发者、用户负责!

原生包

  • 一般情况需要提供如下
  1. androidstudio平台相关包:demo工程+aar仓库
  2. eclipse平台相关包:demo工程+依赖工程(包含SDK的jar包+res包+manifest文件+SDK所依赖的support包、第三方so、jar等)
  3. 可直接安装运行的demo.apk包

基本原则

高质量的示例代码

  1. 代码的准确性
    示例代码上线后开发者可以通过拷贝就能运行起来,维护一份准确的示例代码是一劳永逸的事情,可避免很多不必要的打扰。

  2. 代码的规范性
    一份好的示例代码是符合优秀编程规范的,规范的命名、模块化、小函数、异常检测等等。

  3. 优秀的示例代码是对文档的补充
    示例代码会在进行接口调用的过程中通过清晰的代码结构和必要的说明来让开发者看清楚调用过程,并且对一些代码注意事项进行必要说明。

高质量的 SDK

  1. 优秀的设计
    SDK 必须要遵从原生、简短、执行迅速、代码干净、易读、可测试的原则。原生指的是 SDK 所依赖的库必须是自己开发或第三方开源的库;简短指的是 SDK所包含的接口数量越少越好,接口参数越少越好,调用流程越少越好。
  2. 兼容旧版本
    SDK 版本一旦发布要保证可以兼容旧版本并且要能被将来的版本兼容。
  3. 注重性能
    良好的性能带来良好的用户体验,性能优化也是Android 开发者基本素质。
  • 建议:可使用leakcanary检查内存泄漏问题,使用android profiler分析代码可能存在的性能问题

三、代码与设计规范

分包(分层)规范

  1. 若 SDK 的功能较多,且各个功能模块之间业务相关性不大,建议使用组件化的方式分包,即按模块划分包,一般项目包结构如下:
  • api 包:暴露给开发者的类都放在这(该包不被混淆),这里可能包含接口类,接口返回值类,对外监听器,全局配置类等。对外的接口一旦对外提供,就尽量不要修改!
  • 各个功能模块包:每个功能模块包包含一个 ApiXXX 开头的类,仅该类可与其他包通信,提供该模块的 api 给统一的接口类调用,其他类必须为包可见,该包内可继续细分包,由实际情况决定采用具体MVX模式
  • independent 包:该sdk通用的类(抽象类),如通用弹窗,导航栏的封装。
  • support 包:一般放通用的工具类,或者网络请求封装,错误日志收集等等,总之,就是可以被其他sdk复用的包,项目最底层,不能对任何其他包有依赖。
  1. 若 SDK 包含的功能较单一,可能仅包含:api 包、support包及功能模块包,如上传 SDK; 更简单的,连support包都没有,如直接对已有的微吼SDK的封装

3. 层级关系,高 —> 低:api包 > 各个能模块包 > independent包 > support包

4. 低层模块不依赖高层模块(除了全局配置类)

注意:以上的分包方式只是一般情况,实际开发结合具体业务给出更良好设计,不应墨守成规。


代码规范

遵守 Java 的基本规范,比如:

  • 设计模式,如单一职责(一个类、方法不要做不同的事)
  • 高内聚、低耦合
  • 一个类行数不超过500行,一个方法行数不超过15-20行
  • 全局变量使用 m 开头的驼峰命名;
  • 常量使用下划线分隔的大写字母;
  • 类、方法、变量的范围尽可能缩小(能用 private 不用 public,能用局部变量就不用全局变量)
  • SDK 代码中对资源的引用方式,不能用传统 R.xx.xxx,需要使用反射获取(考虑到eclipse接入的开发者)
  • 能用轻量级的fragment尽量不用重量级的activity

资源规范

  • 资源命名使用下划线分隔的小写字母
  • 所有资源需要有前缀区分,避免引入应用后出现资源名冲突
  • 应用图标放在 mipmap 下,普通资源放在 drawable
  • 尽量用.9.png
  • 尽量使用内置的资源,如@android:color/white,
  • values资源存放路径:
    • 百分数存在文件:values/fractions.xml
    • 整数值存在文件:values/integers.xml
    • 布尔值存在文件:values/bools.xml
    • id 存在 values/ids.xml (同一功能的控件出现在不同的地方,应使用同一id 引用它)

注意:无论是代码还是资源文件,都应该有良好的命名 ,尤其是提供给开发者的api方法!良好的命名体现意图 > 功能 > 类型,过长要缩写,避免使用意图不明确的命名如 initView、setData、saveData、updateView

每个类文件和资源做文件需检查并去掉编译器的警告提示,这有助于避免命名拼写错误,提升代码的效率,养成良好的代码习惯,这并不是多余的操作

建议:可使用lint检查代码存在的问题,如硬编码、未使用的资源、可优化的逻辑关系、可能造成性能问题的代码等


第三方库依赖原则

关于sdk项目中使用到第三方库的依赖,需要针对开发者的平台考虑这两种情况

  • 只考虑AndroidStudio平台接入

    我们sdk工程依赖aar仓库没有限制,直接在gradle文件配置dependencies xxx,但是这种情况下,如果我们的sdk工程打包要注意,需要将aar包打包在仓库(本地或私服或远程,一般是maven仓库),让开发者使用远程依赖的方式,而不能直接提供aar包!因为如果这样,一旦我们的包含有远程依赖无法传递依赖,开发者主项目还需要自己配置去依赖我那些远程库,增加接入成本,也违背了最少暴露的原则。

    为什么直接提供aar包对方需要自己配置aar包中的远程依赖,而提供aar仓库,对方只需依赖aar仓库无需关心aar中所依赖的远程仓库?因为打包到aar仓库中已经包含了相关依赖的配置,具体信息可查看仓库中pom相关文件。

  • 需考虑AndroidStudio和eclipse/adt这两种平台下的接入

    这种需要同时提供aar和eclipse/adt下的依赖包,

    1. 要求sdk工程不能使用远程依赖aar仓库,因为eclipse没有aar这个概念,他只认jar和res,第三方依赖使用源码导入或者使用本地libs依赖的方式,
    2. 并且第三方库相关的类文件必须与sdk源码分开打包,这样当开发者依赖的其他第三方库冲突时,开发者可直接删除我们依赖的库。
    3. 既然不允许依赖远程仓库,那也就没必要让开发者以远程依赖的方式依赖我们的sdk,所以直接提供aar包就可以。实际开发中,建议,无论需不需要考虑eclipse/adt的开发者,都以这种方式提供包

注意:直接通过远程依赖第三方库的方式将aar包自动生成eclipse/adt 所需的jar和res,然而这样生成的jar与自己通过类文件打包的jar可能会有一些区别,如果是一些常用的库,如网络请求,只有jar包,是可以直接使用,大部分第三方库不可直接使用 ,建议直接找源码,然后拷贝到本地项目,分别打包。

注释规范

  • 原则:
  1. 注释形式统一
    在整个应用程序中,使用具有一致的标点和结构的样式来构造注释。如果在其它项目中发现它们的注释规范与这份文档不同,按照这份规范写代码,不要试图在既成的规范系统中引入新的规范。
  2. 注释内容准确简洁
    内容要简单、明了、含义准确,防止注释的多义性,错误的注释不但无益反而有害。
  • 注释格式:
  1. 单行(single-line)注释:“//……”
  2. 块(block)注释:“/……/”
  3. 文档注释:“/**……*/”
  4. javadoc 注释标签语法
    @author   对类的说明 标明开发该类模块的作者
     
     @version   对类的说明 标明该类模块的版本
     
     @see     对类、属性、方法的说明 参考转向,也就是相关主题
     
     @param    对方法的说明 对方法中某参数的说明
     
     @return   对方法的说明 对方法返回值的说明
     
     @exception  对方法的说明 对方法可能抛出的异常进行说明
    

编写优美的、专业的注释可参考www.jianshu.com/p/54e896473…

四 、项目管理规范

某个大牛曾经说过 “SDK 工程不能包含任何不需要的东西,因为他对包大小的要求更严格”

Git 提交规范

# <type>(<scope>): <subject>
# <类型>: (影响的范围,比如数据层、控制层、视图层等等) <主题> (header最多72个字)

# 解释为什么要做这些改动
# |<----  请限制每行最多72个字   ---->|

# 提供产品需求、Bug管理等平台相关信息
# 例如: EXE-14394

# --- 提交结束 ---
# 类型值包含
#    feat 新功能(feature)
#    fix 修补bug
#    docs 文档(documentation)
#    style (格式化, 缺失分号等; 不包括生产代码变动)
#    refactor (重构代码)
#    test (添加缺失的测试, 重构测试, 不包括生产代码变动)
#    chore (更新grunt任务等; 不包括生产代码变动)
#    perf (性能优化)
#    ci (持续集成相关任务)
#    build (构建相关)
#    revert (回滚操作)
# --------------------
# 注意
#    主题和内容以一个空行分隔
#    主题限制为最大50个字
#    主题行大写
#    主题行结束不用标点
#    主题行使用祈使名
#    内容每行72个字
#    内容用于解释为什么和是什么,而不是怎么做
#    内容多行时以'-'分隔
# --------------------

五、公司内部 SDK 开发规则

1. 版本号规则

使用三位版本号,每位版本号最高三位数字:(1~999).(0~999)
如:1.0.12

  • 版本号递增原则:
    • 第三位:bug修复,极小的变更
    • 第二位:一般的功能迭代
    • 第一位:项目重构,功能变更较大,需团队共同确定

2. 命名规则

遵循以下命名规则可避免不同sdk命名冲突

  • 所有资源命名前缀:mEft_xxx_
  • 工程命名:eft-sdk-xxx-android(如果是ios:eft-sdk-xxx-ios)
  • demo 项目命名:demo,包名:cn.eft.sdk.xxx.demo
  • sdk 项目命名:mEftXxxSDK,包名:cn.etf.sdk.xxx

3. 打包原则

  • 打包必须符合以下原则和规范
  1. 对外提供的包不能包含任何编译生成的文件和目录,如安卓的build目录
  2. 使用脚本一键打包,提升打包效率,降低手动打包带来的出错率
  3. 打包脚本需与项目其他脚本分离,尽量职责单一
  4. 包中尽量提供示例工程, 示例工程必须让开发者以最低的成本运行起来
  5. 打包指令根据实际情况调整,打完的包需亲测有效才可对外发布

为了可读性,gradle脚本同样要模块化,不要将所有gradle脚本放在同一个文件中
sdk module项目对应的打包脚本均放在对应module的publish文件夹下,打完的包放在对应module的build/output下,如果是一次性打整合sdk与demo的所有包,放在根目录的build/output下。

结语

关于公司项目和一些打包指令这里就省略,毕竟涉及到公司内部项目不方便公开,基本上遵循以上原则,便可使整个sdk开发流程迭代等更规范。还有很多不足之处或需要补充的,请在评论指出。