八月到最近做的事情

what I did from Auguest

Posted by 宸笙 on November 13, 2018

概述

8月入职一家游戏公司,负责SDK周边相关,包括Android和iOS平台,这其中也经历和学习到了一些东西,下为总结;

main

起初都是挑的SDK开发的工作,开始是广告SDK,也拿到了一家成长速度很快的公司(日活3亿,用户有猎豹等)的offer,其实做SDK能做到行业化和数量化的估计也就广告SDK和游戏SDK比较典型(或许这应该是规律),不会像之前笔者所从事的后端云SDK,国内就几家公司,或者一些专门做数据采集和分析的公司比如热云,GrowingIO等,不过也没广告和游戏的多,这些都相对会做到通用化,因为接入的用户可能很多,这会逼着SDK作者想办法去做到通用化和规范化(对外开放,文档,标准等),不像比如一些企业部门的内部SDK,给特定行业如公交地铁之类的用,这样的话用户(或者说接入方本来不多)遇到的问题就不多,对SDK开发的人的提升也相对有限,所以后面自己也慢慢会留意游戏SDK开发(而且主要是很多游戏SDK的文档和类库都没能公布使我越发好奇),也是好奇游戏行业SDK是怎么实现的,虽说在某方面不太自信比如自己打小就没玩过游戏,后面还是拿到了一些offer;

工作内容

面试的时候,得知除了Android平台的SDK,还有iOS的SDK需要持续开发维护,自己没涉猎过iOS,不过还是想尝试下,对自己的快速学习能力还是有底的,也因为这家公司很多是从某游戏大厂出来的,加上比较nice,觉决定来这边了;

游戏SDK初探

其实很多做开发的伙伴都知道SDK本质上就是一个共享(复用)的软件类库,在java或者Android平台多表现为jar或aar包,为什么在一些比如游戏,广告等会有这样对外SDK的出现呢,就是因为比如某个游戏渠道的一些注册登录支付的这些都是游戏应用所共用的,这样就能实现功能复用和应用解耦,也能加快开发效率,不过游戏行业有一些特点和术语,比如渠道特别多,游戏开发者(接入方或称CP),母包,子包(渠道包)等,下面大致讲下:

CP

之前笔者开发维护的SDK有Bmob,某地铁公司的业务方的云卡云码SDK,这些和游戏SDK还是有些差异的,而这里的CP就是你的SDK的接入方(开发者)了,他们需要结合你写的文档接入你的SDK并测试,这点上和其他的第三方SDK倒是没什么差异,只是因为一般成熟的商业游戏都是通过cocos,unity等游戏引擎打出来的Android或iOS工程,有一些特殊的规律和坑(后文会讲到),所以接入的成本和需要提供的技术支持有的时候会多一些;

游戏SDK的功能形态

