今天想和大家分享一个在编译时清理Log语句的插件。
问题描述
在平时的开发中我们经常使用安卓的Log方法来打印一些调试信息。由于有很多日志只是在开发时期有需要,所以我们一般通常都会自定义一个Logger,这个Logger只在我们的Debug包中输入日志信息,而在Release包中则不输出,大致的逻辑如下所示:
这种模式可以确保在release保重是不会打印调试信息的,但是却仍然存在性能问题,因为在调用Logge的i方法之前要先计算出实参的值,也就是说会执行:
如果以上Logger的使用发生在onDraw等调用非常频繁的方法中的话,则对性能的影响更为严重。
解决方案
其实有过c/c++开发的同学就会想到,用宏可以完美解决这个问题,但奈何java不支持宏定义。那我们有没有其他的方案来实现呢?笔者一开始想用javassist来实现,可是查了一圈资料之后发现javassist并没有符合我们需求的api。最终确定的方案是,在gradle插件运行java compile任务之前对java源码进行扫描,并删除以”Log.”和”Logger.”开头的行。大致逻辑如下所示:
处理逻辑已经实现了,那么我们在什么时候执行删除操作呢?在安卓的编译过程中有一个compile${variantName}JavaWithJavac,我们可以在该方法之前执行:
通过以上的代码,我们则可以将源代码中的Logger语句移除了。但是,我们可以直接操作将源代码中的Logger语句移除吗?不行的,我们应该先将源代码复制到一个临时目录中,然后将这些临时目录中的源码中的Logger移除,最后将compile${variantName}JavaWithJavac 任务的输入目录改为这个临时目录:
至此,我们便能真正实现我们最初的目的,移除java文件中的Logger了,大家可以尝试移除kotlin文件中的Logger。
总结
文中的源码可以参考Delogger。笔者希望本文中使用的预处理思路可以起到抛砖引玉的作用,大家可以头脑风暴一下这种预处理方式的其他使用场景。