iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android解决所有双击优化的问题
  • 490
分享到

Android解决所有双击优化的问题

2024-04-02 19:04:59 490人浏览 安东尼
摘要

目录背景transfORM简介具体开发初始化构建transformClassVisitor机制修改前的类修改后的类其中init方法我们回去给doubletap 完成初始化操作,下面我

背景

产品想对多次快速点击做一下优化,想要的效果就是双击不会打开多次

但是从开发角度来说,我可以用Kotlin的拓展方法来调整这个,但是之前的历史债务可能会把我让我有点手足无措,同时java代码也会有问题。有没有什么方法可以让开发可以投机取巧呢,我想到了去年项目里写到的插桩埋点的方式,是不是我只要在编译的时候编织插入字节码就可以解决这个问题了。

transform简介

在打包流程中,我们知道生成.class文件后,利用dx工具生成.dex文件,而利用Transform api可以在生成.class文件后修改.class文件,从而修改源码。我们将Transform注册到AppExtension中,在java compile Task执行后会执行Tramsform类型的task。

具体开发

初始化

首先先创建一个groovy的module,然后初始化一个gradle插件

声明一个gradle-plugins 这个基础 

https://www.jb51.net/article/79966.htm

 这个博客内有基础的流程操作

构建transform

class DoubleTabTransform extends Transform {
    Project project
    DoubleTabTransform(Project project) {
        this.project = project
    }
    @Override
    String getName() {
        return "DoubleTabTransform"
    }
    @Override
    Set<QualifiedContent.ContentType> getInputTypes() {
        return TransformManager.CONTENT_jarS
    }
    @Override
    Set<? super QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT
    }
    @Override
    boolean isIncremental() {
        return false
    }
    @Override
    void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
        final DoubleTapDelegate injectHelper = new DoubleTapDelegate()
        BaseTransform baseTransform = new BaseTransform(transformInvocation, new TransformCallBack() {
            @Override
            byte[] processJarClass(String className, byte[] classBytes, BaseTransform transform) {
                if (ClassUtils.checkClassName(className)) {
                    return injectHelper.transformByte(classBytes)
                } else {
                    return null
                }
            }
            @Override
            File processClass(File dir, File classFile, File tempDir, BaseTransform transform) {
                String absolutePath = classFile.absolutePath.replace(dir.absolutePath + File.separator, "")
                String className = ClassUtils.path3Classname(absolutePath)
                if (ClassUtils.checkClassName(className)) {
                    return injectHelper.beginTransform(className, classFile, transform.context.getTemporaryDir())
                } else {
                    return null
                }
            }
        })
        baseTransform.startTransform()
    }
}

上述代码对transform 以及classvisitor代码进行了一次抽象封装,方便后续如果有类似的插入逻辑可以快速接入开发。

主要的逻辑代码是对jar包以及.class文件进行扫描,当文件符合修改标准的情况下会回调文件修改的方法,然后基于asm的classvisitor 对文件进行访问操作。

ClassVisitor机制

这个可以看下网上的资料,我这边就不多过于简述了, 简单的说就是构造了一个类访问器,然后顺序的读取类的所以属性,方法,以及方法的每一行。

class ClassFilterVisitor extends ClassVisitor {
    private String[] interfaces
    boolean visitedStaticBlock = false
    private String owner
    ClassFilterVisitor(ClassVisitor classVisitor) {
        super(Opcodes.ASM5, classVisitor)
    }
    @Override
    void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces)
        this.interfaces = interfaces
        if (interfaces != null && interfaces.length > 0) {
            for (Map.Entry<String, MethodCell> entry : MethodHelper.sInterfaceMethods.entrySet()) {
                MethodCell cell = entry.value
                if (cell != null && interfaces.contains(cell.parent)) {
                    visitedStaticBlock = true
                    this.owner = name
                    cv.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, "doubleTap",
                            "Lcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck;",
                            signature, null)
                }
            }
        }
    }
    @Override
    FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
        return super.visitField(access, name, descriptor, signature, value)
    }
    @Override
    MethodVisitor visitMethod(int access, String name,
                              String desc, String signature, String[] exceptions) {
        if (interfaces != null && interfaces.length > 0) {
            try {
                if (visitedStaticBlock && name == "<init>") {
                    MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions)
                    return new InitBlockVisitor(methodVisitor, owner)
                }
                MethodCell cell = MethodHelper.sInterfaceMethods.get(name + desc)
                if (cell != null && interfaces.contains(cell.parent)) {
                    MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions)
                    CheckVisitor mv = new CheckVisitor(methodVisitor, owner)
                    return mv
                }
            } catch (Exception e) {
                e.printStackTrace()
            }
        }
        return super.visitMethod(access, name, desc, signature, exceptions)
    }
}

