Asm+Gradle8修改aar
前景提要
最近遇到一个需求,需要修改
第三方aar中的内容,通过百度搜索获得的大部分内容都是,使用
重打包的方式修改aar,该方法确实可行,不过在学习了
Asm了解原理后,觉得也可以用来修改aar中的字节码不过网上关于Asm修改aar的实践文章几乎与没有,
在自我折腾了很久以后,决定把踩坑内容,以及过程写出来
基本概念
apk的编译流程大概可以这么理解
.java->.classes->.dex
Asm则是可以直接修改.classes文件的工具
如下图所示
开始修改aar
创建以下的目录,
- 一个是用来构建需要修改的aar
- 一个用来制作修改aar的插件
使用as编译出一个 能够修改的aar
·
编写一个要修改的类
package com.onBit.lib_base.base
object HelloUtils {
fun greet() {
println("Hello world")
}
}
编写插件
build.gradle
plugins {
id("java-gradle-plugin")
//gradle 支持Kotlin
alias(libs.plugins.org.jetbrains.kotlin.jvm)
id("maven-publish")
}
/**
* 定义发布 每次更新后 执行才能更新
*/
gradlePlugin {
plugins {
create("onepixelPlugin") {
group = "com.onepixel.plugin"
version = "1.0.0"
//插件的唯一标识,使用插件的时候就是这个id
id = "com.onepixel.plugin"
//PageAnalysisPlugin的全类名 取代resources声明
implementationClass = "com.onepixel.plugin.HelloPlug"
}
}
}
dependencies {
// 使得 gradle 提供的 api
implementation gradleApi()
implementation localGroovy()
// 最新版本的 asm 库是 9.0
implementation 'com.android.tools.build:gradle:8.0.0'
implementation 'com.android.tools.build:gradle-api:8.0.0'
implementation 'org.ow2.asm:asm:9.6'
implementation 'org.ow2.asm:asm-util:9.2'
implementation 'org.ow2.asm:asm-commons:9.2'
}
publishing {
repositories {
maven {
url = uri("../plugin") //本地maven地址
}
}
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
plung
package com.onepixel.plugin
import com.android.build.api.instrumentation.FramesComputationMode
import com.android.build.api.instrumentation.InstrumentationScope
import com.android.build.api.variant.AndroidComponentsExtension
import com.onepixel.transform.HelloTransform
import org.gradle.api.Plugin
import org.gradle.api.Project
class HelloPlug : Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
//在compileDebugJavaWithJavac 后执行
androidComponents.onVariants(androidComponents.selector().all()) { variant ->
variant.instrumentation.transformClassesWith(HelloTransform::class.java, InstrumentationScope.ALL,){
}
variant.instrumentation.setAsmFramesComputationMode(FramesComputationMode.COPY_FRAMES)
}
}
}
使用Asm ByteCode Viewer 生成代码
制作修改代码ClassVisitor
package com.onepixel.transform
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.commons.AdviceAdapter
class HelloClassVisitor(api: Int, classVisitor: ClassVisitor?) :
ClassVisitor(api, classVisitor) {
override fun visit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
println("Class Name>>$name")
super.visit(version, access, name, signature, superName, interfaces)
}
override fun visitMethod(
access: Int,
name: String,
desc: String?,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
println(" Method >>$name")
val methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions)
if (name == "greet" && desc == "()V") {
// 只修改 greet 方法
return object : AdviceAdapter(Opcodes.ASM9, methodVisitor, access, name, desc) {
override fun onMethodEnter() {
super.onMethodEnter()
methodVisitor.visitCode();
methodVisitor.visitLdcInsn("Hello 112341235235");
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitInsn(SWAP);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 1);
methodVisitor.visitEnd();
}
}
}
return methodVisitor
}
}
检查结果
进行编译后检查生成结果,
使用
jadx检查
大功告成,aar也是同样的方式
坑点
当时 我用debug包测试的时候
但是缓存release后
后面输出才发现以及改了代码,
缺点:
因为是aar,提供给项目使用,在使用的时候会是修改前的代码如何要修改或者添加参数的话,在写代码的过程中是编译不通过的
只能在编译后才能看见是否修改完,可以使用反射的方式,不过不够直观
结论:
可以使用Asm修改aar,因为是在classes转dex文件时候拦截,但是实际开发中不方便使用,所以还是推荐使用
重打包方式,Asm,还是应该使用在插装等代码增强上面