概述
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的使用使用步骤结尾所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复