U8SDK——Application继承关系的终极解决方案

作者: 分类: U8SDK 发布时间: 2018-01-18 15:20 6U8SDK——Application继承关系的终极解决方案已关闭评论

熟悉Android应用开发的同学应该知道, 一般每个Android应用程序都有一个Application类, 整个app进程里面只有该类的一个实例存在。同时,该类的生命周期函数onCreate等函数的执行要早于Activity组件的。那么,很多渠道SDK或者插件开发者,在设计开发SDK的时候,经常会定义一个Application的子类,然后在生命周期函数onCreate中做一些初始化的工作,还有因为该类是单进程中唯一实例, 也会将一些全局的变量放在这里。

所以,我们在接渠道SDK或者插件的时候, 经常遇到需要定义一个Application来继承他们的某个XXXApplication。 这样看, 似乎设计合理,没啥毛病。 但是,实际情况,我们还需要结合当前国内游戏的现状来看。

国内渠道平台保守估计几百家,算上其他功能插件(比如统计、分享、推送,热更等), 估计上千家不在话下。 那么一般情况下,对于单款游戏来说, 可能接入的SDK数量,在几十家左右。这几十家的SDK中, 有上面这个要求的,可能在10家左右。但是, Java语言中, 我们知道类是单继承的, 也就是如果M渠道要求你定义一个Application继承MApplication, N渠道要求你定义一个Application继承NApplication。 那么作为游戏研发或者渠道SDK接入同学, 你如何来解决这样的需求悖论呢? 以前,我们自己直接在游戏中接入每个渠道, 可能会将游戏母工程独立出来, 然后每个渠道一个接入工程,并引用游戏母工程,然后在每个渠道接入工程里面定义该Application,并做一些其他配置,然后每个渠道再单独打包。 但是,这样仅仅解决了上面不同渠道的这个需求。 很多时候, 游戏除了接入渠道, 还同时需要接入统计、推送、分享等插件SDK。 那如果这些插件SDK中也有类似需求的话, 那只有两条路可以走了。 要么让其中一方修改SDK, 要么就是放弃其中一个SDK,另寻其他。

而在U8SDK框架中, 为了将渠道SDK和插件SDK的接入完全和游戏工程分开,达到渠道SDK接入的维护性和复用,不用每个游戏再去接入这些SDK,我们采用了反编译母包,动态合并资源的方式进行渠道打包。 对于上面Application的业务情形,我们采用了代理的方案【具体方案参考这篇文档或者博客】。

但是这种方案, 并没有完全解决上面的问题。如果SDK要求继承XXXApplication, 同时SDK代码中有对XXXApplication做强制类型转换的话, 那么显然这种方式是无效的。强制转换的代码类似如下:


因为通过上面u8的默认方案, 最终activity.getApplication()获取到的是U8Application,U8Application是没有继承XXXApplication的, 配置的proxyApplication是在U8Application中被调用的。 所以SDK中有直接的强制类型转换的话,这种做法就行不通了。

那么对于上面这种情况, 我们又引用了第二种方案, 想必同学看到上面的情况, 已经有了这个想法, 那我让U8Application直接继承这个XXXApplication不就可以了吗? 道理是这样, 但是U8Application是抽象层中的类, 抽象层是不会和任何渠道代码有耦合的。 所以, 这种方式不通, 但是思路是对的, 只是我们换一种处理方式。

我们定义一个XXXProxyApplication继承XXXApplication(注意:这个时候不再实现IApplicaitonListener接口了,而是直接继承渠道的XXXApplication)。 但是这样的话, 那U8Application怎么和XXXProxyApplication共存呢, 这个就是我们在XXXProxyApplication的生命周期函数中, 调用U8Application中调用的api。 然后通过该渠道的打包自定义脚本, 将AndroidManifest.xml中application节点的android:name设置为XXXProxyApplication。这种方式相当于让U8Application继承了XXXApplication。

这两种方式组合起来基本可以应付80%的情形了。 但是上面分析到的几个点,依然无法解决。 比如如果同时接了第三方统计插件也要求继承他们的MMMApplication, 再比如游戏母包里面, 有自己的Application,他们是直接继承了U8Application, 那么我直接通过自定义脚本将application换成XXXProxyApplication,会导致游戏自己的Application失效。

那到底有没有十全十美的解决方案来兼容上面所说的所有情况呢?十全十美的方案?

仔细想想,天无绝人之路: 其实, 不管我同时打进去多少SDK, 不管里面有多少个MMApplication还是PApplication, 我只要修改他们的继承关系,让他们改祖认宗即可。如下图:

application_ext

通过上图可以看到, 要想让上面三个部分的application变成单继承,我们需要想办法,修改这些Application类的继承关系。 上面说了, 如果能够要求渠道或者SDK提供方修改实现方式可行的话,这里就没有讨论的必要了,所以,我们还是另寻他路。

了解过U8SDK打包工具原理的同学应该知道, 母包apk、渠道sdk以及插件的jar包,在打包的过程中, 会生成为对应的smali文件(smali文件是啥,可以自行百度google了解)。那么, 如果我们想让这些Application类改祖认宗,我们可能的方式,就是在这里进行操作了。

所以,接下来,我们的思路很简单: 打包的时候, 合并渠道和插件代码是一步步执行的。 执行顺序是, 先合并插件, 再合并渠道。渠道和插件的自定义脚本执行也是这个顺序。 所以, 我们只需要在各自的自定义脚本类中, 将当前AndroidManifest.xml中第一级Application(就是他直接继承了android系统的Application类)继承的父类改为这个SDK要求的XXXApplication即可。如下图示意:

application_change

这样打包工具顺序执行下来, 最终每个SDK的Application都会合并到最终的Application继承树里面。 所以,剩下的问题, 就是怎么写逻辑了。 逻辑还是比较简单, 先解析AndroidManifest.xml中当前的Application,找到他的第一级父类, 然后定位该父类的smali文件, 然后将smali文件中该类继承的Application改为我们指定的SDK的Application类。 我们直接定义一个辅助类application_helper.py, 直接上python代码:


这样, 在有application需求的渠道SDK或者插件SDK中,我们不再需要像上面一样写ProxyApplication也无需写一个类去继承XXXApplication了, 直接在渠道或者插件的自定义脚本中, 这样调用即可完成application类的继承:

好了,有兴趣的同学可以试验一下, 目前不排除上面代码有BUG或者没有想到的点, 欢迎指正并加入U8SDK技术交流群:207609068

本文出自 U8SDK技术博客,转载时请注明出处及相应链接。

本文永久链接: http://www.uustory.com/?p=2195

评论功能已经关闭,请加入U8SDK技术群进行讨论和咨询:207609068
Ɣ回顶部
U8SDK技术群 x
技术同学请加入
点击加入