理解一个工具的最快方式就是跑起来,然后原理自然了然于心 

本文以一个最简单的demo来实现对ASM全过程的了解。创建一个Child类,有一个call方法,最终的目的是在class类的call方法下增加一行输出语句。

ASM概念,操作流程:

需要创建一个 ClassReader 对象,将 .class 文件的内容读入到一个字节数组中

然后需要一个 ClassWriter 的对象将操作之后的字节码的字节数组回写

需要事件过滤器 ClassVisitor。在调用 ClassVisitor 的某些方法时会产生一个新的 XXXVisitor 对象,当我们需要修改对应的内容时只要实现自己的 XXXVisitor 并返回就可以了

1.引入最新的依赖:

//ASM相关依赖
implementation 'org.ow2.asm:asm:9.2'
implementation 'org.ow2.asm:asm-commons:9.1'

2.创建Child类,有一个phone属性,以及call()方法

public class Child {public String phone;public void call() {System.out.println("call");}
}

最终目的是在方法下面加一句,输入手机的名字和价格:

System.out.println("use :" + this.phone + " with price :" + this.price + " call");

3.创建方法过滤器ChildMethod,这里没有用MethodVisitor,使用了AdviceAdapter,因为它是 MethodVisitor 的子类,功能更全。

public class ChildMethod extends AdviceAdapter implements Opcodes {public ChildMethod(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {super(api, methodVisitor, access, name, descriptor);}@Overridepublic void visitCode() {//表示 ASM 开始扫描这个方法super.visitCode();}@Overridepublic void visitEnd() {//表示方法扫码完毕super.visitEnd();}@Overrideprotected void onMethodEnter() {//进入这个方法super.onMethodEnter();}@Overrideprotected void onMethodExit(int opcode) {//即将从这个方法出去super.onMethodExit(opcode);Label label1 = new Label();mv.visitLabel(label1);mv.visitLineNumber(16, label1);mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");mv.visitTypeInsn(NEW, "java/lang/StringBuilder");mv.visitInsn(DUP);mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);mv.visitLdcInsn("use :");mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);mv.visitVarInsn(ALOAD, 0);mv.visitFieldInsn(GETFIELD, "com/ng/ngstatistical/test/asmhook/Child", "phone", "Ljava/lang/String;");mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);mv.visitLdcInsn(" with price :");mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);mv.visitVarInsn(ALOAD, 0);mv.visitFieldInsn(GETFIELD, "com/ng/ngstatistical/test/asmhook/Child", "price", "Ljava/lang/String;");mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);mv.visitLdcInsn(" call");mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);}@Overridepublic void visitInsn(int opcode) {//扫描Opcodes操作符super.visitInsn(opcode);}
}

可以看到,添加小小的一行源代码却需要我们编写这么多的字节码,其实这里有一个取巧的办法,就是先在Child.java类中输入代码,实现我们想要的效果,再使用ASM Bytecode Viewer 插件来看生成的字节码,如下图所示:

之后,再复制到我们的方法过滤器的指定位置就好了~如此简单~高效~快捷~

4.然后需要实现类过滤器,在类过滤器中按方法名过滤方法,然后调用我们刚刚实现的方法过滤器:

public class ChildClassVisitor extends ClassVisitor {/*** @param api asm的api版本*/public ChildClassVisitor(int api) {super(api);}public ChildClassVisitor(int api, ClassVisitor classVisitor) {super(api, classVisitor);}@Overridepublic MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);if (name.equals("call")) {return new ChildMethod(Opcodes.ASM9,methodVisitor,access,name,descriptor);}return methodVisitor;}
}

5.最后需要新创建一个类,编写main函数,首先需要调用一下Child类的call方法来生成class文件:

