脱离 Gradle 环境运行
在平常的开发过程中,我们可能需要脱离 Gradle 环境,对某些个 JAR、 class 文件或者是 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
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
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
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
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
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
2
3
4
5
6
7
扫描 class 文件
val klass = File("Some.class").asClassNode()
println(klass.name)
1
2
2