根据《编译插桩操纵字节码,实现不可能完成的任务》
一、插件步骤
- 新建一个Android 工程
- 在上面工程中新建一个Android Module项目,类型选择Android Library
- 第三步:将Module里的内容删除,只保留build.gradle文件和src/main目录,同时移除build.gradle文件里的内容
- 第四步:建立Gradle插件目录

按照上图在main下面建一个groovy目录,在建一个resources/META-INF/gradle-plugins。
5.修改插件.gradle文件
apply plugin:'groovy'
apply plugin:'maven'
dependencies{
implementation fileTree(dir: 'libs', include: ['*.jar'])
compile gradleApi()
compile localGroovy()
compile 'com.android.tools.build:gradle:3.3.2'
//asm
implementation 'org.ow2.asm:asm:7.1'
implementation 'org.ow2.asm:asm-commons:7.1'
}
group='wxx.lifecycle.plugin'
version='1.0.0'
uploadArchives{
repositories{
mavenDeployer{
//本地仓库地址
repository(url:uri('../asm_lifecycle_repo'))
}
}
}
在插件的task中找到upload,发布到本地

- new LifeCyclePlugin.groovy
class LifeCyclePlugin implements Plugin<Project>{
@Override
void apply(Project project) {
println '==LifeCyclePlugin=='
def android=project.extensions.getByType(AppExtension)
println '------registering AutoTrackTransform------------'
LifeCycleTransform lifeCycleTransform=new LifeCycleTransform()
android.registerTransform(lifeCycleTransform)
}
}
-
定义插件名称
asm_pro.png
代码如下,就是你插件的地址
implementation-class=com.xxw.plugin.LifeCyclePlugin
- app build.gradle 插件配置
buildscript {
repositories {
google()
jcenter()
maven { url '../asm_lifecycle_repo' }
}
dependencies {
classpath 'wxx.lifecycle.plugin:asm_lifecycle_plugin:1.0.0'
}
}
8.继承自Transform
class LifeCycleTransform extends Transform {
/**
* getName:设置我们自定义的Transform对应的Task名称。
* Gradle在编译的时候,会将这个名称显示在控制台上。
* 比如:Task:app:transformClassesWit...
* @return
*/
@Override
String getName() {
return "LifeCycleTransform"
}
/**
* 在项目中会有各种各样格式的文件,
* 通过getInputType可以设置LifeCycleTransform接收的文件类型,
* 此方法返回的类型是Set<QualifiedContent.Conte...
*
* @return
*/
@Override
Set<QualifiedContent.ContentType> getInputTypes() {
return TransformManager.CONTENT_CLASS
}
/**
* 这个方法规定自定义 Transform 检索的范围,具体有以下几种取值:
*
*
* @return
*/
@Override
Set<? super QualifiedContent.Scope> getScopes() {
return TransformManager.PROJECT_ONLY
}
/**
* 表示当前 Transform 是否支持增量编译,我们不需要增量编译,
*
* @return
*/
@Override
boolean isIncremental() {
return false
}
/**
* inputs:inputs 中是传过来的输入流,其中有两种格式,
* 一种是 jar 包格式,一种是 directory(目录格式)。
*
* outputProvider:outputProvider 获取到输出目录,
* 最后将修改的文件复制到输出目录,这一步必须做,否则编译会报错。
*
* @param transformInvocation
* @throws TransformException
* @throws InterruptedException
* @throws IOException
*/
@Override
public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
//拿到所有的class文件
Collection<TransformInput> transformInputs = transformInvocation.inputs
TransformOutputProvider outputProvider = transformInvocation.outputProvider
if (outputProvider != null) {
outputProvider.deleteAll()
}
transformInputs.each { TransformInput transformInput ->
// 遍历directoryInputs(文件夹中的class文件) directoryInputs代表着以源码方式参与项目编译的所有目录结构及其目录下的源码文件
// 比如我们手写的类以及R.class、BuildConfig.class以及MainActivity.class等
transformInput.directoryInputs.each { DirectoryInput directoryInput ->
File dir = directoryInput.file
if (dir) {
dir.traverse(type: FileType.FILES, nameFilter: ~/.*\.class/) { File file ->
System.out.println("find class: " + file.name)
//对class文件进行读取与解析
ClassReader classReader = new ClassReader(file.bytes)
//对class文件的写入
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
//访问class文件相应的内容,解析到某一个结构就会通知到ClassVisitor的相应方法
ClassVisitor classVisitor = new LifecycleClassVisitor(classWriter)
//依次调用 ClassVisitor接口的各个方法
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
//toByteArray方法会将最终修改的字节码以 byte 数组形式返回。
byte[] bytes = classWriter.toByteArray()
//通过文件流写入方式覆盖掉原先的内容,实现class文件的改写。
//FileOutputStream outputStream = new FileOutputStream( file.parentFile.absolutePath + File.separator + fileName)
FileOutputStream outputStream = new FileOutputStream(file.path)
outputStream.write(bytes)
outputStream.close()
}
}
//处理完输入文件后把输出传给下一个文件
def dest = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes,
directoryInput.scopes, Format.DIRECTORY)
FileUtils.copyDirectory(directoryInput.file, dest)
}
}
}
}
9.ClassVisitor
public class LifecycleClassVisitor extends ClassVisitor {
private String className;
private String superName;
public LifecycleClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
this.className=name;
this.superName=superName;
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
System.out.println("ClassVisitor visitMethod name="+name+",superName"+superName);
MethodVisitor mv=cv.visitMethod(access,name,descriptor,signature,exceptions);
if (superName.endsWith("AppCompatActivity")){
if (name.startsWith("onCreate")){
return new LifeCycleMethodVisitor(mv,className,name);
}
}
return mv;
}
@Override
public void visitEnd() {
super.visitEnd();
System.out.println("visitEnd");
}
}
10.MethodVisitor
public class LifeCycleMethodVisitor extends MethodVisitor {
private String className;
private String methodName;
public LifeCycleMethodVisitor(MethodVisitor methodVisitor,String className,String MethodName) {
super(Opcodes.ASM5, methodVisitor);
this.className=className;
this.methodName=methodName;
}
@Override
public void visitCode() {
super.visitCode();
super.visitCode();
System.out.println("MethodVisitor visitCode------");
mv.visitLdcInsn("TAG");
mv.visitLdcInsn(className + "---->" + methodName);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "android/util/Log", "i", "(Ljava/lang/String;Ljava/lang/String;)I", false);
mv.visitInsn(Opcodes.POP);
}
}
运行activity查看结果

网友评论