脱离 Gradle 环境运行

在平常的开发过程中,我们可能需要脱离 Gradle 环境,对某些个 JARclass 文件或者是 Android Transform Pipeline 的产物进行扫描来得到一些结果,鉴于此,Booster 提供了一系列实用工具类和扩展方法,来帮助开发者提升效率:

运行时注入

一般在 Java 环境中,可能会需要在运行时修改某些特定的 Class ,我们可以通过 Transformer 很容易的实现。

自定义 ClassLoader

class TransformerClassLoader : URLClassLoader {

    private val transformer: Transformer

    constructor(
            delegate: URLClassLoader,
            factory: (ClassLoader) -> Transformer
    ) : super(delegate.urLs) {
        this.transformer = factory(this)
    }

    constructor(
            delegate: URLClassLoader,
            factory: (ClassLoader, Iterable<Transformer>) -> Transformer,
            vararg transformer: Transformer
    ) : super(delegate.urLs) {
        this.transformer = factory(this, transformer.asIterable())
    }

    private val classpath: Collection<File> by lazy {
        this.urLs.map { File(it.path) }
    }

    private val context: TransformContext by lazy {
        object : AbstractTransformContext(javaClass.name, javaClass.name, emptyList(), classpath, classpath) {}
    }

    override fun findClass(name: String): Class<*> {
        val bytecode = transformer.run {
            try {
                onPreTransform(context)
                getResourceAsStream("${name.replace('.', '/')}.class")?.use(InputStream::readBytes)?.let {
                    transform(context, it)
                } ?: throw IOException("Read class $name failed")
            } finally {
                onPostTransform(context)
            }
        }

        return defineClass(name, bytecode, 0, bytecode.size)
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

使用 ASM

val delegate = Thread.currentThread().contextClassLoader as URLClassLoader
val tcl = TransformerClassLoader(delegate) {
    AsmTransformer(it)
}
Class.forName("io.johnsonlee.booster.SimpleClass", tcl)
1
2
3
4
5

使用 Javassist

val delegate = Thread.currentThread().contextClassLoader as URLClassLoader
val tcl = TransformerClassLoader(delegate) {
    JavassistTransformer(it)
}
Class.forName("io.johnsonlee.booster.SimpleClass", tcl)
1
2
3
4
5

扫描 Android Transform Pipeline 的产物

通过 TransformHelper在新窗口打开,我们就可以很方便对 Android Transform Pipeline 的产物进行扫描:

val variant = "debug"
val input = File("build").file("intermediates", "transforms", "booster", variant)
val output = File(System.getProperty("java.io.tmpdir"))

TransformHelper(input).transform(output, AsmTransformer(object : ClassTransformer {
    override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
        println(klass.name)
        return klass
    }
}))
1
2
3
4
5
6
7
8
9
10

扫描 JAR 文件

通过上面提供的扩展方法,我们可以很方便的扫描 JAR 文件中的 class

File("some.jar").transform(File("out")) { bytecode ->
    val klass = bytecode.asClassNode()
    println(klass.name)
    bytecode
}
1
2
3
4
5

或者

JarFile("some.jar").use { jar ->
    jar.entries().iterator().forEach { entry ->
        jar.transform(entry.name) { klass ->
            println(klass.name)
        }
    }
}
1
2
3
4
5
6
7

扫描 class 文件

val klass = File("Some.class").asClassNode()
println(klass.name)
1
2