下面我将以 “在所有 Activity 的 onCreate 方法插入日志”为例,分别给出Transform API(老方案)和Instrumentation API(新方案,AGP 7.0+)的完整集成过程,包括插件工程结构、依赖、注册、应用、调试等所有关键步骤,确保你可以直接落地实践。
一、Transform API 完整集成过程(适用于 AGP < 7.0)
⚠️ 注意:Transform API 在 AGP 7.0+ 已废弃,不推荐新项目使用,仅供老项目参考。
1. 新建插件模块(如 activity-log-transform)
目录结构示例:
activity-log-transform/
├── build.gradle
├── src/
│ └── main/
│ ├── java/
│ │ └── com/example/transform/
│ │ ├── ActivityLogTransform.java
│ │ └── ActivityLogTransformPlugin.java
│ └── resources/
│ └── META-INF/
│ └── gradle-plugins/
│ └── activity-log-transform-plugin.properties
2. 配置插件模块依赖
activity-log-transform/build.gradle
plugins {
id 'java'
}
dependencies {
implementation 'com.android.tools.build:gradle:4.2.2' // 你的 AGP 版本
implementation 'org.ow2.asm:asm:9.5'
implementation 'org.ow2.asm:asm-commons:9.5'
}
3. 实现 Transform 插件
ActivityLogTransformPlugin.java
package com.example.transform;
import com.android.build.gradle.AppExtension;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class ActivityLogTransformPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPlugins().withId("com.android.application", plugin -> {
AppExtension android = project.getExtensions().getByType(AppExtension.class);
android.registerTransform(new ActivityLogTransform());
});
}
}
ActivityLogTransform.java
package com.example.transform;
import com.android.build.api.transform.*;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.AdviceAdapter;
import java.io.*;
import java.util.Collection;
public class ActivityLogTransform extends Transform {
@Override
public String getName() { return "ActivityLogTransform"; }
@Override
public Set<QualifiedContent.ContentType> getInputTypes() {
return Collections.singleton(QualifiedContent.DefaultContentType.CLASSES);
}
@Override
public Set<QualifiedContent.Scope> getScopes() {
return Collections.singleton(QualifiedContent.Scope.PROJECT);
}
@Override
public boolean isIncremental() { return false; }
@Override
public void transform(TransformInvocation transformInvocation) throws IOException {
for (TransformInput input : transformInvocation.getInputs()) {
for (DirectoryInput dirInput : input.getDirectoryInputs()) {
File dir = dirInput.getFile();
if (dir.isDirectory()) {
for (File file : org.apache.commons.io.FileUtils.listFiles(dir, new String[]{"class"}, true)) {
byte[] bytes = org.apache.commons.io.FileUtils.readFileToByteArray(file);
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return new AdviceAdapter(Opcodes.ASM9, mv, access, name, desc) {
@Override
protected void onMethodEnter() {
if (name.equals("onCreate") && desc.startsWith("(Landroid/os/Bundle;)")) {
mv.visitLdcInsn("Monitor");
mv.visitLdcInsn("Activity onCreate: " + name);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"android/util/Log",
"d",
"(Ljava/lang/String;Ljava/lang/String;)I",
false
);
mv.visitInsn(Opcodes.POP);
}
}
};
}
};
cr.accept(cv, 0);
org.apache.commons.io.FileUtils.writeByteArrayToFile(file, cw.toByteArray());
}
}
// 输出到目标目录
File dest = transformInvocation.getOutputProvider().getContentLocation(
dirInput.getName(), getInputTypes(), getScopes(), Format.DIRECTORY);
org.apache.commons.io.FileUtils.copyDirectory(dir, dest);
}
// 还需处理 jarInput(省略,原理类似)
}
}
}
4. 注册插件
activity-log-transform/src/main/resources/META-INF/gradle-plugins/activity-log-transform-plugin.properties
implementation-class=com.example.transform.ActivityLogTransformPlugin
5. 在主工程中应用插件
app/build.gradle
plugins {
id 'com.android.application'
id 'activity-log-transform-plugin'
}
6. 构建并验证
- 执行
./gradlew assembleDebug - 运行 App,查看所有 Activity 的 onCreate 是否有日志输出
二、Instrumentation API 完整集成过程(AGP 7.0+ 推荐)
1. 新建插件模块(如 activity-log-instrumentation)
目录结构示例:
activity-log-instrumentation/
├── build.gradle.kts
├── src/
│ └── main/
│ ├── kotlin/
│ │ └── com/example/instrumentation/
│ │ ├── ActivityLogInstrumentationPlugin.kt
│ │ └── ActivityLogClassVisitorFactory.kt
│ └── resources/
│ └── META-INF/
│ └── gradle-plugins/
│ └── activity-log-instrumentation-plugin.properties
2. 配置插件模块依赖
activity-log-instrumentation/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
implementation("com.android.tools.build:gradle:8.2.0") // 你的 AGP 版本
implementation("org.ow2.asm:asm:9.5")
implementation("org.ow2.asm:asm-commons:9.5")
}
3. 实现 Instrumentation 插件
ActivityLogInstrumentationPlugin.kt
package com.example.instrumentation
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
class ActivityLogInstrumentationPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.application") {
val androidComponents = project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
androidComponents.onVariants { variant ->
variant.instrumentation.transformClassesWith(
ActivityLogClassVisitorFactory::class.java,
InstrumentationScope.ALL
) { }
variant.instrumentation.setAsmFramesComputationMode(
com.android.build.api.instrumentation.FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
)
}
}
}
}
ActivityLogClassVisitorFactory.kt
package com.example.instrumentation
import com.android.build.api.instrumentation.*
import org.objectweb.asm.*
import org.objectweb.asm.commons.AdviceAdapter
abstract class ActivityLogClassVisitorFactory : ClassVisitorFactory<InstrumentationParameters.None> {
override fun createClassVisitor(classContext: ClassContext, nextClassVisitor: ClassVisitor): ClassVisitor {
return object : ClassVisitor(Opcodes.ASM9, nextClassVisitor) {
override fun visitMethod(
access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?
): MethodVisitor {
val mv = super.visitMethod(access, name, desc, signature, exceptions)
return object : AdviceAdapter(Opcodes.ASM9, mv, access, name, desc) {
override fun onMethodEnter() {
if (name == "onCreate" && desc.startsWith("(Landroid/os/Bundle;)")) {
mv.visitLdcInsn("Monitor")
mv.visitLdcInsn("Activity onCreate: $name")
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"android/util/Log",
"d",
"(Ljava/lang/String;Ljava/lang/String;)I",
false
)
mv.visitInsn(Opcodes.POP)
}
}
}
}
}
}
override fun isInstrumentable(classData: ClassData): Boolean {
// 只处理 Activity 类
return classData.superClasses.contains("android/app/Activity")
}
}
4. 注册插件
activity-log-instrumentation/src/main/resources/META-INF/gradle-plugins/activity-log-instrumentation-plugin.properties
implementation-class=com.example.instrumentation.ActivityLogInstrumentationPlugin
5. 在主工程中应用插件
app/build.gradle.kts
plugins {
id("com.android.application")
id("activity-log-instrumentation-plugin")
}
6. 构建并验证
- 执行
./gradlew assembleDebug - 运行 App,查看所有 Activity 的 onCreate 是否有日志输出
三、调试与验证建议
- 插桩代码可先用
println或Log.d输出,确认插件已生效。 - 可用
javap或 Bytecode Viewer 反编译 class 文件,验证字节码是否被修改。 - Instrumentation API 支持 variant 粒度,可在
onVariants里加条件只对特定 buildType/flavor 生效。
四、总结
- Transform API 适合 AGP < 7.0,注册方式为
android.registerTransform,全局生效,需手动处理输入输出。 - Instrumentation API 适合 AGP 7.0+,注册方式为
variant.instrumentation.transformClassesWith,variant 粒度,AGP 自动处理输入输出,类型安全,推荐新项目使用。 - 插件模块建议独立于主工程,便于维护和复用。