sdk开发经验总结

文章目录

博主之前做过一些 sdk 的开发,也对接过一些 sdk ,有一些 sdk 设计得非常优秀,也有非常糟糕的。

sdk 是给别人使用的,所以在开发 sdk 的过程当中,我们始终要提醒自己站在使用者的角度去考虑问题。

1. 降低接入成本

1.1 接入简单

Android 推荐使用 maven 仓库,通过 gradle 进行依赖。Ios 使用 pod 库。sdk 对外屏蔽内部的实现,只提供接口即可。

good case
通过 maven 仓库管理 sdk

maven { 
    url "https://xxx.xxx.xxx/xxx/xxx"
}
implementation 'com.xxx.xxx:xxx:1.0.0'

bad case
之前接手过一个护眼 sdk,模块并没有良好的封装,而是直接将源码和工程提供给了业务方,导致维护起来特别麻烦,出现问题,业务方将问题抛给 sdk,sdk 觉得是业务方修改了他们的源码,另外,更新版本也特别麻烦,需要源码对比,然后手动将 diff 应用,体验极差。

1.2 文档和 demo

文档和笔记最重要的区别就是,文档是给别人看的,而笔记是给自己看的。
很多时候阅读文档的人并不了解相关的上下文,所以在文档当中最好要先介绍一下相关的背景,以及提供详细的示例。
另外文档更新要及时,当某个接口更新,需要及时的更新对应的文档,避免出现文档和代码不一致的情况。

good case
微信小程序官方文档
每篇文档均有详细的说明和代码示例,一目了然

bad case
如下所示为我之前对接过的一个后台接口文档,每个接口都没有参数和返回值的说明和例子,文档看了让人觉得很疑惑,可能只有开发者自己能看懂,看完之后还需要再次找对应的开发沟通和确认。
《sdk开发经验总结》

1.3 api接口设计

api 接口设计应当注意以下几点:

1. 接口保持精简,不要提供过多的接口
2. 每个接口均应当提供详细的接口说明

包括参数和返回值,以及接口的使用示例,
例如,以下为某个生命周期的接口文档,每个生命周期的接口均有详细的描述,并提供使用示例。

接口参数备注
onDownloadingint total,int downloaded下载进度
onDownloaded下载结束
onMerged合并结束,此时会调用xxx接口
onUpdated更新完成
UpdateListener updateListener = new SimpleUpdateListener();
HotFix.registerListener(updateListener);//注册生命周期监听
HotFix.unRegisterListener(updateListener);//反注册生命周期监听 
3. 接口参数不应过多

个人觉得当参数超过5个时,就应当要考虑将参数封装成一个数据类
good case
上报接口,参数简洁明了

void report(String event, Map<String, String> params);

bad case
参数过多,应当将过多的参数封装成数据类

public static void report(final Config appConfig, final int eventType, final String page,
        final String info, final String cmdName,
        final int cmdRetCode, final String appType, final long cost, final String httpRequestUrl,
        final long timestamp,
        final String reserves1, final String reserves2, final String reserves3, final String reserves4,
        final String renderMode)
4. 接口方法过多,应当提供默认实现

以下是一个生命周期的接口:

public interface Listener { 

    void onStartRequest();

    void onFinishRequest();

    void onStartDownload();

    void onDownloading(int total,int process);

    void onDownloadResult(boolean result,String msg);

    void onError(int code, String msg);

}

如果我们不做处理,那么用户在使用的时候就会变成下面这样,可能用户只关心 onError 和 onDownloadResult 两个方法,但是却需要复写所有的方法。

sdk.registerListener(new Listener() { 
    @Override
    public void onStartRequest() { 

    }

    @Override
    public void onStartDownload() { 

    }
    
    ......

    @Override
    public void onDownloadResult(boolean result,String msg) { 
        
    }

    @Override
    public void onError(int code, String msg) { 
        
    }
});

针对这种情况,我们应当提供一个默认的实现,这样使用者只需复写他关心的方法即可。

