37手游SDK架构分享
大家好,我是加权,本篇文章向大家分享下发行SDK的架构。
需求背景
问题
作为发行SDK,通常需要面对的几大需求
- 渠道需求,研发接入发行SDK后,可以适配到TapTap,华为,应用宝等其他渠道
- 广告上报需求,广告投放时,需要接入不同的上报SDK,如抖音,腾讯等
- 皮肤替换需求,如特定游戏皮肤定制
- 试验性功能,如当前较常见的云游戏、直播等功能,特点时效果不佳时会移除
方案:插件化
以上需求,最大的问题是要解决功能灵活性的问题,需要做到面对不同的渠道、广告,游戏,做到功能灵活删减。
为此我们引入功能插件化的概念。
通常行业中的插件化是指
是将整个App拆分成很多模块,每个模块都是一个Apk(组件化的每个模块是一个lib),最终打包的时候将宿主Apk和插件Apk分开打包,只需发布宿主Apk到应用市场,插件Apk通过动态按需下发到宿主Apk。
这里我们只需要实现模块为apk,在切包时灵活组合即可,动态下发、加载可以视为高阶功能,本文不讨论。
方案架构
切包架构
切包是实现插件化的关键步骤,基本架构如下
- 37 SDK*(蓝色):是指提供给研发接入的sdk,当不涉及对外API改动时,不需要更新
- 37 SDK(绿色):是指包含核心功能的apk包
- 渠道功能、广告工程(插件工程/模块):是用来构建插件apk的工程/模块
通过切包流程需要实现
- 覆盖37 SDK*(蓝色)的代码,实现功能更新
- 合并核心apk和插件apk,往最终包体中接入插件功能
具体的工程结构如下
- 打包后台:统一管理sdk、插件、母包以及插件参数
- 切包主工程:核心切包逻辑
- 补丁工程:针对特殊游戏或者渠道定制化切包流程时使用
- 母包:由研发提供
- sdk apk:核心功能apk
- 插件apk:插件功能apk
代码架构
在具体实现中,为了实现功能的灵活组装,我们需要解决以下问题
- 插件不存在的情况
- 插件不存在,意味着功能不可用,我们在代码中需要进行特殊处理,避免程序异常
- 存在多个插件的情况
- 多个插件,意味着我们需要有手段选择具体插件,例如增加配置文件和后台下发配置
- 新增插件的情况
- 当某个功能没有预留插件功能,需要新增开发,我们需要有手段让旧版本也可以动态加载该插件功能
为了实现以上目的,代码构架设计如下(以登录功能为例) 我们对功能实现划分以下概念
- 对外接口类:这里会直接引入管理类,是所有功能的集合,我们直接替换整个对外接口类就可以实现替换任意功能
- 管理类抽象:在代码实现中,大部分都是引用管理类抽象类,这样在替换实现时不会出现问题
- 管理类实现:管理类通常是同个模块下多个功能的组合,这样可以提供手段批量替换多个功能
- 功能抽象:单一功能的抽象,是最常用的插件层级
- 功能实现:具体的实现
对功能进行划分后,我们就可以根据实际需要,使用插件类对不同的类进行替换,来实现不同程度的功能替换。
关于如何处理无插件情况和插件配置化,在下文讲解
插件
参数
对于大多数第三方sdk,都有单独的参数,因此当我们通过插件接入第三方功能时,我们需要有手段处理这些插件的参数。
这里我们主要通过字符串资源获取参数,并通过gradle插件脚本处理参数文件的方式处理,架构如下
- 打包后台:统一管理插件和参数,可以输出sdk和插件关联的参数到json中
- JSON参数文件:sdk和插件使用的所有参数
- 参数插件、切包工程:逻辑一样,将JSON参数文件中的参数转换成string资源
这里利用了安卓字符串资源加载的特点,可以做到
- 编译时修改参数
- 动态获取参数,可以通过切包修改参数
实例化
插件的实例化常见的几种形式
- 代码枚举
- 通过直接枚举所有插件类型进行实例化
- 配置文件/接口下发
- 根据配置文件的类型,将对应插件实例化
- 插件注入
- 插件apk自己注入插件实例到sdk中
我们实际会结合第2和第3种方法处理插件的实例化问题
配置文件/接口下发
关键步骤
- 通过读取文件或者接口下发的配置获取配置项
- 根据配置项,通过插件管理类获取插件实例
- 在插件不可用时,返回fake实例,避免程序异常
插件注入
根据配置获取实例时,我们可以指定插件类型获取,那么sdk是如何知道有哪些插件的呢,比较合适的方法是让插件自己持有插件信息,通过切包注入插件信息到skd中,这样sdk本身对插件完全无感知,解耦更加彻底。
代码架构如下图
- PluginContentProvider:我们利用provider的特点,在应用启动时,通过解析AndroidManifest.xml中的meta-data来获取插件信息,然后注册到PluginManager中
- PluginManager:保存所有插件信息,注意在外部获取插件时才实例化插件
- Plugin Apk:插件apk,带有插件的meta-data信息,在切包时把插件信息合并到sdk中,实现注入
- 插件注解+脚本:使用注解标记插件类,并且利用脚本自动生成插件信息,可以提高插件开发效率和出错几率
至此,我们就已经完整构建了整个插件化架构,包括了插件的基本架构,sdk使用插件的形式,切包架构等多个模块。
总结
使用插件化架构需要多个模块互相配合,关键点如下
- 代码需要足够解耦,功能需要足够抽象
- 插件模块,包含注册插件、插件实例化等
- 切包模块,需要支持多apk融合,并且可以处理多插件参数等情况
- sdk模块,需要支持适配多插件情况
- 打包后台,需要管理所有插件
整个方案大致就是这样,欢迎大家在评论区回复讨论~