Shadow为何要求插件和宿主包名一致

缘由

咱们过去也用过基于各类反射实现的插件框架,用了3年左右时间,也维护了3年左右时间。在过去维护的经验中,咱们就发现了插件使用单独包名(ApplicationId,下同)带来的问题。c#

ApplicationId通常是在build.gradle中设置的,在编译时这个字符串会被记录在2个位置。第1是记录在应用的AndroidManifest.xml中,第2是记录在应用的resources.arsc文件中。框架

记录在AndroidManifest.xml中的包名主要用来构造应用的Context对象。在咱们开发中通常经过context.getPackageName()方法得到到当前应用设置的ApplicationId。可是重要的是,系统也会经过context获取包名来识别context来自于哪一个安装的应用。咱们知道系统不容许安装多个相同ApplicationId的应用,这也是由于系统就是根据这个包名来区分安装的应用的。经过这个ApplicationId,系统也能够反向查找到应用安装在系统中的apk文件路径。ide

问题在于系统不是很是简单的和咱们同样只会调用context.getPackageName()方法得到应用的ApplicationId。还会调用一些私有API获取,例如getOpPackageName()方法。因此咱们过去以Hack方式实现的插件框架中,只能不停的兼容各类OEM系统、各类Android版本,在Override了这些获取PackageName的方法后,经过抛一个Throwable,查询当前调用来自哪里。若是来自于系统,咱们就会返回给系统宿主的包名。不然系统就会拿到一个没有安装的包名,抛出SecurityException,形成Crash。gradle

在设计Shadow时,咱们坚持一个原则来避免使用私有API,就是经过一层中间件将插件代码变成宿主代码的一部分。实际上这个过程是能够经过手工将中间件和插件代码都写在宿主中达到相同效果的。因此在这个设计中,插件代码实际上就是宿主代码的一部分。既然是一部分,ApplicationId怎么会不同呢?因此要求插件和宿主的ApplicationId保持一致,就永远不会将插件代码没有安装这件事暴露给系统。ui

记录在resources.arsc的ApplicationId其实算一个小问题了。Resources对象上有一些API是接收包名做为参数的,若是这个包名在独立安装和插件环境下动态获取的不同,那么有可能形成这些API失效,找不到想要的资源。插件

应对措施

Shadow代码中并无什么对包名的特殊处理逻辑,只有一处检查包名是否一致的逻辑(com.tencent.shadow.core.loader.blocs.ParsePluginApkBloc#parse)。去掉这段逻辑大部分状况下插件也是能够运行的。只有在一些OEM手机的特殊场景会出问题,好比一些国产手机系统的WebView或者输入栏中长按弹出菜单就有可能会Crash。因此去掉了这个限制后,就须要不停地去兼容各类OEM系统。这也不是不可行,由于Shadow有全动态的设计,插件框架的兼容代码也能够动态更新。设计

可是咱们认为更合理的方法仍是保持插件和宿主包名一致。只须要有一套完善的自动构建CI/CD,针对不一样渠道自动修改ApplicationId,编译出ApplicationId不一样的插件包去分发也不是很难的事情。关键是,这件事看起来麻烦,实际上长期来看不须要人工干预。而不停地兼容OEM系统,则须要长期投入人力人工分析解决。code

因此,建议你们保持插件和宿主包名一致。xml

相关文章
相关标签/搜索