我是靠谱客的博主 刻苦鼠标,最近开发中收集的这篇文章主要介绍android ASM的使用demoASM的使用使用步骤结尾,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

ASM的使用

git:https://github.com/lewis-v/AsmDemo

使用步骤

建立子模块

名字随意

修改子模块gradle

plugins {
    id 'kotlin'
    id 'groovy'
    id 'maven'
}
//定义资源位置
sourceSets {
    main {
        groovy {
            srcDir 'src/main/groovy'
        }

        java {
            srcDir 'src/main/java'
        }

        kotlin {
            srcDir 'src/main/kotlin'
        }

        resources {
            srcDir 'src/main/resources'
        }
    }
}
repositories {
    mavenCentral()
}
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation gradleApi()
    implementation "com.android.tools.build:gradle:3.6.3"
    implementation "org.ow2.asm:asm:7.0"
    implementation "org.ow2.asm:asm-commons:7.0"
}
compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}
compileTestKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

//指定上传的地址和插件id,版本
//需要上传之后才可以给其他工程当做插件使用
uploadArchives {
    repositories {
        mavenDeployer {
            pom.groupId = 'com.lewis.plugin'
            pom.artifactId = 'asm-plugin'
            pom.version = '0.0.2'
            repository(url: uri('G:/program/Repo'))
        }
    }
}

工程结构

其中groovy和java没有使用到,但是还是建立了文件夹

src
|-main
    |-groovy
    |-java
    |-kotlin
        |-com.lewis.asmdemoplugin
            |-DemoClassVisitor
            |-DemoMethodVisitor
            |-DemoTransform
    |-resources
        |-META-INF/gradle-plugins
            |-com.lewis.asmdemoplugin.properties

各个类实现

其中DemoTransform为插件入口,其实现了Plugin接口,apply为入口方法,在方法中进行了Transform的注册
DemoTransform.kt

package com.lewis.asmdemoplugin

import com.android.build.api.transform.Format
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.Transform
import com.android.build.api.transform.TransformInvocation
import com.android.build.gradle.AppExtension
import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.utils.FileUtils
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import java.io.FileOutputStream

class DemoTransform : Transform(), Plugin<Project> {


    override fun apply(p0: Project) {
        println("=======apply ${p0.version}=======")
        p0.extensions.getByType(AppExtension::class.java)//获取扩展的类型,apk
            .registerTransform(this)//注册transform
        println("=======apply register=======")
    }

    override fun getName(): String {
        return "ASMDemoTransform"
    }

    //transform要处理的输入类型,有class,resource,dex
    override fun getInputTypes(): MutableSet<QualifiedContent.ContentType> {
        return TransformManager.CONTENT_CLASS
    }

    //是否为增量工作
    override fun isIncremental(): Boolean {
        return false
    }

    /**
     * 输入文件的范围
     * PROJECT 当前工程
     * SUB_PROJECTS 子工程
     * EXTERNAL_LIBRARIES lib
     * LOCAL_DEPS jar
     */
    override fun getScopes(): MutableSet<in QualifiedContent.Scope> {
        return TransformManager.SCOPE_FULL_PROJECT
    }

    //具体处理
    override fun transform(transformInvocation: TransformInvocation?) {
        println("transform start")
        val inputs = transformInvocation?.inputs
        val outputProvider = transformInvocation?.outputProvider

        if (!isIncremental) {
            outputProvider?.deleteAll()
        }

        inputs?.forEach {
            it.directoryInputs.forEach {
                if (it.file.isDirectory) {
                    FileUtils.getAllFiles(it.file).forEach { file ->
                        val name = file.name
                        println("transform name :$name")
                        if (name.endsWith(".class") && name != "R.class"
                            && !name.startsWith("R$") && name != ("BuildConfig.class")
                        ) {//过滤出需要的class,将一些基本用不到的class去掉
                            val classPath = file.absoluteFile
                            println(">>>>>>>>> classPath :$classPath")

                            val cr = ClassReader(file.readBytes())
                            val cw = ClassWriter(cr, ClassWriter.COMPUTE_MAXS)
                            //需要处理的类使用自定义的visitor来处理
                            val visitor = DemoClassVisitor(cw)
                            cr.accept(visitor, ClassReader.EXPAND_FRAMES)

                            val bytes = cw.toByteArray()

                            val fos = FileOutputStream(classPath)
                            fos.write(bytes)
                            fos.close()
                        }
                    }
                }

                val dest = outputProvider?.getContentLocation(
                    it.name,
                    it.contentTypes,
                    it.scopes,
                    Format.DIRECTORY
                )
                FileUtils.copyDirectoryToDirectory(it.file, dest)
            }

            //将jar也加进来,androidx需要这个
            it.jarInputs.forEach {
                val dest = outputProvider?.getContentLocation(
                    it.name,
                    it.contentTypes,
                    it.scopes,
                    Format.JAR
                )
                FileUtils.copyFile(it.file, dest)
            }
        }
    }

}

