热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

android开发分享Android通过ASM实现多次点击拦截

从事Android开发的同学可能都会有这个需求,最近在学ASM相关的知识,拿这个想法练了一下手。大体思路是这样的,通过字节码Hook所有onClick(Viewview)方法,通过

从事Android开发的同学可能都会有这个需求,最近在学ASM相关的知识,拿这个想法练了一下手。大体思路是这样的,通过字节码Hook所有onClick(View view)方法,通过view.setTag(key,value)设置tag为当前时间戳,这样再次点击的时候就有一个时间差,通过对这个时间差,可以过滤掉多余的响应操作。

首先我们看一下lamba表达式和普通的setOnClickListener编译完是什么样的。

Android 通过ASM实现多次点击拦截

由截图可以看到不管我们以哪种方式设置监听点击,最终都是一个实现View.OnClickListener接口的静态内部类,由此我们可以Hook所有实现了View.OnClickListener接口的类中的名字为onClick,签名为(Landroid/view/View;)V的方法,在方法前面插入我们想要的代码。具体实现是这样的:

定义一个ClassVisitor:

 class MutiClickHandleVisitor(classVisitor: ClassVisitor): ClassVisitor(Opcodes.ASM5,classVisitor) {       private val classFullName = "android/view/View$OnClickListener"     private var isMatchClass = false      override fun visit(         version: Int,         access: Int,         name: String?,         signature: String?,         superName: String?,         interfaces: Array     ) {         super.visit(version, access, name, signature, superName, interfaces)         isMatchClass = matchClass(interfaces, classFullName)     }      override fun visitMethod(         access: Int,         name: String,         desc: String,         signature: String?,         exceptions: Array?     ): MethodVisitor {         val mv = cv.visitMethod(access, name, desc, signature, exceptions)         if (isMatchClass && matchMethod(name, desc)){             return MutiClickHandleMethodAdapter(mv)         }         return mv     }       private fun matchMethod(name: String, desc: String): Boolean {         return name == "onClick" && desc == "(Landroid/view/View;)V"     }      private fun matchClass(         interfaces: Array,         classFullName: String     ): Boolean {         var isMatch = false         // 是否满足实现的接口         for (anInterface in interfaces) {             if (anInterface == classFullName) {                 isMatch = true                 break             }         }         return isMatch     }  }

其中matchMethod方法就是确保名字和签名符合预期即 name ==”onClick” && desc ==”(Landroid/view/View;)V”

接着定义一个MethodVisitor:

 class MutiClickHandleMethodAdapter(methodVisitor: MethodVisitor) : MethodVisitor(Opcodes.ASM5,methodVisitor) {      override fun visitCode() {         super.visitCode()          mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false)         mv.visitVarInsn(Opcodes.LSTORE, 2)          mv.visitVarInsn(Opcodes.ALOAD, 1)         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "android/view/View", "getId", "()I", false)         mv.visitVarInsn(Opcodes.ISTORE, 4)          mv.visitVarInsn(Opcodes.ALOAD, 1)         mv.visitVarInsn(Opcodes.ILOAD, 4)         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "android/view/View", "getTag", "(I)Ljava/lang/Object;", false)         mv.visitVarInsn(Opcodes.ASTORE, 5)          mv.visitVarInsn(Opcodes.ALOAD, 1)         mv.visitVarInsn(Opcodes.ILOAD, 4)         mv.visitVarInsn(Opcodes.LLOAD, 2)         mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false)         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "android/view/View", "setTag", "(ILjava/lang/Object;)V", false)           mv.visitVarInsn(Opcodes.ALOAD, 5)         val l5 = Label()         mv.visitJumpInsn(Opcodes.IFNULL, l5)          mv.visitVarInsn(Opcodes.ALOAD, 5)         mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Long")         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false)         mv.visitVarInsn(Opcodes.LSTORE, 6)          mv.visitVarInsn(Opcodes.LLOAD, 2)         mv.visitVarInsn(Opcodes.LLOAD, 6)         mv.visitInsn(Opcodes.LSUB)         mv.visitLdcInsn(1500L)         mv.visitInsn(Opcodes.LCMP)         mv.visitJumpInsn(Opcodes.IFGE, l5)          mv.visitInsn(Opcodes.RETURN);         mv.visitLabel(l5)         mv.visitFrame(             Opcodes.F_APPEND,             3,             arrayOf(                 Opcodes.LONG,                 Opcodes.INTEGER,                 "java/lang/Object"             ),             0,             null         )      }  }

这里还有一些插件开发的常识,这里就不多说了,百度一下很多。等于一切都配置好了,我们来看下插桩后的代码长啥样。

Android 通过ASM实现多次点击拦截

好了到这里多次点击拦截过滤功能就实现了。

奉上源码


推荐阅读
author-avatar
mobiledu2502902041
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有