美文网首页
编译时动态修改xml内容探索

编译时动态修改xml内容探索

作者: X_Sation | 来源:发表于2019-10-13 17:15 被阅读0次

背景:想在编译时按照配置文件修改xml中的特定字符串

android编译时,会在mergeResource任务中把三方依赖库、依赖module、自己的资源进行合并、汇总,最后保存到app/build/intermediates/res/merged/中,processResource的时候,从这个目录中拿数据,进行打包、链接等操作

AAPT时代

aapt时代,mergeResource之后,会在app/build/intermediates/res/merged/中暂存资源,xml文件也以xml的形式直接保存。只需要编写gradle插件,在mergeResource之后修改这些xml即可。processResource任务执行时,已经拿到的是修改后的xml

AAPT2时代

aapt2时代,mergeResource之后会在app/build/intermediates/res/merged/中暂存资源,但是这些资源也不再是普通的xml等文件,而是经过AAPT2编译后的二进制flat文件,这些文件修改起来没有xml那么简单。

开启了aapt2该怎么办

mergeResource负责把各个依赖的资源汇总出来,放到一起,processResource中会进行执行aapt2 link,所以只要在link之前,把app/build/intermediates/res/merged/中的flat修改/替换成我们要的文件即可

方案1

修改aapt2源码,让他在compile的时候做处理。(可行吗?类似atlas aapt)

方案2

解析flat二进制文件,然后重新写入(貌似是比较通用的方案)

方案3

debug了mergeResource源码发现mergeResource之后,会在app/build/intermediates/incremental/mergeReleaseResources/merger.xml生成一个文件,存放了资源情况,里面记录了所有资源的本地路径(在电脑中的)、所属包等情况。那么就可以解析这个文件,把资源copy一份,然后修改。最后把这些资源用aapt2进行一次主动编译,覆盖打包自动生成的那些flat文件即可。(看着可行,具体需要实践)

Debug Code

贴几个点,具体还是自己走一遍代码,才会有深刻的印象。

  • 给MergeResources的doFullTaskAction打一个断点,这里必会走
for (ResourceSet resourceSet : resourceSets) {
    resourceSet.loadFromFiles(getILogger());
    //里面把各个文件解析一下,比如items类的解析成item,创建成ResourceFile,layout类的不解析。最后要checkItems检测是否重复
    merger.addDataSet(resourceSet);
}
  • ResourceSet中读取每一个xml文件createResourceFile,创建成ResourceFile
    根据type来,列入values.xml这种文件需要解析成item进行合并。xxx.layout这种不用解析
    ResourceSet中processNewResourceFile应该会把每个item添加到mItems中
for (String dataItemKey : dataItemKeys) {
    //遍历这些item
    //如果是style开头的需要merge
    //这里竟然用到了goto语句,牛逼。。。
    setLoop: for (int i = mDataSets.size() - 1 ; i >= 0 ; i--) {
            break setLoop;
    }
}
  • writeAdditionalData方法会把所有的资源写在一起 ,跟标签是merge,包括item、style、layout、attr、图片。文件类型、来源等等。主要是item类型会罗列出来,文件类型如layout、图片等,形成file节点,存放在/rootProject/app/build/intermediates/incremental/mergeDebugResources/merger.xml
  • 以上,mergeResource就结束了

  • 断点过程中观察app/build/intermediates/res/merged/目录,发现走到doFullTaskAction的最后一行了,目录还是空的,但是放开之后,flat文件就生成了。直接追代码还不好追。
  • 在mergeResource的doFullTaskAction方法中断点观察路径,发现destinationDir就是merged目录,那想办法看这个目录在哪用了
  • MergedResourceWriter#mRootFolder就是那个目录,Writer中搜rootFolder,发现构造了一个CompileResourceRequest。
  • mResourceCompiler.submitCompile(request);构造好之后,还进行了submit。
  • 给这里打断点,跑程序,发现确实这里for循环了所有的资源,说明这是编译的入口。
  • 直接点源码进去WorkerExecutorResourceCompilationService,发现添加到requests集合中了,一看就是一个生产者消费者模式。
  • WorkerExecutorResourceCompilationService有一个成员变量workerExecutor,顾名思义,执行者
  • 点进去,果然有submit方法,可能他的实现,用了futures,执行编译任务。那具体的实现在哪呢
  • 看WorkerExecutorResourceCompilationService的注释Resource compilation service built on top of a Aapt2Daemon and Gradle Worker Executors.
  • 搜Aapt2Daemon,是一个守护进程,看方法就知道,编译操作都在这里完成。
  • 之后的源码都没法直接点,as上scroll from source,滚到这个文件处,发现aapt2编译相关源码都在这个包,那就好找了。
  • 最后附上一张图,编译就是执行这个命令


    image.png

以上用了将近半天的时间,收获不小,也了解了一些编译细节。debug源码还是很重要,相对读源码来说,要简单多了,要经常实践。如有错误,欢迎指正。

相关文章

网友评论

      本文标题:编译时动态修改xml内容探索

      本文链接:https://www.haomeiwen.com/subject/ktfkmctx.html