Android如何解决所有双击优化的问题(android,开发技术)

时间:2024-05-04 23:26:43 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

transform简介

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

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

具体开发

初始化

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

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

构建transform

classDoubleTabTransformextendsTransform{ProjectprojectDoubleTabTransform(Projectproject){this.project=project}@OverrideStringgetName(){return"DoubleTabTransform"}@OverrideSet<QualifiedContent.ContentType>getInputTypes(){returnTransformManager.CONTENT_JARS}@OverrideSet<?superQualifiedContent.Scope>getScopes(){returnTransformManager.SCOPE_FULL_PROJECT}@OverridebooleanisIncremental(){returnfalse}@Overridevoidtransform(TransformInvocationtransformInvocation)throwsTransformException,InterruptedException,IOException{finalDoubleTapDelegateinjectHelper=newDoubleTapDelegate()BaseTransformbaseTransform=newBaseTransform(transformInvocation,newTransformCallBack(){@Overridebyte[]processJarClass(StringclassName,byte[]classBytes,BaseTransformtransform){if(ClassUtils.checkClassName(className)){returninjectHelper.transformByte(classBytes)}else{returnnull}}@OverrideFileprocessClass(Filedir,FileclassFile,FiletempDir,BaseTransformtransform){StringabsolutePath=classFile.absolutePath.replace(dir.absolutePath+File.separator,"")StringclassName=ClassUtils.path4Classname(absolutePath)if(ClassUtils.checkClassName(className)){returninjectHelper.beginTransform(className,classFile,transform.context.getTemporaryDir())}else{returnnull}}})baseTransform.startTransform()}}

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

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

ClassVisitor机制

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

classClassFilterVisitorextendsClassVisitor{privateString[]interfacesbooleanvisitedStaticBlock=falseprivateStringownerClassFilterVisitor(ClassVisitorclassVisitor){super(Opcodes.ASM5,classVisitor)}@Overridevoidvisit(intversion,intaccess,Stringname,Stringsignature,StringsuperName,String[]interfaces){super.visit(version,access,name,signature,superName,interfaces)this.interfaces=interfacesif(interfaces!=null&&interfaces.length>0){for(Map.Entry<String,MethodCell>entry:MethodHelper.sInterfaceMethods.entrySet()){MethodCellcell=entry.valueif(cell!=null&&interfaces.contains(cell.parent)){visitedStaticBlock=truethis.owner=namecv.visitField(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL,"doubleTap","Lcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck;",signature,null)}}}}@OverrideFieldVisitorvisitField(intaccess,Stringname,Stringdescriptor,Stringsignature,Objectvalue){returnsuper.visitField(access,name,descriptor,signature,value)}@OverrideMethodVisitorvisitMethod(intaccess,Stringname,Stringdesc,Stringsignature,String[]exceptions){if(interfaces!=null&&interfaces.length>0){try{if(visitedStaticBlock&&name=="<init>"){MethodVisitormethodVisitor=cv.visitMethod(access,name,desc,signature,exceptions)returnnewInitBlockVisitor(methodVisitor,owner)}MethodCellcell=MethodHelper.sInterfaceMethods.get(name+desc)if(cell!=null&&interfaces.contains(cell.parent)){MethodVisitormethodVisitor=cv.visitMethod(access,name,desc,signature,exceptions)CheckVisitormv=newCheckVisitor(methodVisitor,owner)returnmv}}catch(Exceptione){e.printStackTrace()}}returnsuper.visitMethod(access,name,desc,signature,exceptions)}}
修改前的类
publicclassTestJavaClickListenerimplementsView.OnClickListener{@OverridepublicvoidonClick(Viewv){Log.i("onClick","1");Log.i("onClick","2");Log.i("onClick","3");Log.i("onClick","4");}}
修改后的类
publicclassTestJavaClickListenerimplementsOnClickListener{privatefinalDoubleTapCheckdoubleTap=newDoubleTapCheck();publicTestJavaClickListener(){}publicvoidonClick(Viewvar1){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。
publicclassInitBlockVisitorextendsMethodVisitor{privateStringowner;InitBlockVisitor(MethodVisitormv,Stringowner){super(Opcodes.ASM5,mv);this.owner=owner;}@OverridepublicvoidvisitInsn(intopcode){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 之后执行newDoubleTapCheck();这个操作。这边我使用了asm的一个idea的插件ASM ByteCode Viewer,借助这个类你可以简单的把你想插入的代码的字节码都观察出来,之后再去用asm插入你想要的代码。

最后我们修改了onClick方法
publicclassCheckVisitorextendsMethodVisitor{privateStringowner;CheckVisitor(MethodVisitormv,Stringowner){super(Opcodes.ASM5,mv);this.owner=owner;}@OverridepublicvoidvisitCode(){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);Labellabel=newLabel();mv.visitJumpInsn(Opcodes.IFNE,label);mv.visitInsn(Opcodes.RETURN);mv.visitLabel(label);super.visitCode();}}

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

条件语句与label分析

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

publicclasscom/xxx/doubleclickplugin/sample/TestJavaClickListenerimplementsandroid/view/View$OnClickListener{//accessflags0x609publicstaticabstractINNERCLASSandroid/view/View$OnClickListenerandroid/view/ViewOnClickListener//accessflags0x12privatefinalLcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck;doubleTap//accessflags0x1public&lt;init&gt;()VALOAD0INVOKESPECIALjava/lang/Object.&lt;init&gt;()VALOAD0NEWcom/xxx/doubleclickplugin/sample/test/DoubleTapCheckDUPINVOKESPECIALcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck.&lt;init&gt;()VPUTFIELDcom/xxx/doubleclickplugin/sample/TestJavaClickListener.doubleTap:Lcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck;RETURNMAXSTACK=3MAXLOCALS=1//accessflags0x1publiconClick(Landroid/view/View;)VALOAD0GETFIELDcom/xxx/doubleclickplugin/sample/TestJavaClickListener.doubleTap:Lcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck;INVOKEVIRTUALcom/xxx/doubleclickplugin/sample/test/DoubleTapCheck.isNotDoubleTap()ZIFNEL0RETURNL0LDC"onClick"LDC"1"INVOKESTATICandroid/util/Log.i(Ljava/lang/String;Ljava/lang/String;)IPOPLDC"onClick"LDC"2"INVOKESTATICandroid/util/Log.i(Ljava/lang/String;Ljava/lang/String;)IPOPLDC"onClick"LDC"3"INVOKESTATICandroid/util/Log.i(Ljava/lang/String;Ljava/lang/String;)IPOPLDC"onClick"LDC"4"INVOKESTATICandroid/util/Log.i(Ljava/lang/String;Ljava/lang/String;)IPOPRETURNMAXSTACK=2MAXLOCALS=2}

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

 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:Android如何解决所有双击优化的问题的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:gtoken如何替换jwt实现sso登录下一篇:

3 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18