




版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
1、使用APT減少MVP的冗余代碼前言不知道從何時起,移動端開發(fā)都開始采用MVP。我們在認識到MVP有點的時候,也不妨會察覺到它其實也有很多惱人的地方,比如,我們針對每種狀態(tài)渲染不同的視圖: 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狀態(tài)時,把View A設為可見,把其他的View設為不可見,當我們又去渲染Summary狀態(tài)是,又重復上面的動作,不過這次是吧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); . 在執(zhí)行ButterKnife.bind(this);1后,ButterKnife會采用APT自動生成代碼執(zhí)行findViewById操作。 同樣的,我們在解決MVP冗余代碼時,我們也可以使用APT生成代碼執(zhí)行 setVisibility(Vi
5、ew.VISIBLE); 操作。思路1:模仿ButterKnife對于要setVisibility的View我們使用注解來標示 2:當知道有哪些View要setVisibility后,我們可以把它們存到容器里 3:當外部要setVisibility某些View時,我們可以提供一個類似 4:為了避免APT生成的代碼和現(xiàn)有的代碼重復類名,我們可以嘗試在APT的類名中出現(xiàn)$符號,但是這樣用戶用起來很難受,我們可以是APT生成的代碼都實現(xiàn)某個接口,當new出對象后以接口類型返回以保障代碼整潔性。void setVisible(View. target)1的接口去遍歷容器,如果容器中的View在集合ta
6、rget中,就設為可見,否則不可見。1:如果你最APT還不是很了解,建議閱讀下鴻洋的文章鴻洋APT實現(xiàn)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,畢竟這樣可以最大化數(shù)據(jù)的封裝程度。如果是這樣的話我們生成的類必須得和被注解的類在同一包下面當然這很容易實現(xiàn)。我們自定義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、類我們依次看下實現(xiàn)方式。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是個問題。我們首先想到的就是創(chuàng)建一個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"); /構造函數(shù) 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的內(nèi)容 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)之后就是收集完所有的信息,我們所要做的就是調(diào)用codeGenerator.generate()生成代碼在codeGener
33、ator.generate()函數(shù)里,我們首先調(diào)用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查看執(zhí)行的過程,本身也比較簡單,講解卻十分煩,光是例子就不少代碼。在獲得包名之后就是生成響應的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(); 寫文件再上文已經(jīng)給出,其中沒有多少技術難度,只有有一點核心代碼需要解釋: /構造函數(shù) 參數(shù)為被注解的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. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年機電一體化專業(yè)考試試題及答案
- 2025年環(huán)境藝術設計專業(yè)答辯試卷及答案
- 2025年多元文化教育專業(yè)考試試卷及答案
- 物資采購供貨管理制度
- 特殊群體品牌管理制度
- 特殊設備安全管理制度
- 特色小鎮(zhèn)開發(fā)管理制度
- 特許經(jīng)營合同管理制度
- 獵頭業(yè)務過程管理制度
- 豬場冬季物料管理制度
- 2025年河北省中考麒麟卷生物(三)及答案
- 2025年河北省中考麒麟卷生物(二)及答案
- 2025年中國鐵路濟南局集團招聘筆試沖刺題(帶答案解析)
- 2025年河北省萬唯中考定心卷地理(二)
- 2025年高考全國二卷英語高考真題含解析
- 2025年全國高考一卷英語真題(解析版)
- 上海市靜安區(qū)2023-2024學年八年級下學期期末數(shù)學試題(無答案)
- 引水罐的設計計算
- 三年級譯林版英語下學期按要求寫句子專項強化練習題
- 電纜接線工藝設計規(guī)范流程
- 中醫(yī)經(jīng)絡減肥課件
評論
0/150
提交評論