美文网首页
写一个自定义Android Java编译时注解失败了,但是学到了

写一个自定义Android Java编译时注解失败了,但是学到了

作者: 青岛大桥_Android到后端 | 来源:发表于2021-04-20 13:33 被阅读0次

    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;
        }
    
    
    }
    

    相关文章

      网友评论

          本文标题:写一个自定义Android Java编译时注解失败了,但是学到了

          本文链接:https://www.haomeiwen.com/subject/ofcnlltx.html