//生成class
private static void testChild() {Child child = new Child();child.call();
}public static void main(String[] args) {testChild();//startHook();}

运行之后,可以看到生成的class:

6.最后在main函数执行asm的调度方法:

//Child 的 class文件路径public static final String LOCAL_PATH = "/Users/xiaoguagua/AndroidProjects/MyProjects/ng_projects/NgStatistical/app/build/intermediates/javac/debug/classes/com/ng/ngstatistical/test/asmhook";private static void startHook() {try {//1.首先创建ClassReader,读取目标类Child的内容ClassReader cr = new ClassReader(Child.class.getName());//2.然后创建ClassWriter对象,ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);ClassVisitor cv = new ChildClassVisitor(ASM9, cw);cr.accept(cv, Opcodes.ASM9);// 获取生成的class文件对应的二进制流byte[] code = cw.toByteArray();//将二进制流写到out/下FileOutputStream fos = new FileOutputStream(LOCAL_PATH + "/Child.class");fos.write(code);fos.close();} catch (Exception e) {e.printStackTrace();}}

运行,再去看刚才的Child.class文件,发现,它已经被改掉了,成功~

Android——ASM 极速上手 简单使用相关推荐

  1. 万师傅使用云产品,上手简单、开箱即用、省去运维烦恼

    云栖号案例库:[点击查看更多上云案例] 不知道怎么上云?看云栖号案例库,了解不同行业不同发展阶段的上云方案,助力你上云决策! 整体架构 每当我在思考技术选型方案的时候,翻翻阿里云的官网,总能找到我想要 ...

  2. android扫码 超简单零代码

    android扫码 超简单零代码 小序 背景介绍 前期准备 zxing和华为扫码服务对比 开始搬运 结语 小序 这是一篇纯新手教学,本人之前没有任何安卓开发经验(尴尬),本文也不涉及任何代码就可以使用 ...

  3. 小米5android p,久违的刷机 小米MIX Android P DP5 上手体验

    久违的刷机 小米MIX Android P DP5 上手体验 2018-07-30 20:15:44 29点赞 60收藏 34评论 前言 随着国产定制安卓系统越来越完善,刷机正在从安卓机的主要特征中逐 ...

  4. 逮虾户!Android程序调试竟简单如斯

    逮虾户!Android程序调试竟简单如斯 PS:行吧,不用百度了,逮虾户是<头文字D>的一首配乐<Deja vu>,中文谐音 "逮虾户",飙车漂移专用BGM ...

  5. android 如何加固,Android应用加固的简单实现方案(二)

    Android应用加固的简单实现方案(二) 前言 上一篇文章介绍了基于dex加固方案的两种具体实现.相对于手动加固,基于gradle实现的加固方案效率有了进一步提升.但是,还是需要在壳Module中增 ...

  6. 《Android App开发入门:使用Android Studio 2.X开发环境》——1-3 Android Studio 快速上手...

    1-3 Android Studio 快速上手

  7. Android TabLayout(选项卡布局)简单用法实例分析

    本文实例讲述了Android TabLayout(选项卡布局)简单用法.分享给大家供大家参考,具体如下: 我们在应用viewpager的时候,经常会使用TabPageIndicator来与其配合.达到 ...

  8. android 共享数据,android进程间共享简单数据

    我们知道,在android中,保存简单的数据最方便的就是使用SharedPreferences,然而,SharedPreferences虽然说也可以设置成进程间共享数据,但是并不可靠(更致命的是,不同 ...

  9. Android PC投屏简单尝试—最终章2

    源码地址:https://github.com/deepsadness/AppRemote 上一章中,我们简单实现了PC的投屏功能. 但是还是存在这一些缺陷. 屏幕的尺寸数据是写死的 不能通过PC来对 ...

  10. Android PC投屏简单尝试—最终章1

    回顾之前的几遍文章,我们分别通过RMTP协议和简单的Socket 发送Bitmap图片的Base64编码来完成投屏. 回想这系列文章的想法来源-Vysor,它通过 USB来进行连接的.又看到了 scr ...

最新文章

  1. 类,封装,this关键字,内存部分(java)
  2. Linux下,如何给PHP安装pdo_mysql扩展
  3. ABAP中的动态运算函数
  4. .NET网络编程学习(三)
  5. 国内最大.NET平台重金招募中 你竟然还不知道?
  6. 插入排序java_「Java」各类排序算法
  7. 这些人生经验与常识相反
  8. SpringMVC中拦截/和拦截/*的区别
  9. 【译】如何写出一份优秀的软件设计文档
  10. Linux 系统下 /etc/group 档案结构
  11. spring4.0.0的配置和使用
  12. gimp 抠图_GIMP入门教程2——利用蒙版抠图.pdf
  13. GB/T 36964-2018《软件工程 软件开发成本度量规范》国家标准正式发布...
  14. 集尘室行业调研报告 - 市场现状分析与发展前景预测(2021 - 2027)
  15. 大龄女计算机考研去当老师,考研、考公务员、当老师!哪个更适合女生?史上最强答案给你!...
  16. nginx同域名动静态分离
  17. Cpu、核、Java Runtime.getRuntime().availableProcessors()
  18. 【夏目鬼鬼分享】rabbitmq消息队列发送于接收
  19. Google 真的抄百度了吗?
  20. 廖雪峰的GIT教程-读书笔记

热门文章

  1. C#等 句柄是什么?
  2. ROS教程之ROS问题集
  3. cisco2811 pppoe上网配置供参考
  4. 微型计算机技术试题,《微型计算机技术》试题库
  5. java多线程优秀开源项目_github比较热门的Java开源项目
  6. 图片标注工具LabelImg
  7. JAVA练习173-幂集
  8. PopClip 能让像苹果电脑像 iOS 一样选中文字后弹出搜索、复制、粘贴等选项
  9. Altium Designer 9 学习笔记(一)基础操作
  10. 力扣-剑指offer所有题