編譯註解----例項
這個 demo 是這篇部落格 裡的一個例子,光看沒看懂,又照著寫了一遍也還是很懵,為了更好的理解,幾乎把每一句核心程式碼都列印,都註釋了,這樣才略懂了一些在編譯時生成程式碼這個流程。
下面這個方法是最懵的,所以註釋寫了很多,裡面的各種元素都比較抽象,看的時候,甚至是第一次寫的時候,都不知道在寫什麼,通過列印把抽象的東西,還原成我們熟悉的東西就好理解多了。
/** * * @param set包含的是所有使用的[註解的資訊],例如BindView,ContentView * @param roundEnvironment 返回的是所有被註解的[元素],例如類,屬性等 * @return */ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { //mMessager.printMessage(Diagnostic.Kind.NOTE,"process..."); // 防止處理多次,要清空 mProxyMap.clear(); System.out.println("mProxyMap"+mProxyMap.toString()); // 獲取全部的帶有 bindview 註解的 element Set<? extends Element> elseWithBind = roundEnvironment.getElementsAnnotatedWith(BindView.class); System.out.println("elseWithBind: " + elseWithBind.toString()); //elseWithBind: [mTextView, mImageView] // 對bindview 進行迴圈,構建 proxyInfo 資訊 for (Element element : elseWithBind) { // 檢查 element 的合法性 checkSAnnotationValid(element,BindView.class); // 強轉成屬性元素 VariableElement variableElement = (VariableElement) element; System.out.println("variableElement: "+variableElement); //variableElement: mTextView System.out.println("-----"+ element.getEnclosingElement()); // 要獲取類元素的類名,直接用 element 也可以,強轉不是必須的。 // 屬性元素的外層一定是類元素 TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement(); System.out.println("typeElement: "+typeElement); // typeElement: com.limiao.annotationdemo.MainActivity // 獲取類元素的類名(全路徑名) String fqClassName = typeElement.getQualifiedName().toString(); System.out.println("fqClassName: "+fqClassName); //fqClassName: com.limiao.annotationdemo.MainActivity System.out.println("mProxyMap: "+mProxyMap); ProxyInfo proxyInfo = mProxyMap.get(fqClassName); if (proxyInfo == null){ proxyInfo = new ProxyInfo(mElementUtils,typeElement); // 以 class 名稱為 key,儲存到 mProxy 中 mProxyMap.put(fqClassName,proxyInfo); } System.out.println("proxyInfo:"+proxyInfo); // 獲取 bindview 註解,把資訊放入 proxyInfo 中 BindView bindAnnotation = element.getAnnotation(BindView.class); int id = bindAnnotation.value(); mMessager.printMessage(Diagnostic.Kind.NOTE,"proxyInfo:" + proxyInfo.toString()); mMessager.printMessage(Diagnostic.Kind.NOTE,"variableElement:" + variableElement); mMessager.printMessage(Diagnostic.Kind.NOTE,"id:" + id); // 上面的強轉要用到這裡,作為引數 proxyInfo.injectVarialbles.put(id,variableElement); } // 獲取所有的 ContentView 註解,操作原理和上面的 bindview 一樣 Set<? extends Element> contentAnnotations = roundEnvironment.getElementsAnnotatedWith(ContentView.class); for (Element element : contentAnnotations) { TypeElement typeElement = (TypeElement) element; String fqClassName= typeElement.getQualifiedName().toString(); ProxyInfo proxyInfo = mProxyMap.get(fqClassName); if (proxyInfo == null) { proxyInfo = new ProxyInfo(mElementUtils,typeElement); mProxyMap.put(fqClassName,proxyInfo); } ContentView contentViewAnnotation = element.getAnnotation(ContentView.class); proxyInfo.contentViewId = contentViewAnnotation.value(); } // 迴圈生成原始檔 for (String key : mProxyMap.keySet()) { ProxyInfo proxyInfo = mProxyMap.get(key); try { System.out.println("ProxyClassFullName: "+proxyInfo.getProxyClassFullName()); System.out.println("TypeElement: "+proxyInfo.getTypeElement()); // 建立一個 javaFile 檔案 // 第一個引數:建立的檔名,包含全路徑 // 第二個引數:與此檔案的建立有因果關聯的型別或包或模組元素,可以為null(文件的說明) //JavaFileObject jfo = processingEnv.getFiler().createSourceFile(proxyInfo.getProxyClassFullName(),proxyInfo.getTypeElement()); //試了一下,如果第二個引數為 null ,也沒有關係,還能正常編譯執行 JavaFileObject jfo = processingEnv.getFiler().createSourceFile(proxyInfo.getProxyClassFullName(),null); Writer writer = jfo.openWriter(); writer.write(proxyInfo.generateJavaCode()); writer.flush(); writer.close(); } catch (IOException e) { e.printStackTrace(); error(proxyInfo.getTypeElement(),"unable to write injector for type %s: %s",proxyInfo.getTypeElement(),e.getMessage()); } } return true; }
參考的這篇部落格的作者關於編譯註解寫了一個系列的文章,都很好,這裡整理一下貼出來,方便查閱。
深入理解編譯註解(二)annotationProcessor與android-apt
深入理解編譯註解(三)依賴關係 apt/annotationProcessor與Provided的區別
深入理解編譯註解(五)RetentionPolicy.SOURCE 和 RetentionPolicy.CLASS區別討論