Android中使用AbstractProcessor在編譯時生成程式碼
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的方法。