Android 寫annotation 的文章汗牛充棟。但是有些坑沒明白聘惦。這里記一下某饰。
需求
想定義一個UniTag, 自動生成 TAG的定義, 結(jié)果失敗了
@UniTag(name="MYMODULE")
public class MainActivity extends AppCompatActivity {
//想自動生成以下代碼
//private static final String TAG = "...";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
原因是,光靠Java annotation恐怕不行善绎,還得寫IDEA(Android Studio)插件黔漂,就跟開源lombok庫類似。
代碼結(jié)構(gòu)
請注意 定義要放在java module(一定不是Android module) mylibrary (網(wǎng)上有人起名myannotation的)
而自定義的繼承自AbstractProcessor是放在mycompile里的禀酱。
然后看mycomplie的build.gradle
//mycompile module 's build.gradle
apply plugin: 'java-library'
//apply plugin: 'kotlin'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//google autoservice
implementation 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
implementation project(path: ':mylibrary')
implementation 'com.squareup:javapoet:1.8.0'
implementation 'com.google.guava:guava:19.0'
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
}
再看mylibrary的build.gradle
//mylibrary module's build.gradle
apply plugin: 'java-library'
apply plugin: 'maven'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
再看 app的build.gradle
//app's build.grale
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "cn.zhuguangsheng.unitagutildemo"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
compileOnly project(':mylibrary')
annotationProcessor project(':mycompile')
}
最后看整個工程的build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
// ext {
// kotlin_version = '1.3.72'
// }
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.2"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
//替換成最新android-apt版本
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
//classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
接下來看源碼
定義注解
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface UniTag {
String name() default "";
}
定義Processor
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {
private Messager messager; // Log 日志
private Elements elementUtils; // 操作Element工具類
private Filer filer; // 支持通過注解處理器創(chuàng)建新文件
private Map<String, String> options; // 額外配置參數(shù)
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
messager = processingEnv.getMessager();
elementUtils = processingEnv.getElementUtils();
filer = processingEnv.getFiler();
options = processingEnv.getOptions();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
types.add(UniTag.class.getCanonicalName());
return types;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//Set<? extends Element> routerElements = roundEnv.getElementsAnnotatedWith(TrackName.class);
messager.printMessage(Diagnostic.Kind.NOTE, "正在process");
try {
Collection<? extends Element> annotatedElements =
roundEnv.getElementsAnnotatedWith(UniTag.class);
//for (TypeElement te : annotations) {
for (Element element : annotatedElements) {
if (element.getKind() == ElementKind.CLASS) {
// 顯示轉(zhuǎn)換元素類型
TypeElement typeElement = (TypeElement) element;
// 輸出元素名稱
messager.printMessage(Diagnostic.Kind.NOTE,typeElement.getSimpleName());
System.out.println(typeElement.getSimpleName());
// 輸出注解屬性值
messager.printMessage(Diagnostic.Kind.NOTE,typeElement.getAnnotation(UniTag.class).name());
System.out.println(typeElement.getAnnotation(UniTag.class).name());
//這塊沒完成炬守,只是隨便寫了個文件,難證效果剂跟,請看網(wǎng)上其它的例子
String fName = TypeUtil.packageNameOf(typeElement)+ "." + typeElement.getClass().getSimpleName();
messager.printMessage(Diagnostic.Kind.NOTE, "fName=" + fName);
JavaFileObject source = processingEnv.getFiler().createSourceFile(fName);
Writer writer = source.openWriter();
writer.write("http://hello hello");
writer.flush();
writer.close();
}
}
} catch (Exception e) {
e.printStackTrace();
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
}
return true;
}
}