原文: SDK开发过程的一些问题总结 过去的一年多时间里都在做SDK, 这一年从Web开发转到Android开发也算是成功的转型了, 被坑了很多次, 也坑了很多人很多次, 在各种互坑的过程中学到了些东西. 写在这里也算是对过去一年坑别人的一次反省(阿弥陀佛~~). SDK有一个很大的特点, 它的用户是程序猿(这是我大学时代梦寐以求的呢); 而App的用户则通常是普通用户. 我们知道, 有的程序猿很懒, 有的程序猿又喜欢追求完美, 简单来说吧, 就是程序猿太tm难伺候了. 这就直接导致了SDK开发的一些特殊性. 下面简单谈谈过去在项目中遇到的觉得应该注意的一些点, 大部分可能不只是SDK开发才会遇到的, 即使是App开发, 如果你写出来的接口别人用起来比较爽, 那也是不错的事呢. 我们的SDK是给游戏用的, 所以后面我会习惯性称SDK的使用者为游戏. 另外说明一点是我们的SDK封装了很多其他外部SDK. 错误码/错误信息 设计我们当初的错误码设计可以简单分为三个阶段:
上面两个阶段是已经完成了, 也就是说我们的现状是第二个阶段, 其实这种方式的问题现在已经开始逐渐显现出来, 目前出现的主要问题在于游戏收到这些错误码以后自己还是需要归类, 例如某些错误码游戏需要重新登录, 有的错误码游戏可以尝试重试, 有的错误码则游戏可以忽略. 也就是说从我们的错误码到游戏逻辑之间是需要一次转换的, 而这次转换交给了游戏, 不同的游戏的这个转换关系不同最终导致了一些使用上的混乱. 说到这里就到了我目前的一个想法了, 错误码按业务逻辑来区分, 直接通过错误码告诉游戏下一步应该做什么, 而不是上一步发生了什么, 了解上一步发生什么问题的目的就是要知道下一步需要做什么. 但是如果错误码这样简化以后必然还会出问题, 如果游戏想要处理得很精细怎么办? 我对这个问题给出的答案是二级错误码, 第一级错误码是简化错误码, 偏向业务逻辑, 供很懒的程序猿用, 他不用理解中间任何事情, 第二级错误码是详细错误码, 偏内部实现, 供最求完美的程序猿使用, 两者互不干扰. 除了上述错误码的问题外, 伴随着错误码的还有错误信息, 错误信息要在前期定位好的他们的作用, 是用于开发内部定位问题OR提示用户, 如果是用于提示用户, 最好是做成可配置的, 因为可能涉及到国际化之类的问题, 另外如果是用于开发内部定位问题, 则应该注意里面应该包含足够的错误信息. 例如之前给一个同事聊天了解到他们的错误信息设计方法是, 首先错误码分段: 10000, 20000, 30000 …, 然后某个模块如果发生错误, 则将错误码返回到下游模块, 下游模块将上游的错误码加到错误信息中, 并将错误码转换成本模块错误码, 继续返回给调用者, 依次类推, 这样可以保证任何一层调用者, 只要收到了错误信息, 可以迅速看出来是哪个模块出现了问题. 上面说的略多, 总结一下: 同步接口设计在设计一个对外接口之前肯定要考虑的几件事:
关于接口名称, 我们之前将自己的接口全都带了统一的前缀, 现在想起来, 这种做饭真是傻的可爱~, 不过之前接口名称上也有做的还不错的, 我们每个接口名称都很长, 基本上说了这个接口在干什么, 这点对于SDK 接口来说真的太重要的了, 一个好得接口名称可以让你少些好几页的文档. 接口输入主要指接口参数, 为什么说主要呢, 因为我个人觉得如果某个接口用到了全局的某些值, 应该将其看作本接口的输入, 这些全局的值是会影响接口输出的. 而且明确的意识到自己写的接口里面用到那些外部全局的东西绝对不是一件坏事. 下面简单说说我自己在参数设计上遇到的几个坑: 第二个参数相关的是我在一个接口里面放了6个String的参数, 结果自己代码里面参数顺序弄乱了, 因此也导致了多个B(内)U(牛)G(满)单(面). 出现这种问题很重要的一个愿意在于, 在接口参数很多的情况下, 我还是按Eclipse的格式去格式化代码了, 格式化出来的样子差不多如下:
随手敲的代码~~, 如果是上面一段, 让我去看参数顺序我肯定会晕的, 特别是调用变多的时候. 所以这样些是很容易出错的, 我自己经历了上面几个bug, 目前想到的一个最简单的办法(不根治此问题)是超过3个参数每个参数折行, 不论是方法申明还是调用, 代码如下:
这样看起来可能没那么漂亮, 甚至有的人可能会觉得我在凑代码行, 但是这样确实能让我更清楚的看出来参数顺序. 但是... 其实这样也还是很容易出错好吧. 有别的方法吗? 目前我想到仍为在项目中使用的方法就是, 用一个简单对象封装参数, 例如上面例子:
经过这样一次封装, 可以让给接口传值变为为参数对象的属性赋值, 这样赋值出错的可能应该是远小于直接传参的. 总结一下上面说的两个参数相关的问题:
最后关于输出呢, 函数的输出可以是返回值或者回调, 对于同步接口, 通常是直接将输出放入返回值中. 如果是异步接口则可能同时具有返回值和回调. 异步接口的返回值将在下面继续探讨. 异步接口设计在设计异步接口之前一定要想清楚确实需要异步接口吗? 因为如果你提供一个阻塞的同步接口, 使用者可以自己开一个线程自己将其变为异步接口使用, 但是如果你提供的异步接口, 使用者像将其改为同步就比较麻烦, 回调太多也会导致代码较为混乱, 所以我认为非必要不提供异步接口, 或者说为了方便, 同一个接口提供异步和同步两种. 异步接口返回值设计通常情况下异步接口的返回值会用来标识参数是否合法, 如果参数不合法直接返回false并且不会有回调. 我觉得这里最重要的一点是: 规则统一, 一定要在实现前确定规则, 例如参数错误是给错误回调 OR 直接返回false不回调, 出错如何回调, 错误码如何定义(见前面错误码部分), 成功如何回调, 并且保证如果参数正确一定要有回调(如果此调用有外部依赖最好是在SDK内部做定时器处理超时, 超时后给调用者回调), 因为很有可能游戏调用以后不做超时处理等SDK回调, 一旦出现SDK没有回调的情况, 则将造成玩家无限等待. 异步接口回调设计在回调设计这一块我们也是踩尽了坑.... 回调第一坑: 没有请求标识 回调第二坑: 使用公用回调对象 上面两个问题继续在困扰着我们, 目前想来有几个方式优化上面两个问题:
环境规划和管理除了代码层面的一些问题, 各种环境的管理也是必不可少的, 例如我们的环境变化过程如下(下面会细说如此变化的原因):
开始阶段, 项目最开始只有一个环境, 后来遇到游戏自己测试数据和正式的数据会相互影响, 于是增加联调环境给游戏联调使用, 相当于游戏的测试环境. 最后各种环境使用的流程如下:
具体需要那些环境需要的根据各自的业务场景确定, 首先必须要确定有哪几类人会使用这些环境, 做好归类, 然后再梳理清楚他们之前是否不能相互影响, 那些是互斥的, 那些可以公用环境, 这样来最终确定需要那些环境. 在项目前期做好这些规划能省很多事, 我们在整个环境变更的过程中也吃了不少的亏. 版本开发/发布策略管理关于版本管理, 我想说的就只有一句: 一定要灰度… 最开始我们每次出版本都是发给全部游戏, 然后… 一(必)旦(然)有BUG, 无穷无尽的咨询, 无穷无尽的吐槽, 压力非常大. 这样的压力也导致了经常在赶紧急版本, 这就必然会打乱版本的节奏, 最终的结果只有一个, 开发加班到吐血. 后来我们讨论每次发布版本灰度以后, 其实吧, 就是每次先找少量游戏让我们坑一下, 坑完我们填一遍坑, 然后我们在全量, 这样发出去的版本一半不会出现很大的BUG或者说不会出现大量的BUG. 这应该算是我们做的比较成功的一点. 开发规范/流程越早定义规范越好, 规范中不明确的点团队内及时沟通, 保持团队内对规范的理解一致. 出现问题尝试梳理规范(这里不是指那些条条框框, 规范一定要实用), 但是规范如果让人执行, 那肯定是不可能完全靠谱的, 所以..... 能用代码做的一定交给代码做, 例如我们的SDK打包过程, 可以定义一个规范说明要放那些文件, 文件夹如何命名等等, 我们前期确实这样做了, 结果是基本上每次都会有一点遗漏, 后来用了一个脚本打包, 从那以后生活美好了许多. 故能用代码做的就用代码做, 代码比人更可信. |
|