android studio利用TransformApi配合ASM(ClassReader,ClassWriter,ClassVisitor)重新生成java字节码


文章来自:http://blog.csdn.net/intbird 转载请说明出处

1.Some Docs

下面的示例代码也是查看这些官网文档后编写的
文档都比较详细, 实现起来也很简单
注意gradle版本和gradle-api版本

  1. Gradle BuildSrc Doc
  2. Gradle Custom Plugin
  3. Gradle Transform Api Doc
  4. ASM Guiles
  5. ASM IDE Plugin
  6. Jadx
2.add buildSrc directory and sync project

在这里插入图片描述

3.ide auto tip add named directory in main

在这里插入图片描述

4.add build.gradle file in buildSrc directory


NOTE:

  1. gradle-api 依赖在 google() 仓库中
  2. [ ASM Maven Url ]
  3. buildSrc build.gradle code
plugins {
    id 'java'
    id 'groovy'
}

group 'intbird.soft.gradle'
version '1.0.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
    google()
}

// https://repo.maven.apache.org/maven2/org/ow2/asm/asm/
def asmVersion = '8.0.1'

dependencies {
    implementation gradleApi()
    implementation localGroovy()

    implementation 'com.android.tools.build:gradle-api:4.0.0'
    implementation 'org.apache.directory.studio:org.apache.commons.io:2.4'

    implementation "org.ow2.asm:asm:$asmVersion"
    implementation "org.ow2.asm:asm-util:$asmVersion"
    implementation "org.ow2.asm:asm-commons:$asmVersion"
}
5.app build.gradle add apply AsmPlugin

在这里插入图片描述
NOTE:

  1. [ use gradle plugin doc ]
  2. app.build.gradle apply plugin
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: intbird.soft.gradle.asm.AsmPlugin
...
6.plugin code

AsmPlugin code

  1. [ use gradle custom plugin ]
import intbird.soft.gradle.asm.transform.string.StringTransform
import org.gradle.api.Plugin
import org.gradle.api.Project

class AsmPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        println("----------intbird AsmPlugin start-----------")

        StringTransform transform = new StringTransform()
        // http://tools.android.com/tech-docs/new-build-system/transform-api
        project.android.registerTransform(transform)

        println("----------intbird AsmPlugin end-----------")
    }
}

StringTransform.groovy

  1. [ Transform API ]
  2. [ Transform API Doc ]
  3. [ Gradle Api Maven Url]
package intbird.soft.gradle.asm.transform.string

import com.android.build.api.transform.*
import intbird.soft.gradle.asm.transform.string.visitor.StringClassVisitor
import org.apache.commons.io.FileUtils
import org.apache.tools.ant.FileScanner
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter

class StringTransform extends Transform {

    @Override
    String getName() {
        return getClass().getName()
    }

    @Override
    Set<QualifiedContent.ContentType> getInputTypes() {
        return Collections.singleton(QualifiedContent.DefaultContentType.CLASSES)
    }

    @Override
    Set<QualifiedContent.ContentType> getOutputTypes() {
        return EnumSet.of(QualifiedContent.DefaultContentType.CLASSES)
    }

    @Override
    Set<QualifiedContent.Scope> getScopes() {
        return EnumSet.of(QualifiedContent.Scope.PROJECT)
    }

    @Override
    boolean isIncremental() {
        return false
    }

    @Override
    void transform(Context context,
                   Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs,
                   TransformOutputProvider outputProvider, boolean isIncremental)
            throws IOException, TransformException, InterruptedException {
        println("----StringTransform-----")

        inputs.forEach { input ->
            println("----inputs-----\n" + input.toString() + "\n")

            input.jarInputs.forEach { jarInput ->
                println("----jar Input-----\n" + input.toString() + "\n")
                File dest = outputProvider.getContentLocation(
                        jarInput.getFile().getAbsolutePath(),
                        jarInput.getContentTypes(),
                        jarInput.getScopes(),
                        Format.JAR)
                transformJar(jarInput.file, dest)
            }

            input.directoryInputs.forEach { directoryInput ->
                println("----directory Input-----\n" + directoryInput.toString() + "\n")
                File dest = outputProvider.getContentLocation(
                        directoryInput.getName(),
                        directoryInput.getContentTypes(),
                        directoryInput.getScopes(),
                        Format.DIRECTORY)

                transformDirectory(directoryInput.getFile(), dest)
            }
        }
    }