修改前的类

public class TestJavaClickListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
        Log.i("onClick", "1");
        Log.i("onClick", "2");
        Log.i("onClick", "3");
        Log.i("onClick", "4");
    }
}

修改后的类

public class TestJavaClickListener implements OnClickListener {
    private final DoubleTapCheck doubleTap = new DoubleTapCheck();
    public TestJavaClickListener() {
    }
    public void onClick(View var1) {
        if (this.doubleTap.isNotDoubleTap()) {
            Log.i("onClick", "1");
            Log.i("onClick", "2");
            Log.i("onClick", "3");
            Log.i("onClick", "4");
        }
    }
}

这个就是项目内的类访问器,其中visit方法代表类被访问了,会返回这个类继承的接口等等基础参数。我在这个方法插入了一个引用的索引,简单的说声明了一个 DoubleTapCheck doubleTap;然后就是classvIstior的visitMethod,这个是我们主要要调整的地方,其中一个关键点是我们需要修改两个地方,一个类的初始化,另外一个onClick方法。

其中init方法我们回去给doubletap 完成初始化操作,下面我们来讲下InitBlockVisitor。

public class InitBlockVisitor extends MethodVisitor {
    private String owner;
    InitBlockVisitor(MethodVisitor mv, String owner) {
        super(Opcodes.ASM5, mv);
        this.owner = owner;
    }
    @Override
    public void visitInsn(int opcode) {
        if ((opcode &gt;= Opcodes.IRETURN &amp;&amp; opcode &lt;= Opcodes.RETURN)
                || opcode == Opcodes.ATHROW) {
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            mv.visitTypeInsn(Opcodes.NEW, "com/xxxx/doubleclickplugin/sample/test/DoubleTapCheck");
            mv.visitInsn(Opcodes.DUP);
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "com/xxx/doubleclickplugin/sample/test/DoubleTapCheck",
                    "&lt;init&gt;", "()V", false);
            mv.visitFieldInsn(Opcodes.PUTFIELD, owner, "doubleTap",
                    "Lcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck;");
        }
        super.visitInsn(opcode);
    }
}

上述代码只完成了一件事情,就是在init 之后执行new DoubleTapCheck();这个操作。这边我使用了asm的一个idea的插件 ASM ByteCode Viewer ,借助这个类你可以简单的把你想插入的代码的字节码都观察出来,之后再去用asm插入你想要的代码。

最后我们修改了onClick方法

public class CheckVisitor extends MethodVisitor {
    private String owner;
    CheckVisitor(MethodVisitor mv, String owner) {
        super(Opcodes.ASM5, mv);
        this.owner = owner;
    }
    @Override
    public void visitCode() {
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, owner, "doubleTap",
                "Lcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck;");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/xxx/doubleclickplugin/sample/test/DoubleTapCheck",
                "isNotDoubleTap", "()Z", false);
        Label label = new Label();
        mv.visitJumpInsn(Opcodes.IFNE, label);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitLabel(label);
        super.visitCode();
    }
}

这个代码就比较少了,他只做了一件事情,就是在onClick方法的最前面插入doubleTap.isNotDoubleTap()的逻辑判断。

条件语句与label分析

下面是一个OnClickListener 的插桩字节码,我们会通过分析这个类了解label的用法

