最近聊到一种编译优化的方案,号称秒级别:
- 每次拿到修改 java 文件,编译为 class;
- 将 1 中的 class 转成 dex;
- push 到 sdcard,然后重启生效;
问题来了:
- 第一步中将特定的 Java 文件转成 class 如何操作?如何极快的操作?
- 如果是 kotlin 文件呢?
更多问答 >>
-
每日一问 | 我们经常说到的 Android 脱糖指的是什么?
2021-07-11 22:06 -
每日一问 | ViewModel 在什么情况下的「销毁重建」能够对数据进行无缝恢复?
2021-08-25 18:11 -
每日一问 | 关于 Activity 重建,值得探究的几个问题
2021-08-30 21:37 -
每日一问 | 好奇ActivityThread中为什么会有一个 Application的集合?
2021-08-30 21:36 -
每日一问 | Gson中序列化对象的操作有低侵入的优化方案吗?
2021-12-02 00:50 -
每日一问 | Dialog 的构造方法的 context 必须传入 Activity吗?
2021-07-11 22:06 -
2021-05-28 00:29
-
每日一问 | 已经有了 Intent,那为啥还要 PendingIntent?
2021-05-28 00:29 -
每日一问 | view.requestLayout如果在灭屏或者切home之后调用会怎么样?
2021-05-06 00:16 -
2021-05-06 00:16
弄好了,哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
现在只做两件事:1.找到有改动的java文件;2.通过JavaCompile这个类来编译这些java文件。测试了一下,耗时在2秒内,比compileDebugJavaWithJavac还要快得多得多得多多多d多多不过代码还没封装,明天我演示一下暂时只支持编译主module的java文件,kotlin的还没研究。多个module要生成多次inputChanges(代码还没写)也可以下载代码来看看:compile-test-master.zip代码同步后需要先assembleDebug
一次,然后试着随便改一下app module下的java文件(新增删除都可以),并找到IncrementalJavaCompile
这个task运行就ok了,在终端可以看到输出的信息。编译完打开项目下的"app/build/intermediates/javac/debug/classes/包名"
目录,就能看到本次编译的结果了现在是以buildSrc的形式来实现的,到时也可以换成其他方式。插件已做好:https://github.com/wuyr/incremental-compiler
现在加入了多module支持。(我发现好像不需要预先assembleDebug一次也可以正常编译?)
现在kotlin文件也可以编译了。我看看能不能脱离【预先执行一次assembleDebug】这个步骤
一开始我有个疑惑:如果同时新增了多个.java和.kt文件,并且他们之间互相都有依赖的话,那么在编译的时候到底是先编译谁呢?
debug了一遍compileDebugKotlin这个task之后发现,kotlin是优先编译的,不论谁依赖了谁,所以我在buildSrc中的做法也是参考compileDebugKotlin,优先编 ...查看更多
debug了一遍compileDebugKotlin这个task之后发现,kotlin是优先编译的,不论谁依赖了谁,所以我在buildSrc中的做法也是参考compileDebugKotlin,优先编译kotlin,再到java
现在已经可以直接生成增量的dex了(只包含本次编译的class),只是首次编译仍需执行一次assembleDebug(不是每次生成增量dex都执行,是每次clean project之后执行一次即可), ...查看更多
现在已经可以直接生成增量的dex了(只包含本次编译的class),只是首次编译仍需执行一次assembleDebug(不是每次生成增量dex都执行,是每次clean project之后执行一次即可),这一步的主要作用不是跟编译依赖项相关,而是和编译之后的文件指纹保存有关,如果现在直接去掉这一步,也是能够正常编译的,但是保存不了编译记录,也就是下次编译还会把全部源文件都编译一次。现在主要遇到的问题是不知道创建ExecutionHistoryStore对象所需的那几个参数是如何生成的,如果找到了生成参数对象的方法的话,就可以跳过这一步
现在已经脱离了对assembleDebug的依赖,能自己创建ExecutionHistoryStore对象并保存编译记录了,但是这样又带来了一个新的问题:R文件没有生成。。。。所以编译到依赖R的源文件 ...查看更多
现在已经脱离了对assembleDebug的依赖,能自己创建ExecutionHistoryStore对象并保存编译记录了,但是这样又带来了一个新的问题:R文件没有生成。。。。所以编译到依赖R的源文件时就会报错。我现在的想法是,如果检测到没有R文件的话,就直接依赖生成R的那个Task算了,应该不会太耗时
ok了,完美解决。现在只剩下最后一步:合并各个module所生成的增量dex(如果有的话)
现在的做法是,检测到没有上次保存的编译记录,就会生成一次R文件,library会依赖generateDebugRFile这个task,app module依赖processDebugResources ...查看更多
现在的做法是,检测到没有上次保存的编译记录,就会生成一次R文件,library会依赖generateDebugRFile这个task,app module依赖processDebugResources
坐等大佬插件
插件已做好:https://github.com/wuyr/incremental-compiler
大佬,新增内部类的增量情况,有什么思路吗
一样能正常编译的啊,这个不影响的
最新发现:
IDEA Plugin的SDK居然有提供编译指定文件的api(这是前段时间在看生成ASM代码的那个插件的代码时偶然发现的):哈哈哈哈哈哈哈哈,找到
那现在就剩下生成当前源文件指纹这一步了compileDebugJavaWithJavac
Task上一次保存的参与编译的源文件对象了!!!汇报一下进度:
研究了assembleDebug的增量编译的大致原理(Java),发现它是通过JavaCompile的compile
方法来完成对.java文件的编译的,这个方法有个叫inputChanges
的参数,它描述了哪些文件在上一次编译后有改动,实现增量编译最重要就是这个参数了,其他比如classpath
、destinationDir
、sourceCompatibility
这些都能在Task创建之后拿到。那它是怎么知道哪些文件有改动的呢?是这样的,gradle在每次编译完成之后,都会把本次参与编译的源文件的指纹(md5sum)记录在executionHistory.bin
文件里 ,如图(如果是第一次编译,那就是全部源文件了,后面每次只保存有变更的,因为没变更的不需要参与编译,它的md5也不会变):现在如果是从自定义的plugin中读取出这些文件指纹,再对比当前源文件的指纹生成JavaCompile所需要的
inputChanges
对象,还有点复杂(gradle源码太难看了),不过我试过,在调用JavaCompile.compile
时,inputChanges
传null,然后标明为全量编译,但source只给一个.java文件,最终就只会编译出一个class,所以JavaCompile里面的增量编译和全量编译,只是inputFiles
不同(JavaCompile在编译前会根据inputChanges
所描述的文件来从source中过滤出不需要参与编译的多余文件),最终执行的流程都是一样的:现在有两条路,一条是继续死磕源码,最终用gradle的api来获取
inputChanges
对象。第二条是在gradle插件中定义一个task依赖assembleDebug
,在每次assembleDebug
执行完成之后,自己再计算一次source的hash存在临时文件里(当然了,用修改日期的方式也可以,只是有时候会误判导致重复编译),这样就可以在另外一个独立的task里只做编译.java文件这一步了(其他什么事情都不做)。这样的话,如果只改动几个文件,我估计编译时间不会超过10秒(assembleDebug
其实除了编译打包还做了很多其他事情的,在很多时候都没有必要,浪费时间)。先睡了,明晚再看。
这汇报时间太狠了
666,那个视频播放器看起来也好好玩~~
😭
播放器插件上周在mac上测试,发现卡成PPT了😭后面发现是屏幕分辨率的原因,本来也打算假期一起改了的,结果拖到了现在还没改😭
有个插件,java2smali
不行啊,你这样一部到位了,那很多字节码的插件就没办法生效了吧
然后 smail 在反向生成 dex,这个过程有语法校验么
我没用过字节插桩的技术,也没看过原理,不了解现有的插桩技术怎么实现的。但是直接在smali里插桩不是比较好的方案么,在smali里插桩之后崩溃看日志连行号都对得上。 ...查看更多
我没用过字节插桩的技术,也没看过原理,不了解现有的插桩技术怎么实现的。但是直接在smali里插桩不是比较好的方案么,在smali里插桩之后崩溃看日志连行号都对得上。
我没理解错的话,这里的校验是指的生成的时候写错语法会不会报错吧。如果写错了语法的话,生成不了dex。 说到极致编译优化,在这里我想声嘶力竭地呼喊一句,项目真的没必要分那么多module,modul ...查看更多
我没理解错的话,这里的校验是指的生成的时候写错语法会不会报错吧。如果写错了语法的话,生成不了dex。 说到极致编译优化,在这里我想声嘶力竭地呼喊一句,项目真的没必要分那么多module,module越多编译越慢,所以优化的一个方向可以是合并module。针对已经非常多的module了,引用module的成果物也是一个优化方向,比如负责这个模块的人写好之后,将成果物传到仓库其他人来引用,这样也会快很多。 现在说的这种情况,和淘宝天天发我优化优化哪哪了有什么区别呢,还有比淘宝更卡的App了么。制造出一大堆问题然后去优化这叫填坑,一个百十兆的小应用怎么搞的编译要编译那么久这不是问题所在吗?少在gradle里写一些花里胡哨的操作就是最好的优化
smile插装有个问题就是,现阶段没有机器成熟的库,而class插装,现在有很网上的库asm,因为java出来20多年了,所以class修改的库要成熟些,理论上Android上肯定是smile插桩更适 ...查看更多
smile插装有个问题就是,现阶段没有机器成熟的库,而class插装,现在有很网上的库asm,因为java出来20多年了,所以class修改的库要成熟些,理论上Android上肯定是smile插桩更适合些
群主估计是想在javac的时候跳过一些语法之类的验证,因为as的检测功能,语法问题本来就直接爆红,正常情况下去编译的时候语法肯定是没问题了,如果javac的时候能不检查代码的语法,理论上应该会快一些, ...查看更多
群主估计是想在javac的时候跳过一些语法之类的验证,因为as的检测功能,语法问题本来就直接爆红,正常情况下去编译的时候语法肯定是没问题了,如果javac的时候能不检查代码的语法,理论上应该会快一些,但是不知道javac具体是咋搞的