使用APT減少MVP的冗余代碼_第1頁
使用APT減少MVP的冗余代碼_第2頁
使用APT減少MVP的冗余代碼_第3頁
已閱讀5頁,還剩10頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

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 設(shè)為可見,把其他的 View 設(shè)為不可見, 當我們又去渲染 Summary 狀態(tài)是,又重復上面的動作,不過這次是吧 View B 設(shè)為可見。這 種冗余代碼 (或者說是模板代碼)

3、 非常的煩人, 因為我們在復制粘貼的時候極有可能設(shè)置錯 誤的 View 為可見了。那么我們有沒有什么辦法來避免這樣的問題呢。其實是有的,我們不 妨回憶下 ButterKnife 怎么做的對于 findViewById 這樣的冗余代碼, ButterKnife 是采用注 解的方式解決的:TextView m_name;TextView m_who;MusicBar m_musicBar;ImageView m_bottomPlayControlView;Overrideprotected IView createView() return this;Overrideprotected void

4、onCreate(Nullable Bundle savedInstanceState) super.onCreate(savedInstanceState);ButterKnife.bind(this);在執(zhí)行ButterKnife.bind(this);1后, ButterKnife 會采用 APT 自動生成代碼執(zhí)行 findViewById 操作。 同樣的,我們在解決MVP冗余代碼時,我們也可以使用APT生成代碼執(zhí)行setVisibility(View.VISIBLE); 操作。思路1:模仿 ButterKnife 對于要 setVisibility 的 View 我們使用注解來標示 2

5、:當知道有哪些 View 要 setVisibility 后,我們可以把它們存到容器里 3:當外部要 setVisibility 某些 View 時,我們可以提供一個類似4:為了避免APT生成的代碼和現(xiàn)有的代碼重復類名,我們可以嘗試在 APT的類名中出現(xiàn)$符號,但是這樣用戶用起來很難受,我們可以是APT生成的代碼都實現(xiàn)某個接口,當 new出對象后以接口類型返回以保障代碼整潔性。void setVisible(View. target)1 的接口去遍歷容器,如果容器中的 View 在集合 target 中,就設(shè)為可見,否則不可見。1:如果你最APT還不是很了解,建議閱讀下鴻洋的文章鴻洋APT實現(xiàn)

6、0x01:在 Android Studio 里新建一個 java 工程: 這里寫圖片描述在 java 工程的 build.gradle 腳本里添加依賴:apply plugin: 'java' sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 dependencies compile fileTree(dir: 'libs', include: '*.jar')0x02:然后我們定義注解:/* Created by

7、 chan on 16 /10/15.* */DocumentedRetention(RetentionPolicy.SOURCE)InheritedTarget(ElementType.FIELD)public interface JoinView 只能用于field,它用于標示我們要 setvisibility的view,像這樣:JoinViewView mViewC;120x03:當注解標示某個 field 之后,我們就可以拿到 field 的變量名,我們可以通過 activity.mViewC 的方式讀取里面的值,不過這有個前提 mView 最起碼應(yīng)該是 protected, 或者 p

8、ublic 的, 但是我們還是選用 protected ,畢竟這樣可以最大化數(shù)據(jù)的封裝程度。如果是這樣的話我們 生成的類必須得和被注解的類在同一包下面當然這很容易實現(xiàn)。我們自定義 Processor: AutoService(Processor.class)public class YellowPeachProcessor extends AbstractProcessor * 用于寫 java 文件 */private Filer mFiler;/* 可以理解為 log*/private Messager mMessager;* 注解檢查器,用于判斷被注解的 field 不是 private

9、 的*/private AnnotationChecker mAnnotationChecker;Overridepublic synchronized void init(ProcessingEnvironment processingEnv) super.init(processingEnv);mFiler = processingEnv.getFiler(); mMessager = processingEnv.getMessager(); mAnnotationChecker = new AnnotationChecker(mMessager);Overridepublic boole