SDK的功能体现为接入方需要对接并调用SDK的功能需求,或者直接说就是Api,一般游戏SDK都有以下的功能:

  1. 账户相关

    比如接入了应用宝渠道的游戏,对于不同需要的游戏来说,这个渠道包的游戏用户其实也就是渠道SDK里面提供的游客(一键注册)或正式账号的用户,还有相关的密保,联系客服,绑定手机,实名认证等都属于账户相关的模块;

  2. 支付和充值相关

    总所周知游戏中有买道具充元宝等增值功能,SDK必须提供好支付的功能,除了Android平台上的支付宝和微信等这种主流的第三方支付渠道,也有其他的支付方式,而iOS平台的官方内购,而涉及到钱的事情也比较敏感,比如完成支付行为后要定时轮询订单,如果支付完成但是网络出错导致回调有问题出现玩家付了款但是没拿到道具或元宝的对账和补单等也是需要处理的(在iOS的官方内购就比较典型);

  3. 数据统计和上报

    在游戏中,有一些典型的用户事件(行为)是一些运营同事很需要关心的,其实这部分基本就和用户增长很有关系了,或者说前期投放了很多资源,需要统计转化率,ROI什么的,所以下面的一些行为需要事先在SDK中打点并上报:

    • 用户注册和登录,分别统计
    • 玩家创建角色,对应需要有传角事件的日志;
    • 支付,方便统计支付的转化率等;

    注意:

    这里说的统计打点其实也没必要用到无埋点之类的技术,因为一些涉及到无埋点之类的用户事件圈选基本都是较大的App的自研方案或者接入比如GrowingIO等提供的,游戏SDK这块提供的是通用(稳定,低可定制性)的事件数据上报,所以也没必要像业界一些支持App自定义事件(因为是App自定义事件,所以会多变动),或者说游戏SDK用到了这些比如热云SDK的自定义事件,但是没暴露给CP;

    除了在上报数据后,客户端也需要对应留有必要的日志,方便自查;

  4. 悬浮球功能

    显然如果玩家在玩游戏的时候如果中途需要返回到某个界面去切换账户的话是很不好的体验,所以业界的SDK都会带有悬浮球功能,你可以理解为一个挂在Window上的floatView,功能表现为进入游戏后贴边半隐藏,点击弹出类似菜单并能选择跳转到用户中心或联系客服,刚号笔者公司现在的SDK暂时没这个功能,Android和iOS平台的悬浮球功能都由笔者实现了,如果你去实现的话你也大概会遇到比如Android上悬浮球的兼容性(后换兼容的方式但是要绑定Activity的token),iOS平台的悬浮球权限比较有限,退出应用就不能显示的问题;

一些特点和规律

  1. 代码层面 SDK的接口设计不难,基本可以自己简单实现一遍,比如注册登录,角色相关等接口,至于事件的回调等都可以直接回调或者采用类似EventBus的方式,比如在反编译阿里游戏的SDK时候发现出了用到了classloader加载插件外,就是用EventBus的方式去回调事件方法了,尽管这样写起来会影响代码阅读的流程性,但因游戏SDK的特殊性,比如调用注册登录和角色日志等接口都是在固定位置的一次性调用,也无妨;
  2. 流程方面 频繁的测试打包,有的时候有点繁琐,虽然可以用脚本尽量改善;

    母包和子包

    入职第一天熟悉了公司自家平台SDK的大致代码后老大交给我做一个Google渠道的SDK,亦即用户部分用Google登录,支付用Google应用内支付,统计用AppFlyer等,当然其中也遇到了GooglePlay的一些坑,做好后联系聚合平台做测试的时候才接触到了母包和子包的概念,简单说,母包就是CP写好并打包的一个游戏包,运营或者发行的同事拿到后会用专门的打包工具去选择接入某个渠道比如Google渠道的打出一个渠道包(子包),这样的包就是最终能使用或者提交的;

    打包工具

    上文说到的母包和子包就和打包工具有很大的关系,刚开始对一些业务流程不熟悉,但还是看同事发的日志慢慢能悟出来一点东西; 比如如果我每一个游戏项目内都去手动对接并调用每个渠道的SDK的Api的话,生产效率是很低的,所以就需要有类似的工具去做到拆包打包,而自己看到打包工具的一些脚本日志也看到是用apktool做的,简单说就是把母包没接SDK的项目逆向解包并加入接入SDK的代码并重新打包就是我们需要的渠道包了;

    一些疑惑

    Android平台的事情

iOS? 怎么做到2周从0到实现一个新的SDK

这个也是我挺想总结并分享的,其实很多时候特别是在创业公司,除了能在自己的既有特定领域(比如AndroidSDK)独当一面外,还需要去应对一些突发的任务比如iOS没有人手,你需要接手或短时间快速入门并能达到上手(自己开发实现,处理突发bug)等,其实就需要较好的科班基础和快速学习新东西的能力,有了较好的这方面的能力,你可以不顾守既有存量(Android)而不惧短期内快速积累其他的增量(iOS or 其他); 起因是公司的既有的SDK(Android & iOS) 都是基于webview实现的,很多功能都在前端(js,所以平时也需要修改这部分的逻辑)实现了复用,而iOS的网游SDK因为苹果官方的审核机制急需一套新的SDK,较大区别于既有的SDK,所以想到了实现了原生的SDK,公司没有iOS的人手而我也需要短期去开发。下为大致过程:

  1. OC初探

    因为旧版SDK用Objctive-C开发,不出意外的从OC入手,简言之,OC脱胎于C也保留有一些类似struct,指针*之类的语法构造,一些基础的语言构建如数据类型有相似的地方,但从java平台切换过来都会有点不太习惯:

    • OC的动态性远比java的灵活,不仅体现在消息的动态分发,这里的消息传递你可以理解为java的对象的方法调用,比如:
    // in java
    Coder coder = new Coder();
    coder.coding();
    // in oc
    Coder *coder = [[Coder alloc]init];
    [coder coding];

