今日图片
时间:2025.02.14 周五
地点:深大地铁站
1. 转换注册
使用 onVariants
添加 transformClassesWith,不同点注意点(有些可能是对于 java 开发来说,突然转到 kotlin 有点不陌生)
- AppExtension 变成了 AndroidComponentsExtension
- registerTransform 变成了
onVariants
transformClassesWith - ClassVisitor 变成了 AsmClassVisitorFactory 接口实现
- ClassVisitor 构造方法直接传参数,变成了 parameters 成员实现
import com.android.build.api.instrumentation.InstrumentationScope
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.gradle.AppPlugin
import org.gradle.api.Plugin
import org.gradle.api.Project
class Check : Plugin<Project> {
override fun apply(project: Project) {
println("自定义 Check 插件运行 ================>")
//在应用 AppPlugin 插件上注册回调
project.plugins.withType(AppPlugin::class.java) {
//获取 AndroidComponentsExtension,类似 AGP7 的
//def appExtension = project.extensions.getByType(AppExtension)
val androidComponentsExtension =
project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponentsExtension.onVariants { varients ->
varients.instrumentation.transformClassesWith(
TestAsmClassVisitor::class.java,//指定 asm 操作类
InstrumentationScope.ALL//指定操作范围
) { params ->//指定操作参数,与之前通过 TestAsmClassVisitor 构造函数传参类似
params.processClassName.set("com.example.agp8kotlinplugin.MainActivity")
params.oldMethodName.set("sayHello")
params.newMethodName.set("sayHelloWorld")
}
}
}
}
}
2. 转换过滤
实现 AsmClassVisitorFactory
接口,并重写重要方法
- isInstrumentable:指定当前类是否需要转换
作用:主要目的是类过滤,明确那部分的类需要转换处理(实现 isInstrumentable
方法)
不同点是:
- 实现高度抽象的 AsmClassVisitorFactory
- 使用范型方式传参数,被抽象类成员 parameters 接收
- 更加明确哪些类需要转换,实现接口
isInstrumentable
import com.android.build.api.instrumentation.AsmClassVisitorFactory
import com.android.build.api.instrumentation.ClassContext
import com.android.build.api.instrumentation.ClassData
import com.android.build.api.instrumentation.InstrumentationParameters
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.objectweb.asm.ClassVisitor
abstract class TestAsmClassVisitor : AsmClassVisitorFactory<TestAsmClassVisitor.TestParams> {
override fun createClassVisitor(
classContext: ClassContext, nextClassVisitor: ClassVisitor
): ClassVisitor {
return ClassMethodVisitor(
instrumentationContext.apiVersion.get(),
nextClassVisitor,
parameters.get().oldMethodName.get(),//parameters 输入的参数
parameters.get().newMethodName.get(),
)
}
//返回值表示输入的类是否需要转入下一步自定义的 classVisitor 处理
override fun isInstrumentable(classData: ClassData): Boolean {
if (classData.className == parameters.get().processClassName.get()) {
println("找到需要处理的类:=${classData.className}")
return true
}
return false
}
//自定义输入或输出参数,对应接口 AsmClassVisitorFactory 的 parameters 成员
interface TestParams : InstrumentationParameters {
@get:Input
val processClassName: Property<String>
@get:Input
val oldMethodName: Property<String>
@get:Input
val newMethodName: Property<String>
}
}
3. 转换实现
实现一个简单的需求:你需要自定义映射,混淆指定类里面的特定方法
你看到了我前面的转换输入参数:
明确指定类需要混淆(转换过滤找到指定类)
params.processClassName.set("com.example.agp8kotlinplugin.MainActivity")
明确特定方法混淆的映射(转换实现做混淆替换)
params.oldMethodName.set("sayHello")
params.newMethodName.set("sayHelloWorld")
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
class ClassMethodVisitor(
apiVersion: Int,
cv: ClassVisitor,
private val oldMethodName: String,
private val newMethodName: String
) : ClassVisitor(apiVersion, cv) {
override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
println("当前类包含的方法=${name}")
if (name == oldMethodName) {
println("\t找到要处理的方法=${name},修改为=${newMethodName}")
return super.visitMethod(access, newMethodName, descriptor, signature, exceptions)
}
return super.visitMethod(access, name, descriptor, signature, exceptions)
}
}
运行输出
实际效果:sayHello
成功修改为映射后的 sayHelloWorld
4. 低版本对比
代码上看相比之前的 AGP4+ transform
确实简洁了很多,之前的 transform 方法重写需要关注的地方比较多:
- transformInput.getJarInputs():需要处理 jar 文件
-
- 拿到 jar 遍历读取里面的每个 element
-
- 有各种不同类型的 element,还需要过滤 class 文件
-
- 再确定当前 class 是否属于处理目标文件
-
- 是目标文件,才以字节数组输入到具体 asm 操作转换
-
- asm 修改完毕之后还要自己保存,否则下一个 transform 没有输入
- transformInput.getDirectoryInputs():需要处理目录下的 class 文件
-
- 需要遍历目录读取每个 class 文件
-
- 再确定当前 class 是否属于处理目标文件
-
- 是目标文件,才以字节数组输入到具体 asm 操作转换
-
- asm 修改完毕之后还要自己保存,否则下一个 transform 没有输入
如果还是不同清楚 AGP8 之前的 transform,你可以查看之前的文章大概了解:AGP4 Transform 小结
AGP 低版本时候感觉确实麻烦
我觉得升级还是挺有必要的,升级往往带来的是效率的提升和各种优化
对你还可以接触新知识,何乐而不为
实际项目生产环境可能不会这么轻易升级,你应该有自驱力,主动接触新知识