Android 编译优化探索2 Hack字节码

Posted 不会写代码的丝丽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 编译优化探索2 Hack字节码相关的知识,希望对你有一定的参考价值。

前言

承接上文:
Android 编译优化探索

再最初的版本中需要编译kotlin才能实现Hack自定义增量,但是还一个任务同样也及其耗时org.gradle.api.tasks.compile.JavaCompile,但是此任务不在kotlin插件,难道我们又要自己编译java插件?就连鄙人导师都觉得编译kotlin插件很麻烦。于是乎最后决定采用动态修改字节码的方式实现。作者使用javassist作为字节码编辑器。

JavaCompile创建流程

android工程中JavaCompile的创建是由AGP插件创建,其核心创建核心流程如下

//TaskFactoryUtils.kt

fun <T : Task> TaskContainer.registerTask(
    creationAction: TaskCreationAction<T>,
    secondaryPreConfigAction: PreConfigAction? = null,
    secondaryAction: TaskConfigAction<in T>? = null,
    secondaryProviderCallback: TaskProviderCallback<T>? = null
): TaskProvider<T> 
    val actionWrapper = TaskAction(creationAction, secondaryPreConfigAction, secondaryAction, secondaryProviderCallback)
    //调用register注册一个task,第一个参数task名字,第二个参数task类型,第三参数注入构造参数
    //这里的creationAction是JavaCompileCreationAction
    //creationAction.name 任务名字根据变体变化 比如tiya变体就为compileTiyaDebugJavaWithJavac
    //creationAction.type返回的一个class类型:JavaCompile::class.java
    return this.register(creationAction.name, creationAction.type, actionWrapper)
        .also  provider ->
            actionWrapper.postRegisterHook(provider)
        



我们最后看看这个

class JavaCompileCreationAction(
    private val variantScope: VariantScope,
    private val processAnnotationsTaskCreated: Boolean
) : TaskCreationAction<JavaCompile>() 
	//...略
	  override val name: String
        get() = variantScope.getTaskName("compile", "JavaWithJavac")

    override val type: Class<JavaCompile>
        get() = JavaCompile::class.java
	//...略

知道大致流程我就可以根据如下思想进行操作:

  1. JavaCompileCreationActiontype替换成自己的JavaCompile类即可
class JavaCompileCreationAction(
    private val variantScope: VariantScope,
    private val processAnnotationsTaskCreated: Boolean
) : TaskCreationAction<JavaCompile>() 
	//...略
	//替换为HackJavaCompile 
    override val type: Class<JavaCompile>
        get() = HackJavaCompile::class.java
	//...略

  1. 抢在gradle的默认的类加载器之前修改类并强行加载(双亲委托知识)

因次插件会在根工程之后直接启用。

  1. 处理父加载器引用子加载器的类问题
    这个问题也是比较头疼的一个地方,在起初使用jdbc那样委托当前线程的类加载器去加载,然后通过反射去调用子类函数,但是最后发现当前线程类加载器居然是一个较为顶层的类加载器。最后采用所有引用的类都交付顶层类加载器加载,并禁使用lambda表达式(编译器会创建一个新的类,并用子类加载器加载)。

最后解决办法

如以下伪代码

以上是关于Android 编译优化探索2 Hack字节码的主要内容,如果未能解决你的问题,请参考以下文章

字节码插桩Android 打包流程 | Android 中的字节码操作方式 | AOP 面向切面编程 | APT 编译时技术

字节码增强技术探索

JVM原理探索字节码指令集调用执行流程分析(语法分析篇)

Android 编译优化探索3

Android 编译优化探索3

Linux系统中编译链接的基石-ELF文件:扒开它的层层外衣,从字节码的粒度来探索