而动态的消息转发亦即比如Button设置的onClick方法回调传入的仅仅是一个方法名(selector),如果没有对应的方法实现会在运行时才报错,这些都是强类型的静态语言如java所不同的,后面自己去翻了《EffecitveOC》才知OC能在运行时交换方法的实现,这些都远比java的反射灵活(一般谈及语言的灵活性或动态性都和运行时类型识别RTTI有关,注入其他的环境都会去窥探语言的runtime的黑魔法和类似的metaprograming);

  • 类,对象的写法的差异,OC中类似C都会有头文件和对应的实现文件(m),你可以把h文件理解为接口(方法声明);
  • 提供的利器block 刚开始就有一个疑惑,SDK中需要的接口回调在OC中要怎么实现呢,后面发现可以用block,关于接口回调,有的语言环境会用提供闭包实现,而block也是类似对代码块的包装,不过刚开始的写法不太适应;
  • 回调的另一种–全局Notification iOS会提供一种全局的通知,类似观察者模式,或者说是系统级的观察者模式,和Android的全局广播比较类似;
  • 其他

    特殊的category,plist文件操作,常用集合操作 KVO,delegate,归档–序列化,多线程,内存管理基础,这些基本会写之后就上写切换到iOS平台;

  1. 从OC到iOS平台 只学了OC后还需要熟悉iOS平台和框架提供的Api,比如开发界面对应的ViewController,常用UI,布局,xib之类,这些都是iOS的基础,需要在实际中学会解决bug,基础UI后能对接接口基本问题就不大了;
  2. 利其器(XCode) IDE的熟练程度或者驾驭能力直接决定了你的开发(生产)效率,所以XCode的常用操作和一些调试的技巧需要会,比如高版本的XCode对函数调用堆栈对应的方法名没有显示只是一堆16进制的内存地址,这个时候需要你懂点lldb的调试命令,比如image lookup –address定位出代码的位置等等;而对于开发iOS的需要账号,证书,p12,描述文件,打包ipa包等先达到能上手解决问题后慢慢找规律看点原理;
  3. 开发iOSSDK注意事项 到这里基本问题不大了,但是开发iOSSDK的话还需要注意SDK在iOS中表现为.framework或.a文件(类似Android中的jar或aar),还有是实现为静态库还是动态库以及考虑不同实现方式在接入时的成本,是否支持bitcode,是否同时支持arm64,armv7s,x86_64以及i386(模拟器)等架构,是否同时支持模拟器和真机,提审是否需要去掉某种cpu架构,和其他第三方SDK(如微信分享SDK)链接编译的时候会不会出现兼容问题,other linker的设置等等,这些都是和AndroidSDK开发中差异很大的,笔者觉得还是很有必要认识和理解程序build和link的过程,在遇到bug的时候相对比较淡定,build和link的知识在iOS开发就比较重要了,不像Android最多抛个ClassNotFound或者jni方法查找失败的日志,也比较好定位和解决;
  4. 文档&技术支持&对接 这些和开发Android渠道SDK都是同等重要的,只是会有所差别,
  5. 自己走过的弯路
  6. 一些处理问题的套路 你可能会说能力和处理bug的经验多了就游刃有余了吗,但是很多时候是还没经历很多bug磨练的时候遇到一些相对不好解决的bug怎么办,也许你会说找大佬呀,如果朋友大佬很忙,公司没有其他iOS同事怎么办,多少有点无为无米之炊的感觉,加上同事一直在催赶紧加快解决,怎么办?还有时间和能力去研究本质的报错,或者慢慢梳理,这个时候就需要在恰当的位置去规避或者换另一种方式解决了;比如在需要给研发的同事做一个通用的分享库的时候遇到了一个cocosJs中调用分享SDK的回调方法总会出现回调方法提前执行的过程,在多次来回和研发同事约定调用方式无果后,发现block的不太好改动,加上历史代码都是基于不规范的block写法,时间上也不太允许条分缕析去解决,山穷水尽的时候觉得我可以暂时规避block来回调,用Notification一下子就解决了,用block回调和Notification的区别大致类似于在Android中用接口回调和用EventBus的onEvent()方法,只是后者代码的位置和调用的位置分散开而已,大致如下:

