已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
使用APT减少MVP的冗余代码前言不知道从何时起,移动端开发都开始采用MVP。我们在认识到MVP有点的时候,也不妨会察觉到它其实也有很多恼人的地方,比如,我们针对每种状态渲染不同的视图: private void renderInit() mViewA.setVisibility(View.VISIBLE); mViewB.setVisibility(View.GONE); mViewC.setVisibility(View.GONE); mViewD.setVisibility(View.GONE); mViewE.setVisibility(View.GONE); private void renderSummary() mViewA.setVisibility(View.GONE); mViewB.setVisibility(View.VISIBLE); mViewC.setVisibility(View.GONE); mViewD.setVisibility(View.GONE); mViewE.setVisibility(View.GONE); 可以看到在这里,我们渲染Init状态时,把View A设为可见,把其他的View设为不可见,当我们又去渲染Summary状态是,又重复上面的动作,不过这次是吧View B设为可见。这种冗余代码(或者说是模板代码)非常的烦人,因为我们在复制粘贴的时候极有可能设置错误的View为可见了。那么我们有没有什么办法来避免这样的问题呢。其实是有的,我们不妨回忆下ButterKnife怎么做的对于findViewById这样的冗余代码,ButterKnife是采用注解的方式解决的: Bind(R.id.id_name) TextView m_name; Bind(R.id.id_who) TextView m_who; Bind(R.id.id_musicBar) MusicBar m_musicBar; Bind(R.id.id_playControl) ImageView m_bottomPlayControlView; Override protected IView createView() return this; Override protected void onCreate(Nullable Bundle savedInstanceState) 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出对象后以接口类型返回以保障代码整洁性。void setVisible(View. target)1的接口去遍历容器,如果容器中的View在集合target中,就设为可见,否则不可见。1:如果你最APT还不是很了解,建议阅读下鸿洋的文章鸿洋APT实现0x01: 在Android Studio里新建一个java工程: 这里写图片描述在java工程的build.gradle脚本里添加依赖:apply plugin: javasourceCompatibility = JavaVersion.VERSION_1_7targetCompatibility = JavaVersion.VERSION_1_7dependencies compile fileTree(dir: libs, include: *.jar) compile com.google.auto.service:auto-service:1.0-rc20x02: 然后我们定义注解:/* * Created by chan on 16/10/15. * */DocumentedRetention(RetentionPolicy.SOURCE)InheritedTarget(ElementType.FIELD)public interface JoinView 只能用于field,它用于标示我们要setVisibility的view,像这样: JoinView View mViewC;120x03: 当注解标示某个field之后,我们就可以拿到field的变量名,我们可以通过activity.mViewC的方式读取里面的值,不过这有个前提mView最起码应该是protected, 或者public的,但是我们还是选用protected,毕竟这样可以最大化数据的封装程度。如果是这样的话我们生成的类必须得和被注解的类在同一包下面当然这很容易实现。我们自定义Processor:AutoService(Processor.class)public class YellowPeachProcessor extends AbstractProcessor /* * 用于写java文件 */ private Filer mFiler; /* * 可以理解为log */ private Messager mMessager; /* * 注解检查器,用于判断被注解的field不是private的 */ private AnnotationChecker mAnnotationChecker; Override public synchronized void init(ProcessingEnvironment processingEnv) super.init(processingEnv); mFiler = processingEnv.getFiler(); mMessager = processingEnv.getMessager(); mAnnotationChecker = new AnnotationChecker(mMessager); Override public boolean process(Set annotations, RoundEnvironment roundEnv) /找到被注解的field Set set = roundEnv.getElementsAnnotatedWith(JoinView.class); if (set != null) CodeGenerator codeGenerator = new CodeGenerator(mFiler, mMessager); for (Element element : set) /先检查权限 if (!mAnnotationChecker.checkAnnotation(element) return false; /把备注解的field添加到生成器里,准备用来生成代码 codeGenerator.add(VariableElement) element); /开始生成代码 codeGenerator.generate(); return true; Override public Set getSupportedAnnotationTypes() /添加支持的注解类型 我们支持JoinView Set set = new HashSet(); set.add(JoinView.class.getCanonicalName(); return set; Override public SourceVersion getSupportedSourceVersion() return SourceVersion.RELEASE_7; 整体代码还是很简单,不过里面有两个类我们依次看下实现方式。0x04: 检查被注解的field的访问权限/* * Created by chan on 16/10/15. * */public class AnnotationChecker private Messager mMessager; public AnnotationChecker(Messager messager) mMessager = messager; public boolean checkAnnotation(Element element) VariableElement variableElement = (VariableElement) element; if (variableElement.getModifiers().contains(Modifier.PRIVATE) mMessager.printMessage(Diagnostic.Kind.ERROR, JoinView不能用于private field: + variableElement.getEnclosingElement() + - + variableElement.getSimpleName(); return false; return true; 可以看到如果针对private field,我们是不能通过类似activity.mViewC的方式访问的,所以这里会报错。0x05: 生成代码,这里比较复杂,我特意建一个Title进行解释。生成代码当我们收集到备注注解的field信息之后,我们就可以生成代码,不过怎么处理这些field是个问题。我们首先想到的就是创建一个Map, key为被注解域的class,而值就是它一系列的被注解的field:public class CodeGenerator private MapString, List mVariableElementMap = new HashMap(); public void add(VariableElement element) List variableElements = mVariableElementMap.get(element.getEnclosingElement().toString(); if (variableElements = null) variableElements = new ArrayList(); /获得被注解的class的名称作为键 mVariableElementMap.put(element.getEnclosingElement().toString(), variableElements); /当前class下备注解的field variableElements.add(element); 这里可能有些人对于element.getEnclosingElement().toString()1感到困惑,举个例子:package com.chan.yellowpeach;import android.support.v7.app.AppCompatActivity;import android.view.View;public class MainActivity extends AppCompatActivity JoinView View mViewC;这里element.getEnclosingElement().toString()返回的就是com.chan.yellowpeach.MainActivity,这必定是唯一的啊,所以作为key再合适不过了,而element就是对应的View mViewC,有了这些生成代码只是分分钟的事。我们可以尝试看下完整的代码:package com.chan.apt.core;import java.io.IOException;import java.io.Writer;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import cessing.Filer;import cessing.Messager;import javax.lang.model.element.Element;import javax.lang.model.element.VariableElement;import javax.tools.Diagnostic;import javax.tools.JavaFileObject;/* * Created by chan on 16/10/15. * */public class CodeGenerator private MapString, List mVariableElementMap = new HashMap(); /* * 用于写java文件 */ private Filer mFiler; /* * logger */ private Messager mMessager; /* * APT生成代码所在的包名 */ private String mPackage; public CodeGenerator(Filer filer, Messager messager) mFiler = filer; mMessager = messager; public void add(VariableElement element) List variableElements = mVariableElementMap.get(element.getEnclosingElement().toString(); if (variableElements = null) variableElements = new ArrayList(); /获得被注解的class的名称作为键 mVariableElementMap.put(element.getEnclosingElement().toString(), variableElements); /当前class下备注解的field variableElements.add(element); public void generate() if (mVariableElementMap.isEmpty() return; init(); try for (Map.EntryString, List entry : mVariableElementMap.entrySet() String clazzName = YellowPeach$ + entry.getKey().replaceAll(., $); JavaFileObject javaFileObject = mFiler.createSourceFile(mPackage + . + clazzName); mMessager.printMessage(Diagnostic.Kind.NOTE, 在 + mPackage + . + clazzName + 生成代码); Writer writer = javaFileObject.openWriter(); writer.write(generateSourceCode(entry, mPackage, clazzName); writer.flush(); writer.close(); catch (IOException e) e.printStackTrace(); private void init() /先获得包名 IteratorMap.EntryString, List iterator = mVariableElementMap.entrySet().iterator(); Map.EntryString, List elementEntry = iterator.next(); VariableElement variableElement = elementEntry.getValue().get(0); Element element = variableElement.getEnclosingElement(); while (element != null & element.getEnclosingElement() != null) mPackage = element.toString(); element = element.getEnclosingElement(); mPackage = mPackage.substring(0, mPackage.lastIndexOf(.); private static String generateSourceCode(Map.EntryString, List entry, String packageName, String clazzName) /包 StringBuilder stringBuilder = new StringBuilder(package ); stringBuilder.append(packageName); stringBuilder.append(;n); /import stringBuilder.append(import android.view.View;n + n + import com.chan.lib.Peach;n + n + import java.util.ArrayList;n + import java.util.List;); stringBuilder.append(public class ); stringBuilder.append(clazzName); stringBuilder.append( implements Peach n); /成员变量 stringBuilder.append(private List mViews = new ArrayList();n); /构造函数 stringBuilder.append(public ); stringBuilder.append(clazzName); stringBuilder.append(); stringBuilder.append(entry.getKey(); stringBuilder.append( o); for (VariableElement item : entry.getValue() stringBuilder.append(mViews.add(); stringBuilder.append(o.); stringBuilder.append(item.getSimpleName(); stringBuilder.append();); stringBuilder.append(); /override的内容 stringBuilder.append( Overriden + public void setVisible(View. target) n + n + for (View v : mViews) n + v.setVisibility(View.GONE);n + n + n + for (int i = 0; i target.length; +i) n + final int index = mViews.indexOf(targeti);n + if (index != -1) n + mViews.get(index).setVisibility(View.VISIBLE);n + n + n + ); /结尾 stringBuilder.append(); return stringBuilder.toString(); 从之前的例子可以看到在add(xxx)之后就是收集完所有的信息,我们所要做的就是调用codeGenerator.generate()生成代码在codeGenerator.generate()函数里,我们首先调用init来获取包名: private void init() /先获得包名 IteratorMap.EntryString, List iterator = mVariableElementMap.entrySet().iterator(); Map.EntryString, List elementEntry = iterator.next(); VariableElement variableElement = elementEntry.getValue().get(0); Element element = variableElement.getEnclosingElement(); while (element != null & element.getEnclosingElement() != null) mPackage = toString(); element = element.getEnclosingElement(); mPackage = mPackage.substring(0, mPackage.lastIndexOf(.); 读者可以通过打mMessager打log查看执行的过程,本身也比较简单,讲解却十分烦,光是例子就不少代码。在获得包名之后就是生成响应的java代码: for (Map.EntryString, List entry : mVariableElementMap.entrySet() /把.都换成$ String clazzName = YellowPeach$ + entry.getKey().replaceAll(., $); /指定java文件写入的位置 JavaFileObject javaFileObject = mFiler.createSourceFile(mPackage + . + clazzName); mMessager.printMessage(Diagnostic.Kind.NOTE, 在 + mPackage + . + clazzName + 生成代码); /开始写文件 Writer writer = javaFileObject.openWriter(); writer.write(generateSourceCode(entry, mPackage, clazzName); writer.flush(); writer.close(); 写文件再上文已经给出,其中没有多少技术难度,只有有一点核心代码需要解释: /构造函数 参数为被注解的class stringBuilder.append(public ); stringBuilder.append(clazzName); stringBuilder.append(); stringBuilder.append(entry.getKey(); stringBuilder.append( o); for (VariableElement item : entry.getValue() stringBuilder.append(mViews.add(); stringBuilder.append(o.); /返回field的名字 stringBuilder.append(item.getSimpleName(); stringBuilder.append();); 我们不妨看下APT生成的代码。如果你一切顺利地话,会在这个目录下看到apt代码: 这里写图片描述package com.chan.yellowpeach;import android.view.View;import com.chan.lib.Peach;import java.util.ArrayList;import java.util.List;public class YellowPeach$com$chan$yellowpeach$Main2Activity implements Peach private List mViews = new ArrayList(); public YellowPeach$com$chan$yellowpeach$Main2Activity( com.chan.yellowpeach.Main2Activity o) mViews.add(o.mView); Override public void setVisible(View. target) for (View v : mViews) v.setVisibility(View.GONE); for (int i = 0; i target.length; +i) final int index = mViews.indexOf(targeti); if (index != -1) mViews.get(index).setVisibility(View.VISIBLE); 还是很简单的,那么下面的问题就只剩下如何new一个apt生成的class的对象new 一个对象package com.chan.lib;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;/* * Created by chan on 16/10/15. * */public class YellowPeach public static Peach bind(Object o) try final String clazzName = o.getClass().getPackage().getName().toString() + .YellowPeach$ + o.getClass().getCanonicalName().replaceAll(., $); Class clazz = o.getClass().getClassLoader().loadClass(clazzName); Constructor constructors = clazz.getConstructors(); return (Peach) constructors0.newInstance(o); catch (ClassNotFoundException e) e.printStackTrace(); catch (IllegalAccessException e) StackTrace(); catch (InstantiationException e) e.printStackTrace(); catch (InvocationTargetException e) e.printStackTrace(); return null; 我们使用反射的方式获得APT生成的类,之后直接new出来然后作为Peach接口类型返回。我们看下客户端是如何使用的使用package com.chan.yellowpeach;import android.content.Intent;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import a
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025巴彦淖尔五原县卫健系统招11人历年真题库附答案解析
- 2026年中国农业发展银行校园招聘(河北招30人)历年真题汇编附答案解析
- 2026年(通讯维修工)理论知识考试题库及参考答案【研优卷】
- 2026年县直事业单位招聘公共基础知识真题200道及完整答案(名师系列)
- 2025年萍乡市人民医院招聘编外人员(第三批)4人历年真题汇编附答案解析
- 2025年下半年四川省第九地质大队考核招聘13人笔试模拟试卷附答案解析
- 2025年下半年象山县机关事业单位公开转任公务员和公开选聘事业单位工作人员29人模拟试卷附答案解析
- 2025年滨州无棣县财金投资集团有限公司公开招聘高层次人才历年真题库带答案解析
- 2025年11月沈阳市医疗卫生系统面向部分医学院校应届毕业生公开招聘175人历年真题汇编带答案解析
- 2025中铁上海设计院集团有限公司招聘8人历年真题库带答案解析
- 山东省精神卫生中心招聘试题及解析
- 企业员工廉洁行为规范培训课件
- JT-T 795-2023 事故汽车修复技术规范
- 计算机及网络运维服务方案
- 国家开放大学《数据结构》课程实验报告(实验2-线性表)参考答案
- DBJ50-200-2014建筑桩基础设计与施工验收规范
- 幼儿园财务审计报告范文
- 全国行政区划代码
- 大客户营销方法论
- 大唐南京发电厂消防安全考核规定
- YS/T 399-2013海绵铪
评论
0/150
提交评论