Standalone Transformer
In daily development, we may need to scan certain JAR files, class files, or artifacts from the Android Transform Pipeline outside of the Gradle environment to obtain some results. For this purpose, Booster provides a series of utility classes and extension methods to help developers improve efficiency:
Runtime Instrumentation
In a Java environment, you might need to modify certain Class at runtime. We can easily implement this using Transformer.
Custom 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
Using 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
Using 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
Analysing Intermediate Artifacts
Using TransformHelperopen in new window, we can easily scan the artifacts of the 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
Analysing JAR File
Using the extension methods provided above, we can easily scan class files in a JAR file:
File("some.jar").transform(File("out")) { bytecode ->
val klass = bytecode.asClassNode()
println(klass.name)
bytecode
}
1
2
3
4
5
2
3
4
5
Or
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
Analysing Class File
val klass = File("Some.class").asClassNode()
println(klass.name)
1
2
2