    private static void transformJar(File input, File dest) {
        println("-----transformJar------\n" + input + "\n" + dest)
        FileUtils.copyFile(input, dest)
    }

    private static void transformDirectory(File input, File dest) {
        println("-----transformDirectory 1------\n" + input + "\n" + dest + "\n")
        if (dest.exists()) {
            FileUtils.forceDelete(dest)
        }
        FileUtils.forceMkdir(dest)

        String srcDirPath = input.getAbsolutePath()
        String destDirPath = dest.getAbsolutePath()
        println("-----transformDirectory 2------\n" + srcDirPath + "\n" + destDirPath + "\n")
        File[] files = input.listFiles()
        for (File file : files) {
            String destFilePath = file.absolutePath.replace(srcDirPath, destDirPath)
            File destFile = new File(destFilePath)
            println("-----transformDirectory 3------\n" + destFile + "\n")
            if (file.isDirectory()) {
                transformDirectory(file, destFile)
            } else if (file.isFile()) {
                FileUtils.touch(destFile)
                transformSingleFile(file, destFile)
            }
        }
    }

    private static void transformSingleFile(File input, File dest) {
        println("-----transformSingleFile------\n" + input + "\n" + dest + "\n")
        String inputPath = input.getAbsolutePath()
        String outputPath = dest.getAbsolutePath()
        try {
            FileInputStream fileInputStream = new FileInputStream(inputPath)
            ClassReader classReader = new ClassReader(fileInputStream)
            ClassWriter classWriter = new ClassWriter(classReader, 0)
            StringClassVisitor classVisitor = new StringClassVisitor(classWriter)
            classReader.accept(classVisitor, 0)

            FileOutputStream fileOutputStream = new FileOutputStream(outputPath)
            fileOutputStream.write(classWriter.toByteArray())
            fileOutputStream.close()
        } catch (Exception e) {
            println("-----transformSingleFile Exception------\n" + inputPath + "  " + e.printStackTrace())
        }
    }
}

StringClassVisitor.groovy

  1. [ ASM DOC ]
  2. [ASM Plugin]
  3. [ ASM Maven Url ]
  4. [Class Api]
    [ClassReader]
    [ClassWrite]
    [ClassVisitor]
    [ClassNote]
class StringClassVisitor extends ClassVisitor {

    String testField = "helloField"
    String testMethod = "helloMethod"

    StringClassVisitor(ClassVisitor classVisitor) {
        super(Opcodes.ASM7, classVisitor)
    }

    @Override
    FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
        println("------ClassVisitor visitField ------\n" + access + "  " + name + " " + descriptor + "  " + signature + "  " + value + "\n")

        FieldVisitor fieldVisitor = super.visitField(access, name, descriptor, signature, value)
        if (name == testField) {
            return new StringFieldsVisitor(fieldVisitor)
        }
        return fieldVisitor
    }

    @Override
    MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        println("------ClassVisitor visitMethod ------\n" + access + "  " + name + " " + descriptor + "  " + signature + "  " + exceptions + "\n")

        MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions)
        if (name == testMethod) {
            return new StringMethodVisitor(methodVisitor)
        }
        return methodVisitor
    }
}
7.gradle task
Executing task 'transformClassesWithIntbird.soft.gradle.asm.transform.string.StringTransformForDebug'

在这里插入图片描述
文章来自:http://blog.csdn.net/intbird 转载请说明出处

猜你喜欢

转载自blog.csdn.net/intbird/article/details/107013895