public class com/xxx/doubleclickplugin/sample/TestJavaClickListener implements Android/view/View$OnClickListener {
  // access flags 0x609
  public static abstract INNERCLASS android/view/View$OnClickListener android/view/View OnClickListener
  // access flags 0x12
  private final Lcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck; doubleTap
  // access flags 0x1
  public &lt;init&gt;()V
    ALOAD 0
    INVOKESPECIAL java/lang/Object.&lt;init&gt; ()V
    ALOAD 0
    NEW com/xxx/doubleclickplugin/sample/test/DoubleTapCheck
    DUP
    INVOKESPECIAL com/xxx/doubleclickplugin/sample/test/DoubleTapCheck.&lt;init&gt; ()V
    PUTFIELD com/xxx/doubleclickplugin/sample/TestJavaClickListener.doubleTap : Lcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck;
    RETURN
    MAXSTACK = 3
    MAXLOCALS = 1
  // access flags 0x1
  public onClick(Landroid/view/View;)V
    ALOAD 0
    GETFIELD com/xxx/doubleclickplugin/sample/TestJavaClickListener.doubleTap : Lcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck;
    INVOKEVIRTUAL com/xxx/doubleclickplugin/sample/test/DoubleTapCheck.isNotDoubleTap ()Z
    IFNE L0
    RETURN
   L0
    LDC "onClick"
    LDC "1"
    INVOKESTATIC android/util/Log.i (Ljava/lang/String;Ljava/lang/String;)I
    POP
    LDC "onClick"
    LDC "2"
    INVOKESTATIC android/util/Log.i (Ljava/lang/String;Ljava/lang/String;)I
    POP
    LDC "onClick"
    LDC "3"
    INVOKESTATIC android/util/Log.i (Ljava/lang/String;Ljava/lang/String;)I
    POP
    LDC "onClick"
    LDC "4"
    INVOKESTATIC android/util/Log.i (Ljava/lang/String;Ljava/lang/String;)I
    POP
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 2
}

我们从第24行开始观察起。首先我们获取了0位置就是view,然后我们获取了doubleTap 的实例,调用了doubleTab.isNotDoubleTap 的方法。27行是关键,这里判断的isNotDoubleTap的结果然后跳转到下面的方法块。其中最后有个L0,我一开始也不能理解这个是什么意思,最后用javap解析了字节码之后发现其实这个L0其实映射到的是下面的方法块的L0,而在真实的字节码中,这个就是对应的行数。而这个地方就是我们使用的Label标签,那么label标签顾名思义,就是标记一个方法块的行数。就是两个label之间的代码行数。

GitHub链接

以上就是Android解决所有双击优化的问题的详细内容,更多关于Android双击优化的资料请关注编程网其它相关文章!

--结束END--

本文标题: Android解决所有双击优化的问题

