




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、使用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
2、 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设为可见。这种冗余代码(或者说是模板代码)非常的烦人,因为我
3、们在复制粘贴的时候极有可能设置错误的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; Ove
4、rride 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(Vi
5、ew.VISIBLE); 操作。思路1:模仿ButterKnife对于要setVisibility的View我们使用注解来标示 2:当知道有哪些View要setVisibility后,我们可以把它们存到容器里 3:当外部要setVisibility某些View时,我们可以提供一个类似 4:为了避免APT生成的代码和现有的代码重复类名,我们可以尝试在APT的类名中出现$符号,但是这样用户用起来很难受,我们可以是APT生成的代码都实现某个接口,当new出对象后以接口类型返回以保障代码整洁性。void setVisible(View. target)1的接口去遍历容器,如果容器中的View在集合ta
6、rget中,就设为可见,否则不可见。1:如果你最APT还不是很了解,建议阅读下鸿洋的文章鸿洋APT实现0x01: 在Android Studio里新建一个java工程: 这里写图片描述在java工程的build.gradle脚本里添加依赖:apply plugin: 'java'sourceCompatibility = JavaVersion.VERSION_1_7targetCompatibility = JavaVersion.VERSION_1_7dependencies compile fileTree(dir: 'libs', include:
7、39;*.jar') compile 'com.google.auto.service:auto-service:1.0-rc2'0x02: 然后我们定义注解:/* * Created by chan on 16/10/15. * jiacheng.li */DocumentedRetention(RetentionPolicy.SOURCE)InheritedTarget(ElementType.FIELD)public interface JoinView 只能用于field,它用于标示我们要setVisibility的view,像这样: JoinView View
8、 mViewC;120x03: 当注解标示某个field之后,我们就可以拿到field的变量名,我们可以通过activity.mViewC的方式读取里面的值,不过这有个前提mView最起码应该是protected, 或者public的,但是我们还是选用protected,毕竟这样可以最大化数据的封装程度。如果是这样的话我们生成的类必须得和被注解的类在同一包下面当然这很容易实现。我们自定义Processor:AutoService(Processor.class)public class YellowPeachProcessor extends AbstractProcessor /* * 用于
9、写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();
10、mMessager = processingEnv.getMessager(); mAnnotationChecker = new AnnotationChecker(mMessager); Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) /找到被注解的field Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(JoinView.class);
11、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 tru
12、e; Override public Set<String> getSupportedAnnotationTypes() /添加支持的注解类型 我们支持JoinView Set<String> set = new HashSet<>(); set.add(JoinView.class.getCanonicalName(); return set; Override public SourceVersion getSupportedSourceVersion() return SourceVersion.RELEASE_7; 整体代码还是很简单,不过里面有两个
13、类我们依次看下实现方式。0x04: 检查被注解的field的访问权限/* * Created by chan on 16/10/15. * jiacheng.li */public class AnnotationChecker private Messager mMessager; public AnnotationChecker(Messager messager) mMessager = messager; public boolean checkAnnotation(Element element) VariableElement variableElement = (Variable
14、Element) element; if (variableElement.getModifiers().contains(Modifier.PRIVATE) mMessager.printMessage(Diagnostic.Kind.ERROR, "JoinView不能用于private field: " + variableElement.getEnclosingElement() + " -> " + variableElement.getSimpleName(); return false; return true; 可以看到如果针对pr
15、ivate field,我们是不能通过类似activity.mViewC的方式访问的,所以这里会报错。0x05: 生成代码,这里比较复杂,我特意建一个Title进行解释。生成代码当我们收集到备注注解的field信息之后,我们就可以生成代码,不过怎么处理这些field是个问题。我们首先想到的就是创建一个Map, key为被注解域的class,而值就是它一系列的被注解的field:public class CodeGenerator private Map<String, List<VariableElement>> mVariableElementMap = new Ha
16、shMap<>(); public void add(VariableElement element) List<VariableElement> variableElements = mVariableElementMap.get(element.getEnclosingElement().toString(); if (variableElements = null) variableElements = new ArrayList<>(); /获得被注解的class的名称作为键 mVariableElementMap.put(element.getEn
17、closingElement().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 AppCom
18、patActivity 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
19、 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 ja
20、vax.tools.JavaFileObject;/* * Created by chan on 16/10/15. * jiacheng.li */public class CodeGenerator private Map<String, List<VariableElement>> mVariableElementMap = new HashMap<>(); /* * 用于写java文件 */ private Filer mFiler; /* * logger */ private Messager mMessager; /* * APT生成代码所在的
21、包名 */ private String mPackage; public CodeGenerator(Filer filer, Messager messager) mFiler = filer; mMessager = messager; public void add(VariableElement element) List<VariableElement> variableElements = mVariableElementMap.get(element.getEnclosingElement().toString(); if (variableElements = n
22、ull) 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.Entry<St
23、ring, List<VariableElement>> entry : mVariableElementMap.entrySet() String clazzName = "YellowPeach$" + entry.getKey().replaceAll(".", "$"); JavaFileObject javaFileObject = mFiler.createSourceFile(mPackage + "." + clazzName); mMessager.printMessage(Di
24、agnostic.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() /先获得包
25、名 Iterator<Map.Entry<String, List<VariableElement>>> iterator = mVariableElementMap.entrySet().iterator(); Map.Entry<String, List<VariableElement>> elementEntry = iterator.next(); VariableElement variableElement = elementEntry.getValue().get(0); Element element = variab
26、leElement.getEnclosingElement(); while (element != null && element.getEnclosingElement() != null) mPackage = element.toString(); element = element.getEnclosingElement(); mPackage = mPackage.substring(0, mPackage.lastIndexOf("."); private static String generateSourceCode(Map.Entry&l
27、t;String, List<VariableElement>> entry, String packageName, String clazzName) /包 StringBuilder stringBuilder = new StringBuilder("package "); stringBuilder.append(packageName); stringBuilder.append("n"); /import stringBuilder.append("import android.view.View;n"
28、 + "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"); /成员变量
29、stringBuilder.append("private List<View> mViews = new ArrayList<>();n"); /构造函数 stringBuilder.append("public "); stringBuilder.append(clazzName); stringBuilder.append("("); stringBuilder.append(entry.getKey(); stringBuilder.append(" o)"); for (Varia
30、bleElement item : entry.getValue() stringBuilder.append("mViews.add("); stringBuilder.append("o."); stringBuilder.append(item.getSimpleName(); stringBuilder.append(");"); stringBuilder.append(""); /override的内容 stringBuilder.append(" Overriden" + &quo
31、t; 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" +
32、" if (index != -1) n" + " mViews.get(index).setVisibility(View.VISIBLE);n" + " n" + " n" + " "); /结尾 stringBuilder.append(""); return stringBuilder.toString(); 从之前的例子可以看到在add(xxx)之后就是收集完所有的信息,我们所要做的就是调用codeGenerator.generate()生成代码在codeGener
33、ator.generate()函数里,我们首先调用init来获取包名: private void init() /先获得包名 Iterator<Map.Entry<String, List<VariableElement>>> iterator = mVariableElementMap.entrySet().iterator(); Map.Entry<String, List<VariableElement>> elementEntry = iterator.next(); VariableElement variableEleme
34、nt = 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(".");
35、 读者可以通过打mMessager打log查看执行的过程,本身也比较简单,讲解却十分烦,光是例子就不少代码。在获得包名之后就是生成响应的java代码: for (Map.Entry<String, List<VariableElement>> entry : mVariableElementMap.entrySet() /把.都换成$ String clazzName = "YellowPeach$" + entry.getKey().replaceAll(".", "$"); /指定java文件写入的位置 J
36、avaFileObject javaFileObject = mFiler.createSourceFile(mPackage + "." + clazzName); mMessager.printMessage(Diagnostic.Kind.NOTE, "在" + mPackage + "." + clazzName + "生成代码"); /开始写文件 Writer writer = javaFileObject.openWriter(); writer.write(generateSourceCode(ent
37、ry, mPackage, clazzName); writer.flush(); writer.close(); 写文件再上文已经给出,其中没有多少技术难度,只有有一点核心代码需要解释: /构造函数 参数为被注解的class stringBuilder.append("public "); stringBuilder.append(clazzName); stringBuilder.append("("); stringBuilder.append(entry.getKey(); stringBuilder.append(" o)"
38、); for (VariableElement item : entry.getValue() stringBuilder.append("mViews.add("); stringBuilder.append("o."); /返回field的名字 stringBuilder.append(item.getSimpleName(); stringBuilder.append(");"); 我们不妨看下APT生成的代码。如果你一切顺利地话,会在这个目录下看到apt代码: 这里写图片描述package com.chan.yellowpea
39、ch;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<View> mViews = new ArrayList<>(); public YellowPeach$com$chan$yellowpeach$Main2Activity( com.ch
40、an.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); 还是
41、很简单的,那么下面的问题就只剩下如何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. * jiacheng.li */public class YellowPeach public static Peach bind(Object o) try final String clazzName = o.getCl
42、ass().getPackage().getName().toString() + ".YellowPeach$" + o.getClass().getCanonicalName().replaceAll(".", "$"); Class<?> clazz = o.getClass().getClassLoader().loadClass(clazzName); Constructor<?> constructors = clazz.getConstructors(); return (Peach) const
43、ructors0.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
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 创造性思维土木工程师试题及答案
- 书法艺术导论试题及答案
- 注册土木工程师考试能力提升路程试题及答案
- 教育教学法试题及答案
- 小学教师教育教学反思制度试题及答案
- 化学反应动力学课程要点试题及答案
- 国画入学测试题及答案
- 中考湖州英语试题及答案
- 幼儿园手工数学活动试题及答案
- 建筑施工现场风险评估报告的撰写试题及答案
- 海康威视校招在线测评题库
- 电网两票培训课件
- 《土地集约利用》课件
- 小学英语(完整版)现在进行时练习题附答案
- 无违法犯罪记录证明申请表(个人)
- 衡水介绍-衡水简介PPT(经典版)
- 性激素六项的解读 课件
- 模具设计与制造毕业设计
- 2023年福建三明市初中毕业班数学质量检测卷(附答案)
- 金蝶固定资产管理系统
- LY/T 2457-2015西南桦培育技术规程
评论
0/150
提交评论