Android 全局防抖/防重复点击
1.下载资源导入到项目。2.在你项目中创建一个FastClickHelper比如/*** * 插桩。只插在本项目中。其他第三方的不管 */ public class FastClickHelper { private static final long DEFAULT_INTERVAL_MS 500L; // 用于存储自定义间隔的 Tag Key private static final int TAG_LAST_TIME com.csii.baseutil.R.id.fastclick_last_time; private static final int TAG_CUSTOM_INTERVAL com.csii.baseutil.R.id.fastclick_custom_interval; private static final int TAG_DISABLE com.csii.baseutil.R.id.fastclick_disabled; public static boolean isFastClick(View view) { if (view null) return true; // 添加详细日志 // String viewInfo View view.getClass().getSimpleName() // , ID view.getId() // , Tag view.getTag(); // Log.i(FastClick, isFastClick called: viewInfo); // 检查是否对该 View 禁用了防抖 Boolean disabled (Boolean) view.getTag(TAG_DISABLE); if (disabled ! null disabled) { return false; // 不禁用直接放行 } // 获取为该 View 单独设置的间隔如果没有则使用默认值 Long customInterval (Long) view.getTag(TAG_CUSTOM_INTERVAL); long interval (customInterval ! null) ? customInterval : DEFAULT_INTERVAL_MS; Long lastTime (Long) view.getTag(TAG_LAST_TIME); long currentTime System.currentTimeMillis(); if (lastTime null || currentTime - lastTime interval) { view.setTag(TAG_LAST_TIME, currentTime); Log.i(FastClick,非快速点击放行); return false; } Log.i(FastClick,快速点击拦截); return true; } public static void setCustomInterval(View view, long intervalMs) { view.setTag(TAG_CUSTOM_INTERVAL, intervalMs); } /*** * 禁止某个view 插桩 * param view * param disabled */ public static void setDisabled(View view, boolean disabled) { view.setTag(TAG_DISABLE, disabled); } }3.修改doubleClick lib包下的FastClickMethodVisitor文件中 修改为自己的包名就是FastClickHelper文件的包名package com.csii.doubleclick.fastclick; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.AdviceAdapter; public class FastClickMethodVisitor extends AdviceAdapter { // FastClickHelper 类的内部名称******************修改这里⬇️ private static final String HELPER_CLASS com/csii/baseutil/FastClickHelper; private final int viewParamIndex; protected FastClickMethodVisitor(MethodVisitor methodVisitor, int access, String name, String descriptor) { super(Opcodes.ASM9, methodVisitor, access, name, descriptor); this.viewParamIndex findFirstViewParameterIndex(descriptor); } private int findFirstViewParameterIndex(String descriptor) { Type[] args Type.getArgumentTypes(descriptor); for (int i 0; i args.length; i) { if (args[i].getClassName().equals(android.view.View)) { return i; } } return 0; // fallback } Override protected void onMethodEnter() { // 在方法开头插入 // if (FastClickHelper.isFastClick(view)) return; // 加载方法中 为view的下标 loadArg(viewParamIndex); // 调用静态方法 FastClickHelper.isFastClick(View) visitMethodInsn(INVOKESTATIC, HELPER_CLASS, isFastClick, (Landroid/view/View;)Z, false); // 判断返回值 org.objectweb.asm.Label label new org.objectweb.asm.Label(); // 如果 isFastClick false ifZCmp(EQ, label); // 否则直接返回拦截点击 returnValue(); // 正常逻辑的标签 visitLabel(label); } public void returnValue() { visitInsn(Opcodes.RETURN); } }必须 includeBuild最后。在你的。settings.gradle 中includeBuild(‘doubleClick’) doubleClick 是你的lib名称ok 你的项目已经实现了全局的防抖FastClickHelper.setDisabled(view, true) //这是禁止某个view插桩的调用方法如果不想下载资源一下是lib的源码 ⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️build.gradlebuildscript { repositories { google() mavenCentral() } dependencies { classpath com.android.tools.build:gradle:7.4.2 } } plugins { id java-gradle-plugin } repositories { google() mavenCentral() } dependencies { // 注意使用单引号没有括号 compileOnly com.android.tools.build:gradle:7.4.2 implementation org.ow2.asm:asm:9.6 implementation org.ow2.asm:asm-commons:9.6 } gradlePlugin { plugins { create(doubleClick) { id com.csii.doubleclick.fastclick implementationClass com.csii.doubleclick.fastclick.FastClickPlugin } } } java { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 }com.csii.doubleclick.fastclickFastClickClassVisitor.JAVApackage com.csii.doubleclick.fastclick; import com.android.build.api.instrumentation.ClassData; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /*** * 可以根据 * 自己的需求 * 来进行插桩 */ public class FastClickClassVisitor extends ClassVisitor { private String className; public FastClickClassVisitor(ClassVisitor classVisitor) { super(Opcodes.ASM9, classVisitor); } Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { this.className name; super.visit(version, access, name, signature, superName, interfaces); } Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv super.visitMethod(access, name, descriptor, signature, exceptions); // 匹配目标方法名且参数包含 View、返回 void if ((onClick.equals(name) || onItemClick.equals(name) || name.contains(lambda$)) descriptor.contains(Landroid/view/View;) descriptor.endsWith()V)) { System.out.println([FastClick] Instrumenting: className . name descriptor); return new FastClickMethodVisitor(mv, access, name, descriptor); } return mv; } }FastClickClassVisitorFactory.javapackage com.csii.doubleclick.fastclick; import com.android.build.api.instrumentation.AsmClassVisitorFactory; import com.android.build.api.instrumentation.ClassContext; import com.android.build.api.instrumentation.ClassData; import com.android.build.api.instrumentation.InstrumentationParameters; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.objectweb.asm.ClassVisitor; public abstract class FastClickClassVisitorFactory implements AsmClassVisitorFactoryFastClickClassVisitorFactory.Parameters { public interface Parameters extends InstrumentationParameters { Input PropertyBoolean getEnabled(); } Override public ClassVisitor createClassVisitor(ClassContext classContext, ClassVisitor nextClassVisitor) { if (getParameters().get().getEnabled().get()) { return new FastClickClassVisitor(nextClassVisitor); } return nextClassVisitor; } Override public boolean isInstrumentable(ClassData classData) { String className classData.getClassName(); //这里根据自己的包名过滤我是只做我项目的过滤 if (className.startsWith(com.***.***) || className.startsWith(com.***.***)) { return true; } // 过滤掉系统类和不需要插桩的类 // if (className.startsWith(android.)) return false; // if (className.startsWith(androidx.)) return false; // if (className.startsWith(kotlin.)) return false; // if (className.contains(R$)) return false; // if (className.endsWith(R)) return false; // if (className.endsWith(BuildConfig)) return false; // if (className.contains(FastClickHelper)) return false; return false; } }FastClickMethodVisitor。javapackage com.csii.doubleclick.fastclick; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.AdviceAdapter; public class FastClickMethodVisitor extends AdviceAdapter { // FastClickHelper 类的内部名称 private static final String HELPER_CLASS com/csii/baseutil/FastClickHelper; private final int viewParamIndex; protected FastClickMethodVisitor(MethodVisitor methodVisitor, int access, String name, String descriptor) { super(Opcodes.ASM9, methodVisitor, access, name, descriptor); this.viewParamIndex findFirstViewParameterIndex(descriptor); } private int findFirstViewParameterIndex(String descriptor) { Type[] args Type.getArgumentTypes(descriptor); for (int i 0; i args.length; i) { if (args[i].getClassName().equals(android.view.View)) { return i; } } return 0; // fallback } Override protected void onMethodEnter() { // 在方法开头插入 // if (FastClickHelper.isFastClick(view)) return; // 加载第一个参数View view loadArg(viewParamIndex); // 调用静态方法 FastClickHelper.isFastClick(View) visitMethodInsn(INVOKESTATIC, HELPER_CLASS, isFastClick, (Landroid/view/View;)Z, false); // 判断返回值 org.objectweb.asm.Label label new org.objectweb.asm.Label(); // 如果 isFastClick false跳转到正常逻辑 ifZCmp(EQ, label); // 否则直接返回拦截点击 returnValue(); // 正常逻辑的标签 visitLabel(label); } public void returnValue() { visitInsn(Opcodes.RETURN); } }FastClickPlugin。java 插件类package com.csii.doubleclick.fastclick; import com.android.build.api.instrumentation.FramesComputationMode; import com.android.build.api.instrumentation.InstrumentationParameters; import com.android.build.api.instrumentation.InstrumentationScope; import com.android.build.api.variant.AndroidComponentsExtension; import org.gradle.api.Plugin; import org.gradle.api.Project; import kotlin.Unit; public class FastClickPlugin implements PluginProject { Override public void apply(Project project) { AndroidComponentsExtension?, ?, ? androidComponents project.getExtensions().findByType(AndroidComponentsExtension.class); if (androidComponents null) { System.err.println(FastClickPlugin: AndroidComponentsExtension not found); return; } // 为所有变体注册插桩 androidComponents.onVariants( androidComponents.selector().all(), variant - { System.out.println(FastClickPlugin: instrumenting variant.getName()); variant.getInstrumentation().transformClassesWith( FastClickClassVisitorFactory.class, InstrumentationScope.ALL, params - { params.getEnabled().set(true); return Unit.INSTANCE; } ); variant.getInstrumentation().setAsmFramesComputationMode( FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS ); } ); } }