关于Android的插件化方案,市面上其实是有很多成熟的解决方案的,比如谷歌的AAB、百度的Qigsaw等等。但是我们在使用这些方案的时候,只要使用到AAPT2打包,都会遇到一个问题,就是插件包的资源ID和宿主的资源ID冲突的问题,如果插件工程和宿主工程本身就是在一起的就其实还好,基本不会有这个问题。更多的问题来自于那种子工程和宿主工程分开来,在两个仓库里,这种就比较棘手。比如子工程是一个独立的模块,然后这个模块又带了插件化的能力,子模块的插件打包环境和宿主的打包环境是完全隔离的,谁知道谁把哪个ID设置为2046951429了,所以就有可能造成不同的工程资源使用了同一个资源ID。比如宿主有一个资源ID是2046951429,而子工程也有一个id也是2046951429,那就有可能宿主的页面拿到了子工程的资源,导致各种不可预知的问题。

关于解决办法的话,之前说过两个工程打包环境完全隔离,如果你可以做PP字段的控制最好,但是大多数情况是没办法做控制的,所以最好有一个自动化的方案,可以设定一个PP字段的偏移量,尽可能选一个比较生僻的偏移量和别的工程错开。

下面说下本人"投机取巧"的一个方案吧,大概看了下AAPT2对插件包的资源ID的偏移逻辑,发现其实是按照顺序偏移的,第一个插件的PP就是0x7e、第二个就是0x7d、第三个就是0x7c.......所以如果我们用假的工程占位,其实就能达到我们要的效果。

1.首先在gradle.properties文件中声明你要的PP偏移量,这里是10

PLUGIN_RESOURCE_PKG_OFFSET = 10

2.在子工程里预置一个假插件工程plugin-place/p1,只包含一个AndroidManifest文件

android 资源 插件化 android插件化资源冲突_android studio

 3.接着在setting.gradle中声明占位逻辑

import com.android.utils.FileUtils    
for(int i = 2; i < PLUGIN_RESOURCE_PKG_OFFSET.toInteger(); i++){
        File place = new File("./plugin-place/copy/p" + i)
        if(place.exists()){
            FileUtils.deleteDirectoryContents(place)
        }
        FileUtils.copyDirectory(new File("./plugin-place/p1"), place)
        FileUtils.writeToFile(new File(("./plugin-place/copy/p" + i + "/src/main/AndroidManifest.xml")),
                            "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
                            "    package=\"com.projectname.place" + i + "\"" + " >\n" +
                            "</manifest>")
        include (':plugin_p' + i)
        project(':plugin_p' + i).projectDir = place
    }
include (':plugin_p1')
project(':plugin_p1').projectDir = new File("./plugin-place/p1")

 4.最后声明假的插件工程即可

android 资源 插件化 android插件化资源冲突_移动开发_02

 接着正常打包即可!