public class SimpleListener implements Listener { 

    @Override
    public void onStartRequest() { 

    }

    @Override
    public void onStartDownload() { 

    }

    @Override
    public void onDownloading(int total,int process) { 

    }

    ......

    @Override
    public void onError(int code, String msg) { 

    }
}

//使用方只需要复写自己关心的方法即可
sdk.registerListener(new SimpleListener() { 

    @Override
    public void onDownloadResult(boolean result,String msg) { 
        
    }

    @Override
    public void onError(int code, String msg) { 
        
    }
});
5. 接口尽可能和系统或者业内标准保持一致

例如,以下是博主之前日志组件的一个接口

public static <T> void i(String tag, T obj, Throwable tr) { 
    instance.log(LogLevel.INFO, tag, obj, tr);
}

public static <T> void i(String tag, T obj) { 
    instance.log(LogLevel.INFO, tag, obj);
}

Android 系统的日志接口

public static int i(String tag, String msg) { 
    return println(LOG_ID_MAIN, INFO, tag, msg);
}

public static int i(String tag, String msg, Throwable tr) { 
    return println(LOG_ID_MAIN, INFO, tag, msg + '\n' + getStackTraceString(tr));
}

可以看到,日志组件的接口在原有系统接口上做到兼容和增强,
其中第二个参数为范型,可以根据类型自动匹配到 sdk 内部的 Formatter,例如 JsonFormatter,ArrayFormatter以及用户自定义的 Formatter,然后将日志格式化输出。
这样做的好处就是,使用方可以快速接入,并且对于接口几乎没有理解成本。

1.4 向后兼容

新版本应当做到和旧版本兼容,这样用户在进行升级的时候,只需要修改 gradle 文件里面的版本号即可。
版本号的命名规则可以使用 x.x.x,
第三位表示 bugfix,第二位表示新增了一些 feature,第一位表示发生 breaking changes。
这样我们就可以通过版本号可以清楚的知道哪些版本是兼容的。
例如发布3.1.1,我们就知道这是基于3.1.0做了一些bugfix;如果发布4.0.0,那么就与3.1.0可能不兼容了。

2.稳定性

稳定性是 sdk 的基础,没人愿意使用不稳定的产品。

2.1 plan B

在 sdk 设计方案和接入的场景,我们可以考虑一下 plan B。

场景方案例子
sdk 接入增加配置开关,屏蔽sdk之前做的日志组件 sdk,是为了替代原来的日志组件,我们可以在第一个版本内保留两个日志组件,然后通过配置开关来设置使用哪个日志组件。
默认使用新的日志组件,上线之后一旦观察到有异常,可以下发配置切换到原有的日志组件,以此来兜底。
sdk 设计方案实现方案考虑兜底逻辑在实现热更新 sdk 时,我们每次进行热更新之后,都会在本地保留一个历史版本,当发生异常时,可以回滚到旧版本,以此来保证用户体验。

2.2 监控

这里同样可以将监控区分为业务层和 sdk 内部,业务层的监控包括 crash 率监控,性能监控,这种我们可以接入一些专业的监控 sdk 来实现,
内部监控为 sdk 本身的业务监控。以热更新组件为例,
在热更新的场景,我们会进行以下几个监控步骤:

  1. 热更新成功和失败上报
  2. 热更新失败触发日志上传
  3. 上报达到阈值,触发告警
  4. 本地在热更新完成之后会进行异常监控,达到条件,触发回滚
  5. 回滚上报并触发日志上传

2.3 错误指引

sdk 的 onError 接口应当返回明确的错误码和错误信息,并且在文档当中每种错误信息均需要给出对应的说明。
我比较偏向于分类错误码的方式,这种方式比较方便后台统计和分类。
以之前做的插件化 sdk 的错误码为例,
我们将错误码分成了4类:

1xx:后台相关的错误码
2xx:下载相关的错误码
3xx:bundle加载相关的错误码
4xx:其他错误码