虽然很多时候还是要尽量直接解决问题,但是在人手不够,经验存量有限(从0开始接触iOS才两个月),时间仓促的情况下,也可以通过这种变通的方式解决,而类似的方式你也可以在知名的SDK看到类似的影子,异曲同工。比如刚入职的时候为了能尽快了解游戏SDK的业务场景,只能先去找大厂的游戏SDK的接入文档之类,其实也比较少,找到了阿里游戏的,刚好朋友猫哥也参与过阿里游戏SDK的开发,看到了阿里游戏的一些比如注册等事件回调不是通过接口回调来做的,是注册一个全局的EventBus然后SDK负责来统一统一并调用的,也是类似的思路,下面的代码的可能比较直观点:

学iOS和开发出SDK还是比较快的,其实两周的后面几天都是调UI和简单适配有点费时间,最快的应该就是和后端同事对接接口的时间了,有参数疑惑的直接抓包后沟通一下马上就好了。

平台SDK之外的,马甲包和渠道的工作

除了平台SDK的日常功能需求和接入外,Android和iOS还有对应的马甲包SDK需要维护和开发,这套SDK的基本思路在于在加载本地游戏之前先请求后端并选择加载本地游戏或者用webview加载线上的游戏,因为缺少文档和规范的东西,也加了接入文档等,特别是iOS的马甲包中,除了需要把代码调整尽量满足运营同事的多变需求后,还需要总结接入cocos和unity打包出来的项目的快速解入方式,比如unity打包出来的项目结构是没有默认的AppDelegate的,但是是在AppController类中,因为该类都有AppDelegate(你可以理解为Android中的Application)的生命周期方法;另外就是公司的一些小游戏接入其他第三方渠道,对应的整套SDK除了在客户端接入还需要后端接入并和后端的同事对接测试,比如调用某SDK的登录后拿到openId等数据给后端,在支付环节先请求后端获取支付参数并调用渠道SDK,这个环境需要和渠道对接沟通好一些既定的参数,支付后的校验,是否成功获取正确数量的游戏道具或元宝等,而马甲包这块也需要经常提交接入马甲SDK的小游戏审核;

左手Android,右手iOS

现在的工作状态相当于同时开启两端的IDE并经常来回切换,慢慢习惯了,也会总结两者的差异,但是一些套路和解法基本都是通用的,比如在做两端的悬浮球的需求,基本都需要Window,而在Android的悬浮球实现中,因为悬浮球需和其他界面组件通信,为了降低代码的复杂性,自己手写了一个简单的EventBus内部使用,就不用依赖官方的比较占体积和可能会引起重复冲突的EventBus了;

反思&&下一步

到前段时间基本很多东西也比较顺畅了,接下来的方向和想做的除了日常的工作就是找个方向专注一点或者在业务上更加全面点或用技术去改善业务痛点,比如虽然现在的接入马甲包SDK的过程虽然能控制在20分钟内,但还是不够自动化或者流程化,在这点上,确实是比较原始的,需要进一步加深iOS的一些细节并运用起来解放双手。

大致总结8月入职以来的过程,也记下点东西,确实要总结或者记下分享,至少回头自己需要回顾,平时忙和加班也是一个小原因,但还是要从自身找原因,定时总结。