前面是一些名詞的解釋和說明存捺,例子創(chuàng)建可以直接看后面的“創(chuàng)建過程”的章節(jié)。
本文主要參考網上各個大神的總結和自己使用過程的問題完成的,如果你發(fā)現(xiàn)有你的文章的內容爷绘,請通知我,我將你的文章放在引用目錄里进倍,謝謝土至。
如有錯誤和需要勘正的地方,請指教猾昆。
Annotation
注解是JDK5.0引入的新特性
JDK5.0 當時提供了三個注解: @Deprecated(廢棄的陶因、過時的)、@Override(重寫毡庆、覆蓋)坑赡、@SuppressWarnings(壓縮警告)
- 注解相當于一種標記,在程序中加入注解就等于為程序打上某種標記么抗。
- javac編譯器毅否、開發(fā)工具和其他程序可以通過反射來了解類及各元素上的標記信息。
- 注解可以加在包蝇刀、類螟加、屬性、方法吞琐、方法的參數(shù)以及局部變量上捆探。
元注解(meta-annotaion)
JDK5.0引入,負責注解其他注解
元注解包含:
- @Target:規(guī)定自定義Annotation所修飾的對象范圍
- ElementType.CONSTRUCTOR站粟,構造器聲明
- ElementType.FIELD黍图,成員變量、對象奴烙、屬性(包括enum實例)
- ElementType.LOCAL_VARIABLE助被,局部變量聲明
- ElementType.METHOD,方法聲明
- ElementType.PACKAGE切诀,包聲明
- ElementType.PARAMETER揩环,參數(shù)聲明
- ElementType.TYPE,類幅虑、接口(包括注解類型)或enum聲明
- 同時支持多種范圍設定丰滑,e.g.:
@Target({ElementType.TYPE,ElementType.FIELD})
- @Retention:對Annotation的“生命周期”限制,表示需要在什么級別保存該注釋信息
- RetentionPolicy.SOURCE倒庵,在源文件中有效
- RetentionPolicy.CLASS褒墨,在class文件中有效
- RetentionPolicy.RUNTIME炫刷,在運行時有效
- @Documented:用于描述其他類型的annotation應該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類工具文檔化貌亭,Documented是一個標記注解柬唯,沒有成員
- @Inherited:標記注解,被標注的類型是被繼承的圃庭,主要用于類锄奢。
自定義注解
使用@interface自定義注釋時,自動繼承了java.lang.annotation.Annotation接口剧腻,由編譯程序自動完成其他細節(jié)拘央。在定義注解時,不能繼承其他的注解或接口书在。@interface用來聲明一個注解灰伟,其中的每一個方法實際上是聲明了一個配置參數(shù)。方法的名稱就是參數(shù)的名稱儒旬,返回值類型就是參數(shù)類型(只能是基本類型栏账、Class、String栈源、enum)挡爵。可以通過default來聲明參數(shù)的默認值甚垦。
注解使用的參數(shù)
只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據類型和String,Enum,Class,Annotation等數(shù)據類型茶鹃,以及這些類型的數(shù)組。
必須有明確的值艰亮,可以在定義注解的默認值中指定闭翩;或者 使用注解時指定,非基本類型的注解元素的值不可為null迄埃。
自定義注解的局限
- 不能修改已有的源文件疗韵,可以創(chuàng)建新的源文件
APT(Used in Android Studio)
APT(Annotation Processing Tool)是一種處理注釋的工具,它對源碼文件進行檢測找出其中的Annotation侄非,使用Annotation進行額外的處理蕉汪。
Annotation處理器在處理Annotation時可以根據源文件中的Annotation生成額外的源文件和其他的文件(文件具體內容由Annotation處理器的編寫者決定),APT還會編譯生成的源文件和原來的源文件彩库,將它們一起生成class文件。
創(chuàng)建過程
-
創(chuàng)建一個Java Library(比如叫做annotation)先蒋,放置自定義的annotation和關聯(lián)代碼骇钦。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface Test { }
-
配置該library的build.gradle
該配置信息在android studio將自動生成
apply plugin: 'java' sourceCompatibility = 1.7 targetCompatibility = 1.7 dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) }
-
創(chuàng)建一個Java library(比如叫做compiler),放置自定義的Annotation Processor竞漾。
AutoService是一個開源庫提供的注冊Processor的方法眯搭。
@AutoService(Processor.class) public class TestProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes() { return Collections.singleton(Test.class.getCanonicalName()); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { return false; } }
-
配置build.gradle
apply plugin: 'java' sourceCompatibility = 1.7 targetCompatibility = 1.7 dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.google.auto.service:auto-service:1.0-rc2' compile 'com.squareup:javapoet:1.7.0' compile project(':annotation') }
-
配置Project的build.gradle加在buildscript的dependencies中
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
-
配置app的build.gradle增加pluge
apply plugin: 'com.neenbedankt.android-apt'
dependencies中增加
compile project(':annotation') apt project(':compiler')
-
新版本android studio 5和6的配置會報錯窥翩,只要直接使用annotationProcessor引入依賴就好,即源碼如下:
dependencies中增加
compile project(':annotation') annotationProcessor project(':compiler')
Debug自定義Annotaion Processor
編譯過程:
- javac啟動一個JVM運行processor鳞仙;
- compiler編譯代碼寇蚊,所以processor生成的代碼可以被編譯進apk;
- IDE啟動JVM運行代碼棍好。
-
將需要debug的processor加入compiler中
- 在setting中打開Enable annotation processing(setting[需要在File->Other Settings->Default Setting 或者 Android studio歡迎頁面中的Configure->Preferences]->Build,Execution,Deployment->Compiler->Annotation Processors)
- Processor FQ Name中增加一個自定義Processor的全路徑
-
設置javac為debug模式
在Annotation Processors的設置項上面的Java Compiler中
-
設置Additional command line parameters中設置:
-J-Xdebug -J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
-
增加一個遠程編譯選項并啟動編譯
- 打開項目的Run/Debug Configurations
- 在左邊欄中仗岸,點擊“+”增加一個Remote,確定借笙。
-
設置項目gradle.properties增加:
org.gradle.jvmargs=-Xmx1536m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
點擊調試按鈕扒怖,打開遠程調試功能(這個時候自定義processor中的斷點顯示生效)
重新編譯工程,斷點響應
注1:1业稼、2步驟在實際中發(fā)現(xiàn)盗痒,不是必須要設置的,具體1低散、2步驟會有什么作用俯邓,還在研究。
注2:當遇到unable to open debugger port: connection refused的錯誤時熔号,是因為:
- 當前的端口號被占用稽鞭,你需要找到一個你電腦未被占用的端口號進行調試
- 工程引用的所有gradle.properties文件中,存在未加調試命令的文件
常用操作
- 獲取VariableElement的類型的TypeElement:Types.asElement(VariableElement.asType()):
- 注意:如果類型是基本類型跨嘉,則返回值是null川慌,需要通過Types.boxedClass((PrimitiveType) VariableElement.asType());獲取到裝箱類的類型,基本類型只有PrimitiveType祠乃,他是一個TypeMirror梦重。
- TypeElement可以通過getEnclosingElement().toString()獲取包名,getSimpleName().toString()獲取類名
- 數(shù)組的處理:asType()亮瓷,返回ArrayType琴拧,getComponentType()返回是哪種類型的數(shù)組
- List的處理:asType(),返回DeclaredType嘱支,getTypeArguments()獲取是哪種類型的list蚓胸。
相關類說明
AbstractProcessor
- init:ProcessingEnvironment,參數(shù)ProcessingEvnironment提供很多有用的工具類除师。
- process:Set<TypeElement> RoundEnvironment沛膳,RoundEvnironment可以用來查詢出包含特定注解的被注解元素。
- 注意問題:
- 可能你使用的源文件還未編譯汛聚,會拋出異常MirroredTypeException
- 可能存在process多次調用锹安,因為處理之后有新的文件生成,所以需要注意內部數(shù)據的更新和清空問題。
- 注意問題:
- getSupportedAnnotationTypes叹哭,指定這個注解處理器是注冊給哪個注解的忍宋。
- getSupportedSourceVersion,指定你使用的Java版本风罩。
ProcessingEnvironment
-
getElementUtils:獲取element的操作類糠排,用于掃描源碼文件,源碼的每一個部分都是一個element超升,如圖:
package com.example; // PackageElement public class Foo { // TypeElement private int a; // VariableElement private Foo other; // VariableElement public Foo () {} // ExecuteableElement public void setA ( // ExecuteableElement int newA // TypeElement ) {} }
Element可以是類入宦、方法、變量等等廓俭。
getMessager: 獲取Messager的操作類云石,用于報告錯誤、警告 以及 提示信息的途徑研乒。
Test
- testCompile 'com.google.testing.compile:compile-testing:0.8':但是因為編譯出來的文件中存在當前工程沒有的類汹忠,所以雖然測試用例過了,但是依然會報錯雹熬。
使用缺點
- 額外生成一定量的類:編譯時間加長宽菜、包變大
- 可讀性比較差:對代碼的理解需要一定的門檻