Android自定義註解

按照Tatget來分的話,註解有10種類型。Target是聲明該註解使用的地方,好比使用在屬性上是field,使用在方法上是method,這裏就不一一說明了,感興趣的同窗能夠深刻研究一下其餘幾種。按照Retention來分的話,註解有3種類型,分別是SOURCE(源碼)、CLASS(編譯)、RUNTIME(運行)。本文主要按照Retention,也就是生命週期方向來區別講解註解。源碼註解在編譯時使用,好比@overide,生命週期短只在編譯時有做用,通常用在IDE代碼檢測;編譯註解缺點是隻在編譯時有做用,優勢是效率比運行時註解高,可是使用較複雜一些;運行時註解優勢是在編譯時和運行時都能工做,可是運行時經過反射來獲取註解的值,效率較低。下面的兩個圖分別是系統定義的這2個方向類別枚舉:




1.源碼期註解(RetentionPolicy.SOURCE)java

源碼註解通常在編譯器IDE中使用,好比@overide。源碼註解只在編譯前起做用,通常用來約束代碼規範,在實際的業務開發中並不常見。數組

2.編譯期註解(RetentionPolicy.CLASS)bash

編譯期註解通常結合autoService+javapoet一塊兒使用,用來在編譯期生成簡單重複的文件,節約手寫的時間。下面咱們一塊兒來生成一個編譯期註解,注意這裏必定要新建一個java library來操做,由於核心的註解處理器父類AbstractProcessor類只在java library中有,在Android中是找不到的。假設咱們須要生成的效果以下:app


第一步 定義註解類ide

第二步 寫好註解處理器測試

註解處理器經過繼承AbstractProcessor的方式實現,在編譯的時候系統會尋找到全部AbstractProcessor子類執行其中的process方法,因此咱們能夠在process中進行生成java文件的操做,生成之後系統會將生成的java文件打包成class。gradle

1.其中須要注意的是,這裏須要導入autoservice類庫,而後註解處理器類須要經過@AutoService(Processor.class)進行標註。這是固定寫法,意思是將該類的路徑告訴註解處理器,否則系統不知道處理器路徑是無法生成文件的。在編譯後,這個路徑就會被保存在javalibrary的build/META-INF下面,其中內容就是咱們自定義註解處理器的完整路徑。固然若是不用@AutoService標註,直接在META-INF目錄下新建文件也是能夠的,只是更麻煩,使用@AutoService方式進行標註的使用方式以下圖:ui

implementation 'com.google.auto.service:auto-service:1.0-rc2'複製代碼

2.標註好了通常須要複寫其中的4個方法,分別是init、getSupportedAnnotationTypes、getSupportedSourceVersion、process。其中process是最重要的,也是最複雜的。咱們先複寫前面3個,process咱們單獨拿出來寫。前面3個方法複寫以下:google

其中init主要是拿到註解處理類。其中最重要的是拿到filer,這個在寫文件的時候要用到,因此必需要複寫init拿到這個filer。elementUtils主要是用來獲取到使用了註解的類基本信息。getSupportedAnnotationTypes是用來告訴系統編譯時支持哪些註解,在這裏咱們把咱們自定義的註解丟進去。getSupportedSourceVersion通常就寫SourceVersion.latestSupported就行了。再提一句,這個getSupportedAnnotationTypes和getSupportedSourceVersion方法也能夠不復寫,可是要在MyAbstractor類上用註解的方式標明。spa

3.接下來咱們來寫最重要的process方法,這個方法是整個apt最核心的部分。這裏能夠徹底手寫可是容易錯,速度也慢,因此通常導入javapoet類庫,藉助javapoet來寫。咱們process方法實現以下圖,在process方法中首先遍歷全部註解,找到咱們自定義的MyAnotation註解。而後拿到MyAnotation對象,獲取到使用處給予的註解的值。接下來經過javapoet中的TypeSpec來拼接出須要生成的java類。最終調用最核心的方法JavaFile.builder(生成的路徑,拼接好的格式).build().writeTo(註解處理器的處理對象)。這樣就寫好了註解處理器的內容,接下來就坐等使用和編譯生成就好啦!這裏須要注意的是,因爲註解處理器是在java library中,因此不能經過Log類來打點,這裏能夠經過java的輸出方式System.out.print()的方式打印出數據來進行調試,也能夠經過從init方法中拿到Message對象來打日誌,而後在gradle console窗口進行查看日誌信息

compile 'com.squareup:javapoet:1.7.0'複製代碼

提問:核心的process方法中兩個參數分別表明什麼?

第一個參數是set<TypeElement>,其中TypeElement表明類元素,也就是全部使用了指定註解的全部類,這個指定註解是指上面getSupportedAnotationTypes指定的註解,好比說本例子中在MainActivity中使用了MyAnotation,因爲MyAnotation在指定的註解中,因此MainActivity這個類就會出如今該集合中。

第二個參數roundEnviroment是保存了使用了指定註解的全部註解項信息,不論是使用在類上仍是屬性上仍是方法上,都會放到該參數中。在本例子中咱們獲取到全部使用了MyAnotation註解的註解項,而且取出了註解的值。

第三步 使用註解

因爲註解是在javalibrary中定義的,咱們首先要將該library導入到app模塊中。須要注意的是類庫聲明和註解聲明都須要作,否則是無法跑起來的。而後咱們給類中的隨意一個屬性上加上咱們的註解,固然方法和類也是能夠的,這裏就不一一演示了。註解使用之後咱們接下來在onCreate方法裏面調用這個經過註解處理器生成的MySimpleAptName類中的test靜態方法來測試一下是否能夠調用到,須要注意的是咱們項目里根本沒有去手寫這個類,因此直接這樣寫報錯也很正常,必定要rebuild之後等該類生成了那麼就不會報錯了。

implementation project(':myapt')
annotationProcessor project(':myapt')複製代碼


第四步 自動生成文件

在運行項目或者rebuild的時候,會在使用時指定好的目錄生成class文件,注意這裏clean的話是不能生成的。最後生成的效果以下,是否是和咱們本身寫出來的類很像呢?這個時候回過頭去看MainActivity裏已經沒有報錯了,至此編譯時註解demo圓滿完成。


3.運行期註解(RetentionPolicy.RUNTIME)

運行期註解的生命週期最長,能夠存活到程序運行時,也是使用最普遍的註解類型。通常用來代替項目中重複簡單的代碼,設置一個註解可比寫不少無用代碼要簡潔得多。編譯器註解能夠經過反射來獲取到註解類,拿到咱們使用註解時設置的內容。不過因爲使用反射,因此對效率會有一些影響,這也是不少開源項目(BufferKnief、Retrofit等)選擇編譯期註解的緣由。不過寫起來很簡單,只須要三步就OK啦!不像編譯期註解要結合其餘功能類,接下來咱們一塊兒來手寫一個運行期註解。

第一步 定義註解類


第二步 使用註解類

第三步 解析註解類

咱們須要在運行的時候對註解進行解析。如上圖,咱們在頁面的oncreate方法執行時首先獲取到該類全部的註解,獲取的值是一個Field[]數組。這裏面每個Field都表明MainActivity類中的一個註解使用項,接下來咱們遍歷全部的註解項,對本身關心的項進行具體的處理。demo中咱們對TextView進行了找控件的操做,這樣就統一處理了從而避免了項目中出現不少findViewById的操做,讓咱們的代碼變得更簡潔了。

最後:本人小萌新,若是本文有寫錯的或者描述不全的地方還望各位大佬多多指點,一塊兒探討,共同進步!

相關文章
相關標籤/搜索