10、an process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) / 找到被注解的 fieldSet<? extends Element> set = roundEnv.getElementsAnnotatedWith(JoinView.class);if (set != null) CodeGenerator codeGenerator = new CodeGenerator(mFiler, mMessager); for (Element element : set) / 先檢

11、查權(quán)限if (!mAnnotationChecker.checkAnnotation(element) return false;/ 把備注解的 field 添加到生成器里,準備用來生成代碼 codeGenerator.add(VariableElement) element);/ 開始生成代碼 codeGenerator.generate();return true;Overridepublic Set<String> getSupportedAnnotationTypes() / 添加支持的注解類型 我們支持 JoinView Set<String> set = r

12、eturn set;Overridepublic SourceVersion getSupportedSourceVersion() return SourceVersion.RELEASE_7;整體代碼還是很簡單,不過里面有兩個類我們依次看下實現(xiàn)方式。0x04: 檢查被注解的 field 的訪問權(quán)限/* Created by chan on 16 /10/15.* */public class AnnotationChecker private Messager mMessager;public AnnotationChecker(Messager messager) mMessager =

13、 messager;public boolean checkAnnotation(Element element) VariableElement variableElement = (VariableElement) element;if (variableElement.getModifiers().contains(Modifier.PRIVATE) 不 能 用 于 private field: "+ variableElement.getEnclosingElement() + " -> " + variableElement.getSimpleNa

14、me();return false;return true;可以看到如果針對 private field ,我們是不能通過類似 activity.mViewC 的方式訪問的,所以 這里會報錯。0x05:生成代碼,這里比較復雜,我特意建一個 Title 進行解釋。生成代碼當我們收集到備注注解的 field 信息之后,我們就可以生成代碼,不過怎么處理這些 field 是 個問題。我們首先想到的就是創(chuàng)建一個Map, key為被注解域的class,而值就是它一系列的被注解的 field :public class CodeGenerator private Map<String, List<

15、;VariableElement>> mVariableElementMap = new HashMap<>();public void add(VariableElement element) List<VariableElement>variableElements=mVariableElementMap.get(element.getEnclosingElement().toString();if (variableElements = null) variableElements = new ArrayList<>();/獲得被注解的cl

16、ass的名稱作為鍵mVariableElementMap.put(element.getEnclosingElement().toString(), variableElements);/ 當前 class 下備注解的 field variableElements.add(element);這里可能有些人對于element.getEnclosingElement().toString()1感到困惑,舉個例子:public class MainActivity extends AppCompatActivity JoinView View mViewC;這里 element.getEnclosi

17、ngElement().toString() 返回的就是 , 這必定是唯一的啊,所以作為 key 再合適不過了,而 element 就是對應(yīng)的 View mViewC ,有 了這些生成代碼只是分分鐘的事。我們可以嘗試看下完整的代碼:* Created by chan on 16 /10/15.* */ public class CodeGenerator private Map<String, List<VariableElement>> mVariableElementMap = new HashMap<>();/* 用于寫 java 文件 */priva

18、te 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<VariableElement> variableElements = mVariableElementMap.get(el

19、ement.getEnclosingElement().toString();if (variableElements = null) variableElements = new ArrayList<>();/ 獲得被注解的 class 的名稱作為鍵 mVariableElementMap.put(element.getEnclosingElement().toString(), variableElements);/ 當前 class 下備注解的 field variableElements.add(element);public void generate() if (mVa

20、riableElementMap.isEmpty() return;init();try for (Map.Entry<String, List<VariableElement>> entry : mVariableElementMap.entrySet() String clazzName = "YellowPeach$" + entry.getKey().replaceAll(".", "$"); JavaFileObject javaFileObject = mFiler.createSourceFile

21、(mPackage + "." + clazzName);"在 " + mPackage + "." + clazzName + "生成代碼 ");Writer writer = javaFileObject.openWriter(); writer.write(generateSourceCode(entry, mPackage, clazzName); writer.flush();writer.close(); catch (IOException e) e.printStackTrace();private

22、 void init() / 先獲得包名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 e

23、lement = variableElement.getEnclosingElement(); while (element != null && element.getEnclosingElement() != null) mPackage = element.toString();element = element.getEnclosingElement();mPackage = mPackage.substring(0, mPackage.lastIndexOf(".");private static String generateSourceCode

24、(Map.Entry<String, List<VariableElement>> entry, String packageName, String clazzName) / 包StringBuilder stringBuilder = new StringBuilder("package "); stringBuilder.append(packageName);stringBuilder.append("n");+ "n" +"n" +stringBuilder.append(&quo

25、t;public class "); stringBuilder.append(clazzName);stringBuilder.append(" implements Peach n");/ 成員變量 stringBuilder.append("private List<View> mViews = new ArrayList<>();n");/ 構(gòu)造函數(shù) stringBuilder.append("public ");stringBuilder.append(clazzName);stringBu

26、ilder.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(&quo

27、t;);");stringBuilder.append("");/override 的內(nèi)容 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

28、= 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" +n/ 結(jié)尾stringBuilder.append("");return stringBuilder.toString();從之前的

29、例子可以看到在add(xxx) 之后就是收集完所有的信息,我們所要做的就是調(diào)用codeGenerator.generate() 生成代碼在 codeGenerator.generate() 函數(shù)里,我們首先調(diào)用 init 來獲取包名:private void init() / 先獲得包名List<VariableElement>>>iteratorIterator<Map.Entry<String, mVariableElementMap.entrySet().iterator();Map.Entry<String, List<VariableE

30、lement>> elementEntry = iterator.next();VariableElement variableElement = elementEntry.getValue().get(0);Element element = variableElement.getEnclosingElement();while (element != null && element.getEnclosingElement() != null) mPackage = = element.getEnclosingElement();mPackage = mPacka

31、ge.substring(0, mPackage.lastIndexOf(".");讀者可以通過打 mMessager 打 log 查看執(zhí)行的過程, 本身也比較簡單, 講解卻十分煩, 光是 例子就不少代碼。在獲得包名之后就是生成響應(yīng)的 java 代碼:for (Map.Entry<String, List<VariableElement>> entry : mVariableElementMap.entrySet() /把.都換成 $String clazzName = "YellowPeach$" + entry.getKey(

32、).replaceAll(".", "$");/ 指定 java 文件寫入的位置JavaFileObject javaFileObject = mFiler.createSourceFile(mPackage + "." + clazzName);"在 " + mPackage + "." + clazzName + "生成代碼 ");/ 開始寫文件Writer writer = javaFileObject.openWriter();writer.write(generat

33、eSourceCode(entry, mPackage, clazzName); writer.flush();writer.close();寫文件再上文已經(jīng)給出,其中沒有多少技術(shù)難度,只有有一點核心代碼需要解釋:/ 構(gòu)造函數(shù) 參數(shù)為被注解的 classstringBuilder.append("public ");stringBuilder.append(clazzName);stringBuilder.append("(");stringBuilder.append(entry.getKey();stringBuilder.append("

34、 o)");for (VariableElement item : entry.getValue() stringBuilder.append("mViews.add("); stringBuilder.append("o.");/ 返回 field 的名字 stringBuilder.append(item.getSimpleName(); stringBuilder.append(");");我們不妨看下 APT 生成的代碼。如果你一切順利地話,會在這個目錄下看到apt 代碼:這里寫圖片描述public class Ye

35、llowPeach$com$chan$yellowpeach$Main2Activity implements Peach private List<View> mViews = new ArrayList<>(); mViews.add(o.mView);Overridepublic void setVisible(View. target) for (View v : mViews) v.setVisibility(View.GONE);for (int i = 0; i < target.length; +i) final int index = mView

36、s.indexOf(targeti);if (index != -1) mViews.get(index).setVisibility(View.VISIBLE);還是很簡單的,那么下面的問題就只剩下如何 new 一個 apt 生成的 class 的對象new 一個對象/* 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.getCl

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論