1.概述
在現階段的Android開發中,註解越來越流行起來,比如ButterKnife,Retrofit,Dragger,EventBus等等都選擇使用註解來配置。按照處理時期,註解又分爲兩種類型,一種是運行時註解,另一種是編譯時註解,運行時註解由於性能問題被一些人所詬病。編譯時註解的核心依賴APT(Annotation Processing Tools)實現,原理是在某些代碼元素上(如類型、函數、字段等)添加註解,在編譯時編譯器會檢查AbstractProcessor的子類,並且調用該類型的process函數,然後將添加了註解的所有元素都傳遞到process函數中,使得開發人員可以在編譯器進行相應的處理,例如,根據註解生成新的Java類,這也就是EventBus,Retrofit,Dragger等開源庫的基本原理。
Java API已經提供了掃描源碼並解析註解的框架,你可以繼承AbstractProcessor類來提供實現自己的解析註解邏輯。下邊我們將學習如何在Android Studio中通過編譯時註解生成java文件。
2.創建名爲processor的module
首先使用Android Studio創建一個Android的project。然後開始創建一個名爲processor的java library。
點擊file->new->new module如圖
我們需要創建一個非Android的library,注意一定要選擇Java Library
3.兼容性配置
由於Android目前不是完全支持Java 8的語言特性,會導致編譯出錯。這裏將項目的源和目標兼容性值保留爲 Java 7。
打開app模塊下的build.gradle
在android標籤下添加 compile options
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
如圖
然後打開processor library的build.gradle
添加
sourceCompatibility = 1.7
targetCompatibility = 1.7
4.創建Annotation
在processor模塊下創建一個註解類
命名爲CustomAnnotation
具體內容如下
5.創建註解處理器
Processor繼承自AbstractProcessor類,@SupportedAnnotationTypes中填寫待處理的註解全稱,@SupportedSourceVersion表示處理的JAVA版本。
@SupportedAnnotationTypes(“<待處理註解類路徑>”)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
在註解類上右鍵選擇copy reference即可快速的獲得類的全稱
實現AbstractProcessor的方法
編譯時候將會執行process方法
向process方法寫入下述代碼
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
StringBuilder builder = new StringBuilder()
.append("package com.yuntao.annotationprocessor.generated;\n\n")
.append("public class GeneratedClass {\n\n") // open class
.append("\tpublic String getMessage() {\n") // open method
.append("\t\treturn \"");
// for each javax.lang.model.element.Element annotated with the CustomAnnotation
for (Element element : roundEnv.getElementsAnnotatedWith(CustomAnnotation.class)) {
String objectType = element.getSimpleName().toString();
// this is appending to the return statement
builder.append(objectType).append(" says hello!\\n");
}
builder.append("\";\n") // end return
.append("\t}\n") // close method
.append("}\n"); // close class
try { // write the file
JavaFileObject source = processingEnv.getFiler().createSourceFile("com.yuntao.annotationprocessor.generated.GeneratedClass");
Writer writer = source.openWriter();
writer.write(builder.toString());
writer.flush();
writer.close();
} catch (IOException e) {
// Note: calling e.printStackTrace() will print IO errors
// that occur from the file already existing after its first run, this is normal
}
return true;
}
此處調用process方法生成了一個Java類文件,該類包含一個getMessage
方法,該方法會返回一個字符串,其中字符串包含被@CustomAnnotation修飾的類的名稱。這裏主要在process方法中獲取註解修飾類的名稱。先看下生成的代碼如下。
注意:由於這個文件是在build過程中創建的,所以只有build成功之後纔可以查看到它。對應該類生成在以下目錄中:
app/build/generated/source/apt/debug/<package>/GeneratedClass.java
這裏便於演示我們只是簡單的生成了一個Java源文件,當然如果要生成更復雜的文件,可以利用第三方工具,例如javapoet
6.創建resource
創建好註解處理器後,我們需要告訴編譯器在編譯的時候使用哪個註解處理器,這裏就需要創建javax.annotation.processing.Processor文件
在processor模塊下,main目錄中創建一個resources文件夾,然後下邊在創建META-INF/services,最後裏邊一個javax.annotation.processing.Processor文件,如下:
在此文件中寫入註解處理器的類全稱
com.yuntao.annotationprocessor.processor.CustomAnnotationProcessor
7.添加android-apt
在project下的build.gradle中添加apt插件
添加依賴
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
然後在app中的build.gradle添加
apply plugin: 'com.neenbedankt.android-apt'
8.設置build的依賴
這一節主要講的是把processor模塊中的註解,註解處理器編譯生成一個jar,然後把這個jar包複製到app模塊下。然後讓app依賴引用這個jar。
首先配置app的build.gradle依賴項,添加jar依賴,看最後一行。
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.0.1'
testCompile 'junit:junit:4.12'
compile files('libs/processor.jar')
}
然後我們編寫一個gradle task,把生成的jar文件複製到app/libs目錄中。
task processorTask(type: Exec) {
commandLine 'cp', '../processor/build/libs/processor.jar', 'libs/'
}
最後我們創建task的依賴順序,app:preBuild依賴我們寫的processorTask, processorTask依賴 :processor:build:
意思就是processor build完成後把jar複製到app,然後在執行app:preBuild。
processorTask.dependsOn(':processor:build')
preBuild.dependsOn(processorTask)
最後配置完的build.gradle文件如下。
執行下編譯命令/gradlew :app:clean :app:build,看task執行順序
同時在app/libs目錄下可以查看到生成的jar
9.使用註解
我們在MainActivity的類與onCreate方法上使用@CustomAnnotation
現在加上註解之後還沒有生成我們需要的java文件,需要rebuild下才會生成,可以在Android Studio中選擇Build>Rebuild或者在終端執行
/gradlew :app:clean :app:build
然後我們可以在下述位置查看到生成的Java文件
app/build/generated/source/apt/debug/package/GeneratedClass.java
10.驗證能否使用
在MainActivity的onCreate方法彈窗,顯示下生成的GeneratedClass的getMessage方法返回的信息。代碼如下
private void showAnnotationMessage() {
GeneratedClass generatedClass = new GeneratedClass();
String message = generatedClass.getMessage();
// android.support.v7.app.AlertDialog
new AlertDialog.Builder(this)
.setPositiveButton("Ok", null)
.setTitle("Annotation Processor Messages")
.setMessage(message)
.show();
}
運行後結果
11.AnnotationProcessor中調試代碼
一般在寫代碼的時候調試是不可避免的,最後一節我們講下如何調試。
首先代碼中對process()方法設置代碼斷點。
設置gradle daemon端口和JVM參數。把下面兩行加入到你的gradle.properties文件。
org.gradle.daemon=true
org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
在命令行中運行gradle daemon來啓動守護線程。
gradle --daemon
在Android Studio建立Remote Debugger
Remote Debugger 配置,我們在這裏使用默認設置。IP:localhost,端口:5005。
然後點擊下圖按鈕運行,它就會連接到daemon線程中了。
最後我們用gradle命令來運行構建。
gradle clean assembleDebug
既然我們已經啓動了守護線程,Remote Debugger將觸發斷點並掛起構建運行。
最後總結下本編文章,主要講了AbstractProcessor的使用,在Android Studio構建過程創建Java文件,同時使用他的例子,最後補充了一下如何調試Processor的方法。