看到有网友看到Shadow的开源公告和Github主页的README之后,觉得都是报喜不报忧的介绍。我们也多少同意这个看法。如果开源一个项目,并不指望其他人能真正使用,也不期待收获网友们回馈的代码贡献,那么报喜不报忧问题不大。但是Shadow开源的目的不是单纯的吹嘘自己的技术多厉害,给大家随便看看代码片段。我们是真的希望大家能用起来,未来我们的业务和大家的业务用的Shadow是一样的,没有任何区别。这样大家能在插件技术上直接获得经过我们业务大量实践的高质量SDK,我们也有机会直接获得大家贡献的代码,减轻我们维护Shadow的负担。因此,我们好好谈谈Shadow的缺点。
功能实现不完整
Shadow采用了用一个壳子代理转调插件组件的技术手段实现组件生命周期。比如Activity的各种生命周期都需要壳子代理Activity收到系统调用后转调插件的Activity。同时,反过来插件Activity想调用的父类方法,比如getIntent()
方法,也需要通过中间件层ShadowActivity
转调回壳子Activity。因此理论上,ShadowActivity
就应该完整复制系统Activity
类的所有方法,大约有200个左右吧。
不像其他插件框架没有插件框架本身动态化的设计,Shadow的插件框架实现代码也是插件包的一部分,是可以动态更新的。比如ShadowActivity
这个类处于runtime
模块,也属于插件框架本身实现,我们的业务假设当前没有使用getIntent()
方法,则同这个业务当前版本一起发布的Shadow实现就不用实现getIntent()
方法。当下一个版本的业务需要用这个方法时,插件框架实现也可以补充这个方法的实现,随业务插件的新版本一同发布。
基于上述原因,Shadow的功能实现很不完整,只满足了我们自身业务的需求。不过请放心的是,我们的业务也比较复杂了,大部分常用的功能我们已经实现了,类似的没有实现的方法,大部分也只需要简单转调即可,非常容易实现。我们之所以没有真的去实现,主要原因是我们的业务不需要这些方法,所以我们实现了也不能保证质量。
目前Shadow开源的代码中所有的功能应该只有ContentProvider是我们业务中没有用到的。确切的说,只使用了宿主的FileProvider获取照相文件,插件自身没有提供任何ContentProvider。ContentProvider的实现确实是为了让Shadow的功能对齐其他插件框架而实现的。
Fragment代码调试变麻烦了
Fragment的具体实现方案和原因在这里先不讲,总是我们由于坚持不使用任何Hack手段实现,包括在编译期也坚持不Hack官方的构建流程,导致我们最后只能选择将插件中原本的Fragment类名改名为在原本名字后面加一个下划线。比如业务插件里有一个com.xx.GiftFragment
类,实际运行时这个类的名字就变成了com.xx.GiftFragment_
。这就导致在com.xx.GiftFragment
的源码上打断点是断不下来的。必须在程序运行起来之后,用IDE的重命名功能把它改名为com.xx.GiftFragment_
,使得源码和运行时类名字一致才能断点。
这个设计能不能改进,我们是反复考虑了没有更好的办法的。但是如果Hack构建过程,确实可以避免这个问题。
全动态设计使版本控制更复杂了
这实际上是优点带来的负面问题,在Shadow的设计中有3个部分:host
,manager
,plugin
,这3个部分是分别发布版本的。而其他没有全动态设计的插件框架只有host
和plugin
两部分。这些部分之间的版本关系都是多对多的关系,所以版本管理变得更复杂了。这部分版本的管理,在我们的实现中是一个依赖于腾讯内部后台框架的后台服务,因此没能在这次开源中带出来。Shadow开源的代码目前是没有包括插件下载和版本检查实现的。manager只实现了下载插件之后的安装逻辑,也包括升级功能。
自动化测试用例比较少
我们的业务开发模式其实是不要求对SDK进行测试的,我们有强大的测试团队会对业务功能进行严格测试。这可以保证Shadow就算有Bug也不会出现在我们现有业务的场景中。但是我们也知道其实插件框架中的实现很多都是非常相关的,对插件框架中一处小小的改动,确实可能会影响很多看似不相关的功能。所以自动化测试对于插件框架来说其实是很重要的,这我们有意识。所以,即便是我们见过的其他插件框架都没有配套的自动化测试,我们还是坚持做了自动化测试。可惜的是由于人力有限,用例还非常少。我们尽量希望未来做出的改动都配套实现自动化测试。
没有多插件的Sample
我们的业务中对Shadow的应用实际上复杂程度远超Sample。我们的一个业务实际上是由多个插件包构成的,这些插件包之间存在依赖关系,不光有类依赖,还有资源依赖。通过一个复杂的manager实现,让业务逻辑可以通知manager在需要的时候才下载一些功能插件,做到插件的懒加载。
这些多插件相关的功能已经在开源的Shadow代码中支持了,但是我们一直没有精力再写一个复杂的Sample演示这些功能。
插件管理的Manager实现在卸载插件方面欠缺
实际上代码写了卸载插件的功能。但是我们的业务对卸载插件需求不强烈,所以我们更重视更新插件,而对旧插件处理不多。还有一个原因是旧插件有可能在运行中,也不能贸然删除。对于卸载的实现,我们是有讨论过和设计的,但是一直没能实现和验证。
自己很难发现自己的缺点,希望大家指正
我们真的是很有诚意,希望大家能知根知底的了解Shadow的缺点。但人自己真的很难发现自己的所有缺点。所以如果大家认为Shadow还有什么缺点,欢迎大家提Issue,我们共同改进。