使用APT减少MVP的冗余代码

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

使用APT减少MVP的冗余代码前言不知道从何时起,移动端开发都开始采用MVP。我们在认识到MVP有点的时候,也不妨会察觉到它其实也有很多恼人的地方,比如,我们针对每种状态渲染不同的视图:privatevoidrenderInit(){mViewA.setVisibility(View.VISIBLE);mViewB.setVisibility(View.GONE);mViewC.setVisibility(View.GONE);mViewD.setVisibility(View.GONE);mViewE.setVisibility(View.GONE);}privatevoidrenderSummary(){mViewA.setVisibility(View.GONE);mViewB.setVisibility(View.VISIBLE);mViewC.setVisibility(View.GONE);mViewD.setVisibility(View.GONE);mViewE.setVisibility(View.GONE);}可以看到在这里,我们渲染Init状态时,把ViewA设为可见,把其他的View设为不可见,当我们又去渲染Summary状态是,又重复上面的动作,不过这次是吧ViewB设为可见。这种冗余代码(或者说是模板代码)非常的烦人,因为我们在复制粘贴的时候极有可能设置错误的View为可见了。那么我们有没有什么办法来避免这样的问题呢。其实是有的,我们不妨回忆下ButterKnife怎么做的——对于findViewById这样的冗余代码,ButterKnife是采用注解的方式解决的:@Bind(R.id.id_name)TextViewm_name;@Bind(R.id.id_who)TextViewm_who;@Bind(R.id.id_musicBar)MusicBarm_musicBar;@Bind(R.id.id_playControl)ImageViewm_bottomPlayControlView;@OverrideprotectedIViewcreateView(){returnthis;}@OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState){super.onCreate(savedInstanceState);ButterKnife.bind(this);...}在执行ButterKnife.bind(this);1后,ButterKnife会采用APT自动生成代码执行findViewById操作。同样的,我们在解决MVP冗余代码时,我们也可以使用APT生成代码执行setVisibility(View.VISIBLE);操作。思路1:模仿ButterKnife对于要setVisibility的View我们使用注解来标示2:当知道有哪些View要setVisibility后,我们可以把它们存到容器里3:当外部要setVisibility某些View时,我们可以提供一个类似4:为了避免APT生成的代码和现有的代码重复类名,我们可以尝试在APT的类名中出现$符号,但是这样用户用起来很难受,我们可以是APT生成的代码都实现某个接口,当new出对象后以接口类型返回以保障代码整洁性。voidsetVisible(View...target)1的接口去遍历容器,如果容器中的View在集合target中,就设为可见,否则不可见。1:如果你最APT还不是很了解,建议阅读下鸿洋的文章鸿洋APT实现0x01:在AndroidStudio里新建一个java工程:这里写图片描述在java工程的build.gradle脚本里添加依赖:applyplugin:'java'sourceCompatibility=JavaVersion.VERSION_1_7targetCompatibility=JavaVersion.VERSION_1_7dependencies{compilefileTree(dir:'libs',include:['*.jar'])compile'com.google.auto.service:auto-service:1.0-rc2'}0x02:然后我们定义注解:/***Createdbychanon16/10/15.*jiacheng.li@shanbay.com*/@Documented@Retention(RetentionPolicy.SOURCE)@Inherited@Target(ElementType.FIELD)public@interfaceJoinView{}只能用于field,它用于标示我们要setVisibility的view,像这样:@JoinViewViewmViewC;120x03:当注解标示某个field之后,我们就可以拿到field的变量名,我们可以通过activity.mViewC的方式读取里面的值,不过这有个前提——mView最起码应该是protected,或者public的,但是我们还是选用protected,毕竟这样可以最大化数据的封装程度。如果是这样的话我们生成的类必须得和被注解的类在同一包下面当然这很容易实现。我们自定义Processor:@AutoService(Processor.class)publicclassYellowPeachProcessorextendsAbstractProcessor{/***用于写java文件*/privateFilermFiler;/***可以理解为log*/privateMessagermMessager;/***注解检查器,用于判断被注解的field不是private的*/privateAnnotationCheckermAnnotationChecker;@Overridepublicsynchronizedvoidinit(ProcessingEnvironmentprocessingEnv){super.init(processingEnv);mFiler=processingEnv.getFiler();mMessager=processingEnv.getMessager();mAnnotationChecker=newAnnotationChecker(mMessager);}@Overridepublicbooleanprocess(Set?extendsTypeElementannotations,RoundEnvironmentroundEnv){//找到被注解的fieldSet?extendsElementset=roundEnv.getElementsAnnotatedWith(JoinView.class);if(set!=null){CodeGeneratorcodeGenerator=newCodeGenerator(mFiler,mMessager);for(Elementelement:set){//先检查权限if(!mAnnotationChecker.checkAnnotation(element)){returnfalse;}//把备注解的field添加到生成器里,准备用来生成代码codeGenerator.add((VariableElement)element);}//开始生成代码codeGenerator.generate();}returntrue;}@OverridepublicSetStringgetSupportedAnnotationTypes(){//添加支持的注解类型我们支持JoinViewSetStringset=newHashSet();set.add(JoinView.class.getCanonicalName());returnset;}@OverridepublicSourceVersiongetSupportedSourceVersion(){returnSourceVersion.RELEASE_7;}}整体代码还是很简单,不过里面有两个类我们依次看下实现方式。0x04:检查被注解的field的访问权限/***Createdbychanon16/10/15.*jiacheng.li@shanbay.com*/publicclassAnnotationChecker{privateMessagermMessager;publicAnnotationChecker(Messagermessager){mMessager=messager;}publicbooleancheckAnnotation(Elementelement){VariableElementvariableElement=(VariableElement)element;if(variableElement.getModifiers().contains(Modifier.PRIVATE)){mMessager.printMessage(Diagnostic.Kind.ERROR,JoinView不能用于privatefield:+variableElement.getEnclosingElement()+-+variableElement.getSimpleName());returnfalse;}returntrue;}}可以看到如果针对privatefield,我们是不能通过类似activity.mViewC的方式访问的,所以这里会报错。0x05:生成代码,这里比较复杂,我特意建一个Title进行解释。生成代码当我们收集到备注注解的field信息之后,我们就可以生成代码,不过怎么处理这些field是个问题。我们首先想到的就是创建一个Map,key为被注解域的class,而值就是它一系列的被注解的field:publicclassCodeGenerator{privateMapString,ListVariableElementmVariableElementMap=newHashMap();publicvoidadd(VariableElementelement){ListVariableElementvariableElements=mVariableElementMap.get(element.getEnclosingElement().toString());if(variableElements==null){variableElements=newArrayList();//获得被注解的class的名称作为键mVariableElementMap.put(element.getEnclosingElement().toString(),variableElements);}//当前class下备注解的fieldvariableElements.add(element);}}这里可能有些人对于element.getEnclosingElement().toString()1感到困惑,举个例子:packagecom.chan.yellowpeach;importandroid.support.v7.app.AppCompatActivity;importandroid.view.View;publicclassMainActivityextendsAppCompatActivity{@JoinViewViewmViewC;}这里element.getEnclosingElement().toString()返回的就是com.chan.yellowpeach.MainActivity,这必定是唯一的啊,所以作为key再合适不过了,而element就是对应的ViewmViewC,有了这些生成代码只是分分钟的事。我们可以尝试看下完整的代码:

1 / 15
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功