1艳吠、什么是注解
An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.
翻譯:
注釋是元數據的一種形式陵吸,可以添加到Java源代碼中渔扎√寐龋可以注釋類误债、方法颗管、變量玫锋、參數和包。注釋對它們注釋的代碼的操作沒有直接影響权薯。
????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????--by 百度翻譯
個人理解注解就是一個標記姑躲,可以標記在類、字段盟蚣、方法等上面黍析,如果我們對這個標記不做任何處理那么其實它不起任何作用
2、元注解
元注解就是注解的注解屎开,主要有四個:@Target阐枣、@Retention、@Inherited奄抽、@Documented
比如:
其中Target表示注解的作用范圍取ElementType中的值
Retention定義了注解在哪個級別可用蔼两,有三個取值:
RetentionPolicy.SOURCE:僅在源碼期間有效,在編譯期間被丟棄
RetentionPolicy.CLASS:保留到class文件中逞度,也就是說編譯期間都是可以對其進行處理的
RetentionPolicy.RUNTIME:保留到運行時额划,即可以用過反射調用
如上圖所示,定義了三個注解Test1档泽、Test2俊戳、Test3其中Test3是SOURCE級別的所以我們在反編譯出來的dex文件中看不到Test3揖赴,Test1和Test2則可以看到。如果我們繼續(xù)編寫代碼在運行期間反射的話那么Test2是可以被反射到的抑胎,Test1則找不到燥滑。(代碼不好貼,可以自行驗證)
Inherited表示注解是否可以被繼承圆恤,意思是如果A被注解@Test1注解突倍,B繼承A,如果@Test1被Inherited標記那么classB.getAnnotation可以獲取到Test1盆昙,如果@Test1沒有被Inherited標記那么那么classB.getAnnotation就無法獲取到Test1羽历。(代碼不好貼,可以自行驗證)
如上圖代碼所示淡喜,Handler3繼承Handler2秕磷,Handler2被Test2注解,Test2被Inherited注解炼团,然后運行程序打印log:
annotation: @annotation.test.jyb.com.annotation.Test2(value=101)澎嚣,可以發(fā)現(xiàn)通過Handler3我們可以取到Test2注解
Documented表示注解是否會保留到文檔中
3、AbstractProcessor
AbstractProcessor只能在java library工程里面用瘟芝,在Android library里面找不到這個類易桃,定義編譯期間注解最核心的是要用到AbstractProcessor用于處理我們的注解,它有四個方法可以讓我們重寫
?1锌俱、 public synchronized void init(ProcessingEnvironment processingEnv)
? 2晤郑、 public Set getSupportedAnnotationTypes()
? 3、 public SourceVersion getSupportedSourceVersion()
? 4贸宏、 public boolean process(Set annotations, RoundEnvironment roundEnv)
init方法主要是用于初始化和獲取相關工具類造寝,Init:初始化工作,獲取工具類:
processingEnv.getElementUtils(): element操作相關工具類吭练;
processingEnv.getTypeUtils():type操作相關工具類诫龙;
processingEnv.getMessager():打印日志;
processingEnv.getFiler()鲫咽,生成java文件签赃;
processingEnv.getOptions(), 獲取gradle中配置的參數
其中用的比較多的是打印日志和生成java文件
getSupportedAnnotationTypes:配置processor處理的注解類分尸。就是指定當前process處理哪幾個注解(我們可能會定義多個注解姊舵,用多個process進行處理,這個函數告訴當前process處理哪幾個注解)
getSupportedSourceVersion: 設置支持的java版本寓落,一般設置為SourceVersion.latestSupported()
process處理注解的方法,主要是獲取注解然后根據注解生成代碼
4荞下、定義編譯期注解
下面用一個例子來說民如何定義編譯期間注解伶选,
第一步定義注解:
如上圖史飞,我們定義了一個注解可以用在類、方法仰税、字段上面构资,CLASS級別(無法反編譯獲取)陨簇,可以被繼承
第二步編寫process方法
新建java library工程吐绵,編寫我們的process類繼承AbstractProcessor
這里我們的注解上也添加了一個@AutoService注解,這個AutoService作用是我們不用新建resources文件夾和配置文件河绽。使用方法就是在當前java library工程中添加Gradle依賴(implementation'com.google.auto.service:auto-service:1.0-rc4')然后在process類上添加注解就行
設置Procerssor能夠處理的注解
重寫process方法
上面process方法己单,首先通過roundEnv獲取被Test1標記的類或者方法或者字段,如果沒有找到那么退出
TypeSpec.Builder用于生成一個類(使用javapoet庫幫助我們生成代碼)
第三部添加一個單例方法(也是使用javapoet庫)
for循環(huán)耙饰,element就是被注解標記的元素纹笼,先判斷它是否是類如果是那么添加成員函數
最后createFile生成代碼。
addInstanceMethod苟跪,addClassMember都是通過javapoet來生成方法和字段的廷痘,createFile生成java文件。最后編譯我們可以在
app\build\intermediates\classes\debug\annotation\test\jyb\com中查看件已,標黑的這個是我們生成類的時候設置的包名笋额,所以它會隨著包名的變化而變化
4、定義運行期間注解
定義運行期間注解比較簡單篷扩,就是在定義注解的時候將Retention設置為RunTime然后通過反射調用就行兄猩,比如我們如果要用運行期間注解實現(xiàn)ButterKnife的話需要以下步驟:
第一步定義注解:
第二部編寫輔助代碼
第三步使用
然后在Activity的Oncreate方法中調用InjectBindHelper.inject(this)即可,因為反射效率比較低所以一般不會用這種方法來用findViewById瞻惋,這里只是演示;
完整demo:https://github.com/yaozhukuang/component,這個demo是通過注解實現(xiàn)項目組件化方案厦滤,所以代碼跟文中的可能有不一樣