rss
    0

    接sdk_入职三个月,游戏SDK开发总结

    2024.02.13 | admin | 56次围观

      7bc94e83ef5443afe1e2ebe7e242ba8b.png

    原文转自掘金:蓝师傅

      链接:

    https://

    juejin.im/post/5ee6545f

    6fb9a047a11dbf56

      蓝师傅最近几个月非常忙,很久没更新文章了,惭愧惭愧,距离上一篇技术文章已经是半年前了~

      前几个月负责游戏SDK的开发、维护、对接工作,项目结束了一段时间了,梳理一下游戏SDK开发涉及到的知识点。

      有些朋友可能对游戏SDK开发有点陌生,希望本文对你有一些帮助。

      记得17年毕业那会儿找工作的时候,去一家公司面试,面的是游戏SDK岗位,面试官一开口就问我之前有没有做过SDK开发,我说没做过,结果聊了几句就回去等通知了~

      HR在筛选简历的时候可能出了点问题,但是,从技术角度讲,没做过SDK开发就不能胜任这个岗位了吗?

      SDK(Software Development Kit)是软件开发工具包的意思,一般我们将一部分功能单独封装成一个library进行开发和维护,然后将编译产物(jar包或者aar)提供给多个项目使用,这就属于SDK开发。 常见的如短视频SDK、推送SDK,分享SDK,以及这篇文章的重点:游戏SDK。

    2.1 游戏SDK是啥

      db24aa3c9ac97b9e4360cb49b5866424.png

      小红是做社交App的娱乐公司,日活几千万,想让自己平台多元化,比如做个游戏下载的功能,给用户下载,用户觉得好玩,可能就会付费买装备,但是有个问题,小红并不会做游戏,如果单开一个产品线去研发游戏,投入是相当巨大的,所以想到能不能去外面接游戏进来。

      37b35efc4e128df585b03103f048a1c7.png

      最终小红和小绿确认合作关系:

    1、小红提供游戏SDK,需要包含核心的登录功能、支付功能;

      2、小绿开发完的游戏,不接入其它平台的登录和支付系统,直接接入小红的游戏SDK,用小红的登陆和支付系统

      3、小红每个月都要跟小绿对账,按比例分成给小绿

      小结: 游戏SDK跟普通SDK的区别在于,它提供一个游戏账号体系和支付体系,核心就是登录和支付功能。

    2.2 游戏SDK的流程图

      游戏SDK最核心的是登录和支付功能,其它的都是运营相关的,例如埋点、数据统计等等~

      登录和支付的流程大概如下图:

      762e58f36b535c8c84b2dc46e6f96fdf.png

      图画的比较简陋,解释一下,上半部分是登录流程、下半部分是支付流程,

      流程还算比较简单的~

      接下来说说游戏SDK开发的一些需要注意的点:

    3.1 少依赖

      很多开发者都知道,作为SDK,应该尽量少使用开源库,或者说不用开源库,

      而是通过手写网络框架,手写数据库等等,主要是考虑两个方面:

    减小SDK体积 ;避免第三方接入的时候发生依赖冲突

      当然,依赖库并不是说不能用,有时候一些数据统计的库需要依赖第三方,那这种情况是没有办法避免的,可以在对接文档中提供一个解决依赖冲突的办法

    3.2 解决依赖冲突

    方式一

      在app的build.gradle中添加类似配置如下:

    方式二

      exclude

      exclude是最常用的解决依赖冲突的方式,但如果多个依赖库引入不同版本的其它库,需要分别写好多个exclude,显然第一种方式比较简单粗暴。

    3.3 对外暴露的接口尽量少

      面向接口编程,以游戏SDK为例,对外暴露的接口一般有SDK初始化、登录、支付等,参考设计如下:

      定义接口:

      实现类

      实现类是我们的内部逻辑,我们不希望被外部访问到,外部只需要知道有 这个接口中的方法就行,我们可以再写个单例的管理类来给外部使用

      的关键字表示单例,

      外部通过来调用SDK中的方法,

      以后要提供其它方法,只要修改 接口,然后在 和 中分别实现即可。

      当然,不是说一定要这样拆分三个类,这只是一个面向接口编程的例子。

      游戏SDK前期开发自测可能是很顺利的,难度不大,但是在跟游戏对接的时候可能会出现一些问题, 什么,至于为什么这样,下面会介绍~

    4.1 SDK要支持Eclipse

      SDK 1.0 测试通过,正式上线,高高兴兴地把文档甩给对接方,心里想,这个我测过的没问题,demo也给了,只要按照文档和demo来,问题不大。

      bf6dd04afddaa4d21be2498117153dcd.png

      然而,对方回复了一句:“有Eclipse接入文档吗?”

      我一脸懵逼,这都什么年代了,真还有人用Eclipse开发App?

      我想试图说服对方用Android Studio,然后得到的回复是:其它的游戏SDK都提供了Eclipse的接入方式~

      想起我上一次用Eclipse应该是大三的时候...

      就这样,第二天下载了Eclipse之后,按照教程安装APT插件,然而编译一直报错,忘记具体的错误信息了,最终的解决办法是下载了一份Eclipse版本的SDK,Eclipse 不能使用Android Studio版本的SDK。

      好了,Eclipse环境弄好了,hello world也跑起来了,开始写demo~

      由于SDK的产物是aar,而Eclipse只能依赖jar包和library,一般都用jar包依赖,先将aar解压出来,把里面的拷贝出来重命名,然后在Eclipse中依赖这个jar包,同时,SDK的资源文件、libs目录下的jar包也需要拷贝到Eclipse项目中。

      终于,编译成功,安装,打开,闪退了~

      e0a6c4e1971097d1e93cebefedc802ba.png

      奔溃信息指向:,错误信息是

    4.2 setContentView(xxx) 凭什么奔溃?

      看以下这段代码

      这段代码在打包aar的时候,Android Studio接入没问题,但是打成jar包,Eclipse接入的时候会奔溃,奔溃信息如下,

      按下command 键,鼠标放到R.layout.activity_test 上去

      6b24d39f3f3a2f4d6b7d563b45a079c7.png

      这是一个常量,这个常量定义在R文件中,在AAPT阶段生成,

      有小伙伴应该已经看出问题所在了,先假设大家都不知道,我们来回顾一下apk打包的主要流程

    AAPT(或者AAPT2)工具打包资源文件,生成R.class文件,resources.arsc资源索引表AIDL 转换成Jave代码(有AIDL的话)Java代码编译成.class文件通过dex工具将.class文件转换成Dalvik 字节码,也就是.dex文件通过ApkBuilde工具将.dex文件和其它资源文件打包成未签名的apk通过签名工具给apk签名,v1签名使用jarsigner、v2签名使用apksigner(sdk 25版本开始提供)

      apk编译的第一个阶段,AAPT会打包资源文件,生成R.class文件和resources.arsc资源索引表

      library项目在打包aar的时候,上面123这几个流程一定会走的,但是aar中并没有生成 resources.arsc 这个资源索引表,资源的id跟资源文件的映射关系记录在R.txt中,如下图:

      60c1ba23c7c7d105a58081b7ad03e3ed.png

      而Eclipse因为只能接入jar包,也就是解压aar后取出里面的,当我们把资源文件拷贝到Eclipse,再编译apk的时候,资源文件会对应一个新的资源id,而aar中classes.jar里引用的资源id是不变的,

      里面的

      相当于,

      而当我们将 拷贝到Eclipse项目后编译,AAPT重新给它生成一个资源id, 对应的资源id已经不是 -1300150 了,

      这就是为什么里面的 会报错找不到资源。

      知道了问题的原因之后, 要解决这个问题,那么SDK里面使用资源id需要动态去获取,不能使用R文件里面的常量~

    4.1.2 动态获取资源id

      谷歌提供了相关的API,可以通过资源名称获取资源id

      Resources#getIdentifier(String name, String defType, String defPackage)

      第一个参数是资源名称,例如一个TextView定义的id叫tv_title;

      第二个参数是类型,例如 string、xml、style、layout 等等,跟R.class文件里面的内部类是对应的

      db9623f945beb440e6d2cea622f8a62e.png

      如果想获取布局文件id,传layout,如果是获取字符串id,传string,以此类推。

      第三个参数是包名。

      最后封装成工具类如下

      然后 需要修改成

      问题是解决了,但是还是需要了解一下底层原理,例如,AAPT打包资源文件,会生成资源id,资源id跟资源是如何关联起来的呢?通过资源名称去读资源id,又是如何读取的呢?

    3.1.3 AAPT打包产物

      编译的第一个阶段,使用AAPT打包资源文件,产物如下

    res文件夹内的图片及xml资源(xml被编译成二进制)assets文件夹(不会生成资源id)二进制AndroidManifest.xml资源索引表 resources.arscR.class文件

      重点关注资源索引表 resources.arsc,

      resources.arsc 文件的数据格式比较复杂,Android Studio可以帮我们解析出来

      通过Android Studio的 Build -> Analyze APK,打开apk后选择 resources.arsc打开

      576a3b4f5dfc861c8c64fb9aca402421.png

      id(资源id)、name(资源名称)、value(资源路径)都可以通过这个索引表来互相转换,

      前面说过 ,之所以可以通过资源名称获取到资源id,当然还是要借助 resources.arsc 这个资源索引表。

    3.1.4 通过资源名称读取资源id的原理

      源码我大概跟了一下,调用流程是

    Resources#getIdentifier

      ResourcesImpl#getIdentifier

      AssetManager#getResourceIdentifier

      AssetManager2.cpp#GetResourceId

      不贴太多源码,大家有兴趣可以看 AssetManager2.cpp 这个类,里面关联了 ApkAssets,frameworks/base/libs/androidfw/ApkAssets.cpp

      ApkAssets.cpp 里面有 resources.arsc 的定义和使用

      59b2b0573a0ffe22a33d0269146b05f1.png

      得出结论是是在native层加载和解析的,通过这个资源索引表,可以将资源id和资源名称、资源路径相互转换。

      上面讲的这些太枯燥了,游戏SDK就这些内容?能不能来点实用的呢?

      如果是普通的游戏SDK,那么只要保证接入方能够成功接入SDK就完事了,然而,

      小红除了提供游戏SDK之外,还需要对 接入游戏SDK的游戏进行验收,确保游戏SDK的功能正常。

      67491d98f1bb6337443c27eeff3be401.png

      毕竟游戏是要在小红的平台上运营,小红有责任和义务对每一个游戏进行测试验收,确保基本功能正常,总不能用户一打开就奔溃吧~

      随着SDK的版本升级,功能会增加,需要验收的功能会越来越多,例如:验证签名,SDK有检查更新的功能,token过期,游戏需要做退出登录逻辑等等...

      下面将介绍我是如何处理一些问题的。

    5.1 日志开关

      SDK接入出现问题,release版本若关闭了日志,我们需要将日志打开复现问题,常用的有两种方式:

    可以参考开发者模式的开关,设置某个控件的点击事件,例如在连续点击5次的时候打开日志开关。 日志开关需要持久化,例如保存到sp,在SDK初始化的时候去读这个开关。还有一种做法是类似友盟,初始化的方法提供debug参数,让接入方可以传true来查看日志,但是考虑到SDK内部信息安全,我没有这么做。

    5.2 配置检查

      我提供的demo运行是正常的,但是第三方他们接入的时候经常会出现一些问题,可能是他们的Android SDK版本不一样,或者一些配置没有严格按照文档来写,作为SDK的开发者,我希望这些配置的问题接入方可以自己发现和处理,这就需要在游戏SDK中增加检测的逻辑。

    5.2.1 一个检查更新的功能

      Android 8.0 开始,调起应用安装页面,需要用户显式打开未知来源开关,于是有如下代码

      2d61041ada38dd92e8297bf71cbbfaab.png

      有一次发现在接入方的apk中,,一直返回false,没法调起安装页面,首先想到的是,接入方没有声明安装权限

      然后自己去掉权限声明验证一下,发现会抛异常,说明不是这个原因。

      最后发现 targetSdkVersion 小于26的话, 一直会返回false,目前各大应用市场已经陆续要求targetSdkVersion必须26或以上,为了保证SDK的更新功能正常,在SDK初始化的时候,添加如下检测代码

      1c828b87ed9ba254859fcf93377cf996.png

      这样接入方targetSdkVersion就一定要26或以上,否则抛异常,从异常日志中就可以发现问题。

    4.2.2 FileProvider 需要增加配置检查

      由于 7.0之后安装apk需要通过FileProvider来获取url,所以manifest有了这样的代码

      974bfb199ccc1e7d78edac9a272ac72f.png

      如果是Android Studio打包,一般会自动读取build.gradle中的applicationId来替换占位符${applicationId},

      如果是Eclipse打包,占位符${applicationId}则原封不动,不会被替换,那么下面的代码就会报空指针了

      如何保证接入方一定有配置FileProvider,并且配置正确呢?增加配置检测代码如下

      4138d4014cfcfe311d5ae41995622400.png

      在sdk初始化的时候去私有目录创建一个空文件,然后通过 方法触发FileProvider获取url的逻辑,如果有异常,说明FileProvider配置不对。

      之后在验收apk的时候,只要能正常安装打开,就说明FileProvider配置是正确的。

    5.2.3 签名验证

      游戏方接入游戏SDK之后打包成apk,这个apk要在我们平台上线,我们希望统一apk签名, 所以在验收apk的时候,需要确认apk的签名。

      查看apk签名主要用两种方式:

    针对v2签名

    keytool -printcert -jarfile xxx.apk

      or

    apksigner verify -v --print-certs xxx.apk

      6f6fb6eb4443a21522ef56093647880b.png

      这个命令虽然简单粗暴,但是要求apk使用v2签名,

    针对v1签名

      如果apk是使用v1签名,那么比较麻烦,需要解压apk,找到目录下的 ,然后执行命令

    keytool -printcert -file CERT.RSA

      7eddead8f7a7ccdb4d1f5f290e690823.png

      针对v1签名可能有更好的办法,我没找到~

    代码签名检查

      如果是使用v2签名还好,直接一个命令就能查看签名,但是大部分游戏发行商都是使用v1签名,手动验证签名还是比较麻烦的,还是代码里验证下比较香啊~

      类的逻辑主要是获取应用签名,check方法是将应用签名跟正确的签名做对比,相同就返回true。

      如果签名不正确,游戏方接入SDK过程会弹toast提示

      如果有其它必选配置,类似的方式处理一下,一劳永逸~

      渠道包大家都不陌生,一般是为了统计app在不同应用市场的数据,例如新增、日活、留存等。

      游戏SDK的渠道包概念稍微有点不同:

    平台上线了游戏之后,依赖用户自己来下载游戏,起量是很慢的,所以需要推广,如果使用推送让用户去下载,那么用户体验会很差。所以需要让那些有影响力的人来做有偿推广。SDK中每一个请求接口都会传渠道标识,比如A用户去推广游戏,我们会给他一个打了A渠道标识的apk,通过这个apk注册的用户,就归属A用户。

    6.1 选择打渠道包的方式

      按照签名方式的不同,目前有两个比较热门的打渠道包的开源库

      下一代Android打包工具:https://github.com/mcxiaoke/packer-ng-plugin

      有两个版本,支持v1签名和v2签名。

      Walle(瓦力):https://github.com/Meituan-Dianping/walle

      目测只支持v2签名。

      对于游戏SDK来说,单纯使用Walle并不适合,因为大部分游戏发行商,默认的apk签名方式都是v1签名。

      成年人不喜欢做选择,两个都要

      可以使用 PackerNg-v1 + PackerNg-v2,也可以使用 PackerNg-v1 + Walle。

    6.2 渠道包原理

    6.2.1 针对v1签名

      PackerNg-v1 的原理:

    APK文件其实是一个带签名信息的ZIP文件,根据 ZIP文件格式规范,ZIP文件末尾有一部分元数据代表ZIP文件注释,正确修改这一部分数据不会对ZIP文件造成破坏

      d794a77185cbc0e0283d849e17f25798.png

      针对v1签名,还有其它渠道包方案,但是大部分都存在效率问题,例如利用gradle的productFlavors属性打渠道包,速度慢;或者利用META-INF目录不被签名校验的特点,加入文件名为渠道名的空文件,但是读取渠道的时候比较慢,因为需要解压apk读取。

    6.2.2 针对v2签名

      使用v2签名的apk,上面针对v1签名的方案全部失效。

      Walle 的原理是:

    V2签名块中有个区块可以添加一些附属信息,并且不会被签名校验,将自定义渠道信息写入这个区块,生成渠道包。

      前期,游戏发行商出的apk可能没有使用我们的签名,让他们重新打包有时候耗时比较长,所以必须掌握apktool的相关命令,来进行解包和打包,以及签名。

    7.1 apktool下载

      apktool github

      需要配置下环境,比较简单,mac:下载apktool.jar、apktool可执行脚本,放到 /usr/local/bin/ 目录下,然后 command + x 设置权限就可以了。

    7.2 反编译

    apktool d demo.apk

      4b9be408323b1580490e5506625cccd4.png

      会将demo.apk反编译之后输出到demo目录,-o 参数可以指定输出目录。

      反编译之后就可以修改资源文件或者字节码

    7.3 回编译

    apktool b demo -o unsign.apk

      1e766b2220d331ac125086a0191a71b1.png

      输出的是未签名的apk,需要签名才能安装到手机上

    7.4 apk签名

      一般我们用Android Studio打一个签名的apk很简单

      

      但是单独给一个未签名的apk签名,就需要借助签名工具,v1签名是使用jarsigner,v2签名是使用apksigner,

    7.4.1 v1签名

    jarsigner -verbose

      -keystore [签名文件路径]

      -keypass [密码]

      -storepass [密码]

      -signedjar [输出apk路径] [需要签名的apk路径]

      -digestalg [摘要算法的名称如SHA1]

      -sigalg [签名算法的名称如MD5withRSA]

      [证书别名]

      例如我的签名文件叫 lizhigame.keystore,别名密码都是 lizhigame,那么签名命令如下

    jarsigner -verbose -keystore lizhigame.keystore -keypass lizhigame -storepass lizhigame -signedjar sign.apk unsign.apk -digestalg SHA1 -sigalg MD5withRSA lizhigame

      执行命令后可以看到控制台日志

      cde83399272946b8423ade92a08008b7.png

    7.4.2 v2签名

      V2 签名使用apkSigner,在SDK build-tools下,注意在版本25以上才有

      3818c44723ef802a91cc80684683f475.png

      apkSigner签名命令:

    apksigner sign

      --ks [签名文件]

      --ks-pass pass:[密码]

      --out [输出apk路径]

      [需要签名的apk]

      例如我的签名文件叫 lizhigame.keystore,别名密码都是 lizhigame,那么签名命令如下

    apksigner sign --ks lizhigame.keystore --ks-pass pass:lizhigame --out sign_v2.apk unsign.apk

      18d330a330af0f0b73373f70ae9c6747.png

      apksigner 签名过程没有任何提示,可以结合验证签名命令一起使用

      验证签名

    apksigner verify -v --print-certs sign_v2.apk

      这篇文章是我对游戏SDK开发三个多月工作的总结和分享,游戏SDK开发,更多的是业务问题处理和对接问题处理。将重复性的工作做成自动化,通过代码检查配置的方式,强制让接入方按照我们的要求来接入SDK,可以减少不必要的沟通成本。

      本文知识点总结:

    介绍游戏SDK的概念,流程介绍游戏SDK开发的注意事项Eclipse接入SDK奔溃原因分析,需要动态获取资源id介绍AAPT打包资源文件过程,源索引表resources.arsc的作用游戏SDK的一些细节的操作渠道包方案和原理apktool反编译和回编译使用jarsigner和apksigner对apk进行签名

      如果你正在找工作,招聘网站上多多少少有一些游戏SDK开发的岗位,薪资一般不会太低,希望这篇文章能带给你一些帮助。

    接sdk_入职三个月,游戏SDK开发总结

    接sdk_入职三个月,游戏SDK开发总结

    版权声明

    本文仅代表作者观点,不代表xx立场。
    本文系作者授权xxx发表,未经许可,不得转载。

    发表评论