每一个类别下面有详细的错误类别,比如201:文件下载失败,202:文件MD5校验失败,203:文件解压失败。

3.其他

3.1 易扩展

一个好的sdk应当具有良好的扩展性,以日志组件为例,
我们将日志输出抽象成了一个 Printer 接口,在 sdk 内部默认实现了将日志输出到控制台,输出到文件。

public interface Printer { 
    void print(LogItem item);
}

使用者可以实现这个接口,然后可以很方便的将日志输出任何地方,比如输出到网络,输出到其他进程。

3.2 关注性能

如果sdk涉及到性能,那么在 sdk 正式发布时,需要先进行一下性能测试,主要包括 cpu 峰值,内存占用率,以及和具体业务相关的维度。
以日志组件为例,在正式发布之前,我做了如下测试:
开启5个线程不间断的瞬间写入10w 条 50byte 的日志,同时观察写入完成的时间,日志大小,cpu 峰值以及内存占用的情况。
以下为部分实现结果,使用三星 s20 机器测试,供参考

维度不压缩不加密压缩加密
时间269.2ms276.6ms
文件大小12.3MB851kb
内存占用峰值增长23MB28MB
cpu峰值12%12%

3.3 鉴权

鉴权并不是每个 sdk 都会遇到的问题,主要涉及到和后台交互较多,并且有安全方面考量的场景就需要考虑到鉴权。

3.4 合规

合规是一个非常重要的问题,在之前的项目组,隐私合规问题也是由我来跟进,作为业务方,最麻烦的就是 sdk 出现隐私合规问题,这样就需要推动 sdk 方去更改,解决问题的链路就会被拉长,
更可怕的是,有时候还会遇到有一些 sdk 已经找不到维护的团队了,就必须使用一些特殊的手段来实现合规。

其实做到合规也不难,主要是对于一些隐私字段的获取,比如 AndroidId ,IMEI ,手机型号等。上报是隐私合规的重灾区,后台为了确定唯一性或者用户画像,通常需要 sdk 提供一些涉及隐私的字段。

如果实在需要这些字段,sdk 可以提供获取隐私字段的接口,让业务方去实现这些接口,以此来避免 sdk 的隐私合规问题以及获取隐私字段的频率问题。

good case
让业务方传入隐私字段,或者提供隐私接口让业务方实现

void init(String appid,String imei)

//或者
interface Privacy { 
    public String getImei()
}

bad case
在 sdk 内部直接访问隐私字段

void report(){ 
    ...
    String device = Build.DEVICE;
    ...
}

3.5 sdk接入标准化

作为业务方,曾经接入过非常糟糕的sdk,体验非常差,至今回想起来都会觉得恶心,所以当时就一直想要尝试建立sdk接入的标准化,只有达到我们标准的sdk才能够接入,具体包括以下几个维度:

维度备注
包大小增量不同的团队的标准不一致
性能对比接入前和接入后版本的cpu,内存,FPS等指标,若差值达到约定的阈值,团队内部应和sdk讨论性能问题以及是否将其下架
接入成本必须要有良好的封装,使用 maven 库或者提供 aar,jar 包等,不应直接提供源码
稳定性在灰度以及正式发版时,观察 crash 率,看是否有 crash 是由新的 sdk 引起,若超过一定的比例,考虑将其下架

4. 总结

作为业务方,我对接过非常优秀的 sdk 也对接过非常糟糕的 sdk,遇到糟糕的 sdk,内心一万匹草泥马呼啸而过。所以当自己作为 sdk 的开发者时,应当换位思考一下,如果我要接入这个 sdk,我希望这个 sdk 是什么样的,其实无非就是要有详细的说明文档,简单的接入方式,清晰的 API 接口以及一个简单的 demo,要求其实并不高。

    原文作者:我是黄大仙
    原文地址: https://blog.csdn.net/hbdatouerzi/article/details/124439273
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