DemoClassVisitor.kt对类的扫描

package com.lewis.asmdemoplugin

import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes

class DemoClassVisitor(classVisitor: ClassVisitor) : ClassVisitor(Opcodes.ASM5, classVisitor) {

    private var className: String? = null

    //类入口
    override fun visit(
        version: Int,
        access: Int,
        name: String?,
        signature: String?,
        superName: String?,
        interfaces: Array<out String>?
    ) {
        super.visit(version, access, name, signature, superName, interfaces)
        className = name
        println("visit name:$name")
    }

    //类中方法的入口
    override fun visitMethod(
        access: Int,
        name: String?,
        descriptor: String?,
        signature: String?,
        exceptions: Array<out String>?
    ): MethodVisitor {
        val result =  super.visitMethod(access, name, descriptor, signature, exceptions)
        println("visitMethod name:$name")
        if (className == "com/lewis/asmdemo/MainActivity" && name == "onCreate") {//过滤需要操作的类名和方法名
            //替换成自定义的方法扫描
            return DemoMethodVisitor(result)
        }
        return result
    }
}

DemoMethodVisitor.kt对方法的扫描

package com.lewis.asmdemoplugin

import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Opcodes.*

class DemoMethodVisitor(methodVisitor: MethodVisitor) : MethodVisitor(Opcodes.ASM5, methodVisitor) {

    //方法开始,此处可在方法开始插入字节码
    override fun visitCode() {
        super.visitCode()
        println("visit code")
        mv.visitLdcInsn("MainActivity");
        mv.visitLdcInsn("ttt");
        mv.visitMethodInsn(
            INVOKESTATIC,
            "android/util/Log",
            "i",
            "(Ljava/lang/String;Ljava/lang/String;)I",
            false
        );
        mv.visitInsn(POP);

    }

    //指令操作,这里可以判断拦截return,并在方法尾部插入字节码
    override fun visitInsn(opcode: Int) {
        if (opcode == ARETURN || opcode == RETURN) {
            mv.visitLdcInsn("MainActivity");
            mv.visitLdcInsn("tttInsn");
            mv.visitMethodInsn(
                INVOKESTATIC,
                "android/util/Log",
                "i",
                "(Ljava/lang/String;Ljava/lang/String;)I",
                false
            );
            mv.visitInsn(POP);
        }
        super.visitInsn(opcode)
    }

    //方法栈深度
    override fun visitMaxs(maxStack: Int, maxLocals: Int) {
        super.visitMaxs(maxStack, maxLocals)
    }

    //方法结束回调
    override fun visitEnd() {
        super.visitEnd()
    }
}

asm代码的生成

 mv.visitLdcInsn("MainActivity");
            mv.visitLdcInsn("tttInsn");
            mv.visitMethodInsn(
                INVOKESTATIC,
                "android/util/Log",
                "i",
                "(Ljava/lang/String;Ljava/lang/String;)I",
                false
            );
            mv.visitInsn(POP);

以上的代码其实是

android.util.Log.i("MainActivity", "ttt run3")

转换关系直接使用asm的插件来生成即可(可在androidStudio中插件中搜索asm,我这里选用的是ASM Bytecode Viewer Support Kotlin),这里需要注意,此时我是用的插件不支持高版本的android studio,所以版本退回3.6.2才能兼容(我猜过段时间就有新版本的插件的支持吧)

插件使用

执行uploadArchives任务进行打包上传,在需要使用的工程中引用

其中工程的gradle添加

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.3.71'
    repositories {
        google()
        jcenter()
        //添加仓库地址
        maven {
            url 'G:/program/Repo'
        }
    }
    dependencies {
        //引用的插件
        classpath "com.lewis.plugin:asm-plugin:0.0.2"
        classpath 'com.android.tools.build:gradle:3.6.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

模块的gradle添加

apply plugin: 'com.lewis.asmdemoplugin'

最后执行工程即可

结尾

ASM是一个字节码操作框架,其作为gradle插件在字节码编译的时候进行字节码操作,可实现代码无侵入式的代码插入.其中ClassVisitor/MethodVisitor为其关键类,具体为实现他们来进行相应的字节码操作.

最后

以上就是刻苦鼠标为你收集整理的android ASM的使用demoASM的使用使用步骤结尾的全部内容,希望文章能够帮你解决android ASM的使用demoASM的使用使用步骤结尾所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(65)

评论列表共有 0 条评论

立即
投稿
返回
顶部