本文链接: https://www.lsjlt.com/news/151156.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android解决所有双击优化的问题
    目录背景transform简介具体开发初始化构建transformClassVisitor机制修改前的类修改后的类其中init方法我们回去给doubletap 完成初始化操作,下面我...
    99+
    2024-04-02
  • Android如何解决所有双击优化的问题
    今天小编给大家分享一下Android如何解决所有双击优化的问题的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。transfor...
    99+
    2023-07-02
  • windows如何解决鼠标单击变双击的问题
    小编给大家分享一下windows如何解决鼠标单击变双击的问题,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!在搜索框输入控【制面板】。进入控制面板后,在【查看方式】那选择【大图标】。然后点击【文件资源管理器选项】。然后进入是...
    99+
    2023-06-27
  • 如何解决windows中双击盘符提示格式化问题
    这篇文章给大家介绍如何解决windows中双击盘符提示格式化问题,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。  最近有人问到我双击盘符提示格式化的问题,今天电脑百科网小篇教大家如何解决双击盘符提示格式化问题...
    99+
    2023-06-13
  • 凸优化:python解决凸优化问题
    1、凸优化 2、安装CVX包 用pip安装cvxpy-0.4.8-py2-none-any.whl 下载地址: http://www.lfd.uci.edu/~gohlke/pythonlibs/ 3、安装过程中可能遇到的...
    99+
    2023-01-31
    python
  • 如何解决JS中双击和单击事件冲突的问题
    这篇文章主要为大家展示了“如何解决JS中双击和单击事件冲突的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决JS中双击和单击事件冲突的问题”这篇文章吧...
    99+
    2024-04-02
  • matlab有约束最优化问题怎么解决
    在Matlab中,可以使用内置函数fmincon来解决约束最优化问题。fmincon函数的基本形式如下:x = fmincon(fu...
    99+
    2023-09-13
    matlab
  • Python Django常见问题解答:解决你遇到的所有问题
    如何设置Django项目? 安装Django:使用pip命令安装Django:pip install django 创建项目:在命令行中使用django-admin startproject命令创建项目:django-admin st...
    99+
    2024-02-26
    Python Django 问题解答 模型 视图 表单
  • 完美解决Redis在双击redis-server.exe出现闪退问题
    问题 本人是习惯直接双击Redis-server.exe来打开Redis程序,然后感觉没用了就关掉窗口,事实上很多时候即使你关闭了,在电脑后台依旧还连接着。这就会导致了你再次双击redis-server.exe的时候出现...
    99+
    2023-01-04
    redis-server.exe闪退
  • MySQL慢查询优化解决问题
    目录1.  MySQL慢查询介绍2.发现问题(主动/被动)3.找到原因-对症下药1.  MySQL慢查询介绍   MySQL的慢查询日志是MySQL提供...
    99+
    2024-04-02
  • Android开发如何解决安卓重复点击问题
    这篇文章主要介绍“Android开发如何解决安卓重复点击问题”,在日常操作中,相信很多人在Android开发如何解决安卓重复点击问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android开发如何解决安卓...
    99+
    2023-07-02
  • BeanUtils.copyProperties()所有的空值不复制问题怎么解决
    本文小编为大家详细介绍“BeanUtils.copyProperties()所有的空值不复制问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“BeanUtils.copyProperties()所有的空值不复制问题怎么解决”文章能...
    99+
    2023-07-02
  • Homebrew下载速度优化(已解决问题)
    问题: brew install 各种软件时,速度超慢,一直断连,一直报的问题如下: Downloading [https://formulae.brew.sh/api/formula.json](https://formulae.brew...
    99+
    2023-08-20
    macos
  • 如何解决多个Tab页点击切换功能所导致的问题
    本篇内容主要讲解“如何解决多个Tab页点击切换功能所导致的问题”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何解决多个Tab页点击切换功能所导致的问题”吧!大...
    99+
    2024-04-02
  • Android的Toast问题怎么解决
    这篇“Android的Toast问题怎么解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android的Toast问题怎么...
    99+
    2023-06-04
  • WinXP双击盘符提示选择打开方式问题解决方法
      在WinXP系统中,双击“我的电脑”里的某个磁盘,正常都会显示其磁盘内的文件,然后有的用户却是跳出了选择打开方式的窗口,该如何排除这故障呢   操作如下:   1、点击“开始&md...
    99+
    2023-06-14
    双击 盘符 打开方式 解决 问题 WinXP 方法
  • Python logging 模块:程序员的宝典,解决所有问题
    1. 模块概述 logging 模块提供了一个标准化且灵活的接口,用于记录应用程序事件。它支持分级日志记录、格式化日志消息、以及通过各种处理程序(如控制台输出或文件写入)输出日志。通过利用 logging 模块,程序员可以轻松地添加日志记...
    99+
    2024-03-06
    Python logging 模块、程序调试、错误记录、日志级别
  • Redis跟MySQL的双写问题怎么解决
    本文小编为大家详细介绍“Redis跟MySQL的双写问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“Redis跟MySQL的双写问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。写在前面严格意...
    99+
    2023-06-29
  • php的ddos攻击问题怎么解决
    要解决PHP的DDoS攻击问题,可以采取以下几种措施:1. 增加服务器的带宽和硬件资源:提高服务器的带宽和硬件资源可以增加服务器的处理能力,从而更好地应对DDoS攻击。2. 使用防火墙配置:使用防火墙来限制对服务器的访问,可以过滤掉恶意...
    99+
    2023-08-11
    php
  • C++中编译优化问题和解决方法的详解
    C++中编译优化问题和解决方法的详解摘要:C++编译器优化是提高程序性能的重要手段。然而,在实际开发中,我们经常会遇到一些与编译优化相关的问题,比如编译器错误优化、代码性能下降等。本文将针对这些问题进行详细的解析,并提供相应的解决方法,以期...
    99+
    2023-10-22
    问题解决方法 C++编译优化 详解编程关键词
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作