Android 写annotation 的文章汗牛充栋。但是有些坑没明白。这里记一下。
需求
想定义一个UniTag, 自动生成 TAG的定义, 结果失败了
@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库类似。
image.png代码结构
请注意 定义要放在java module(一定不是Android module) mylibrary (网上有人起名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; // 支持通过注解处理器创建新文件
private Map<String, String> options; // 额外配置参数
@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) {
// 显示转换元素类型
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());
//这块没完成,只是随便写了个文件,难证效果,请看网上其它的例子
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("//hello hello");
writer.flush();
writer.close();
}
}
} catch (Exception e) {
e.printStackTrace();
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
}
return true;
}
}
网友评论