• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Annotation Processor

武飞扬头像
梦想不只是梦与想
帮助1

annotationProcessor和android-apt的功能是一样的,它们是替代关系。annotationProcessor是APT工具中的一种,他是谷歌开发的内置框架,不需要引入,可以直接在build.gradle文件中使用。android-apt是由一位开发者自己开发的apt框架,随着Android Gradle 插件 2.2 版本的发布,间接的进行了替代。

涉及注解相关知识:

@Retention的取值范围如下(代表注解的保留位置):

RetentionPolicy.SOURCE 表示修饰的注解只在源码中保留,编译后就被遗弃了,也就是class文件中就不存在了。
RetentionPolicy.CLASS 表示修饰的注解保留到编译后的class文件,运行时就被遗弃。比如在运行时通过反射去获取这个注解,会发现是不存在的。
RetentionPolicy.RUNTIME 表示注解一直保留到运行时。

@Target:的取值范围如下(代表注解的作用目标)

@Target(ElementType.TYPE)——接口、类、枚举、注解
@Target(ElementType.FIELD)——字段、枚举的常量
@Target(ElementType.METHOD)——方法
@Target(ElementType.PARAMETER)——方法参数
@Target(ElementType.CONSTRUCTOR) ——构造函数
@Target(ElementType.LOCAL_VARIABLE)——局部变量
@Target(ElementType.ANNOTATION_TYPE)——注解
@Target(ElementType.PACKAGE)——包

@Document:该注解可以被包含在javadoc中
@Inherited:子类可以继承父类中的该注解

三方库依赖方式:

implementation:该依赖方式所依赖的库不会传递,只会在当前module中生效。
api:该依赖方式会传递所依赖的库,当其他module依赖了该module时,可以使用该module下使用api依赖的库。

compile已过时,已被 implementation 和api 取代

当我们依赖一些第三方的库时,可能会遇到com.android.support冲突的问题,就是因为开发者使用的compile或api依赖的com.android.support包与我们本地所依赖的com.android.support包版本不一样。

一、定义注解

在工程中添加一个java library类型的module,取名annotation

Android Studio -> file -> new module -> java library

  1.  
    @Target(ElementType.FIELD)
  2.  
    @Retention(RetentionPolicy.SOURCE)
  3.  
    public @interface BindView {
  4.  
    @IdRes int value();
  5.  
    }

备注:注解@IdRes 引用系统库,如果是jiavaLibrary需要单独添加引用

  1.  
    dependencies {
  2.  
    implementation 'androidx.annotation:annotation:1.2.0'
  3.  
    }

二、定义注解处理器

新建一个module,取名为compiler,类型必须为java librar(因为有用到 javax.*下的类文件)

Android Studio -> file -> new module -> java library

新建一个类MyProcessor,这类必须继承至 AbstractProcessor

  1.  
    @AutoService(Processor.class)
  2.  
    public class MyProcessor extends AbstractProcessor {
  3.  
     
  4.  
     
  5.  
    }

备注:注解处理器创建完毕后,需要创建注册目录,如果是手动创建,目录如下:

学新通

 文件里面内容为自己处理器的全路径path
学新通

 建议使用谷歌的 AutoService,通过@AutoService注解标记一下,就可以自动完成注册动作,避免了手动创建错误,引用如下:

  1.  
    dependencies {
  2.  
    //谷歌的 AutoService
  3.  
    implementation 'com.谷歌.auto.service:auto-service:1.0-rc2'
  4.  
    annotationProcessor 'com.谷歌.auto.service:auto-service:1.0-rc2'
  5.  
    //自己的注解库
  6.  
    implementation project(':annotation')
  7.  
    }

生成路径:

学新通

Processor 常用到的4个方法:

  1.  
    /**
  2.  
    * 初始化常用的工具类
  3.  
    * @param processingEnv
  4.  
    */
  5.  
    @Override
  6.  
    public synchronized void init(ProcessingEnvironment processingEnv) {
  7.  
    super.init(processingEnv);
  8.  
    filer = processingEnv.getFiler();//文件操作相关
  9.  
    messager = processingEnv.getMessager();//日志相关
  10.  
    elementUtils = processingEnv.getElementUtils();//元素相关
  11.  
    }
  1.  
    /**
  2.  
    * 支持的注解类型
  3.  
    * 也可以通过系统注解
  4.  
    * @SupportedAnnotationTypes({"com.example.annotation.BindView"})
  5.  
    * @return
  6.  
    */
  7.  
    @Override
  8.  
    public Set<String> getSupportedAnnotationTypes() {
  9.  
    Set<String> types = new LinkedHashSet<>();
  10.  
    types.add(BindView.class.getCanonicalName());
  11.  
    return types;
  12.  
    }
  1.  
    /**
  2.  
    * 核心逻辑处理
  3.  
    * @param annotations
  4.  
    * @param roundEnv
  5.  
    * @return
  6.  
    */
  7.  
    @Override
  8.  
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
  9.  
    roundEnv) {
  10.  
    return true;
  11.  
    }
  1.  
    /**
  2.  
    * 支持的java版本
  3.  
    * 也可以通过系统注解代替
  4.  
    * @return
  5.  
    */
  6.  
    @Override
  7.  
    public SourceVersion getSupportedSourceVersion() {
  8.  
    return SourceVersion.RELEASE_7;
  9.  
    }

系统注解使用如下:

  1.  
    @SupportedSourceVersion(SourceVersion.RELEASE_7)
  2.  
    @SupportedAnnotationTypes({"com.example.annotation.BindView"})
  3.  
    @AutoService(Processor.class)
  4.  
    public class MyProcessor extends AbstractProcessor {}

如果需要调试传递参数:

  1.  
    defaultConfig {
  2.  
    applicationId "com.example.demo0328"
  3.  
    minSdkVersion 21
  4.  
    targetSdkVersion 25
  5.  
    versionCode 1
  6.  
    versionName "1.0"
  7.  
     
  8.  
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  9.  
    javaCompileOptions {
  10.  
    annotationProcessorOptions {
  11.  
    arguments = [xxxkey: 'xxxxvlaue']
  12.  
    }
  13.  
    }
  14.  
    }

注解处理器所在库获取参数:

Map<String, String> options = processingEnv.getOptions();

注解相关的api可以参考:注解与APT注解处理器技术详解 - 简书

三、新建android library:inject

  1.  
    public interface ViewBinder<T> {
  2.  
    void bind(T target);
  3.  
    }
  4.  
     
  5.  
     
  1.  
    public class InjectView {
  2.  
     
  3.  
    public static void bind(Activity activity) {
  4.  
    String className = activity.getClass().getName();
  5.  
    try {
  6.  
    // 得到我们生成的对应该Activity的ViewBinder类的Class对象
  7.  
    Class<?> viewBinderClass = Class.forName(className "$$ViewBinder");
  8.  
    ViewBinder viewBinder = (ViewBinder) viewBinderClass.newInstance();
  9.  
    // 绑定Activity的控件
  10.  
    viewBinder.bind(activity);
  11.  
    } catch (ClassNotFoundException e) {
  12.  
    e.printStackTrace();
  13.  
    } catch (Exception e) {
  14.  
    e.printStackTrace();
  15.  
    }
  16.  
    }
  17.  
    }
学新通

引用如下:

api project(':annotation')

接下来开始生成代码文件了。。。

代码生成

生成java文件工具有:Filer(注解处理器自带)、JavaPoet(省去手工拼接字符串麻烦)

手工生成api如下:

  1.  
    private void saveFile(String pkNameQ, String content) {
  2.  
    String pkName = pkNameQ;
  3.  
    try {
  4.  
    //创建java类文件
  5.  
    JavaFileObject jfo = filer.createSourceFile(pkName ".ViewBindId", new Element[]{});
  6.  
    Writer writer = jfo.openWriter();
  7.  
    writer.write(writeCode(pkName, content));
  8.  
    writer.flush();
  9.  
    writer.close();
  10.  
    } catch (IOException e) {
  11.  
    e.printStackTrace();
  12.  
    }
  13.  
     
  14.  
    }
  15.  
     
  16.  
     
  17.  
    private String writeCode(String pkName, String content) {
  18.  
    StringBuilder builder = new StringBuilder();
  19.  
    builder.append("package " pkName ";\n\n");
  20.  
    builder.append("import java.io.Closeable;\n");
  21.  
    builder.append("import java.io.IOException;\n");
  22.  
    builder.append("import android.util.Log;\n\n");
  23.  
     
  24.  
    builder.append("public class ViewBindId implements Closeable { \n\n");
  25.  
    //生成方法 1
  26.  
    builder.append("public static void setMsg(String[] args){ \n");
  27.  
     
  28.  
    builder.append("for (int i = 0; i < args.length; i ) { \n");
  29.  
    builder.append("System.out.println(\"内容\" args[i]);");
  30.  
    builder.append("Log.i(\"wangsen\",\"内容:\" args[i]);");
  31.  
    builder.append("}\n");
  32.  
    builder.append("System.out.println(\"" content "\");\n");
  33.  
     
  34.  
    builder.append("}\n");
  35.  
     
  36.  
    //生成方法 2
  37.  
    builder.append("@Override\n");
  38.  
    builder.append("public void close() throws IOException { \n");
  39.  
     
  40.  
    builder.append("System.out.println(\"" content "close\");\n");
  41.  
     
  42.  
    builder.append("}\n");
  43.  
     
  44.  
    builder.append("}");
  45.  
    return builder.toString();
  46.  
    }
学新通

拼接步骤麻烦,比如说换行符、导包操作等等。

javaPoet生成java文件,api如下:

 常用的api:

学新通

  • addStatement() 方法负责分号和换行
  • beginControlFlow() endControlFlow() 需要一起使用,提供换行符和缩进。
  • addCode() 以字符串的形式添加内
  • returns 添加返回值类型
  • .constructorBuilder() 生成构造器函数
  • .addAnnotation 添加注解
  • addSuperinterface 给类添加实现的接口
  • superclass 给类添加继承的父类
  • ClassName.bestGuess(“类全名称”) 返回ClassName对象,这里的类全名称表示的类必须要存在,会自动导入相应的包
  • ClassName.get(“包名”,”类名”) 返回ClassName对象,不检查该类是否存在
  • TypeSpec.interfaceBuilder(“HelloWorld”)生成一个HelloWorld接口
  • MethodSpec.constructorBuilder() 构造器
  • addTypeVariable(TypeVariableName.get(“T”, typeClassName)) 
    会给生成的类加上泛型

占位符

  • $L代表的是字面量
  • $S for Strings
  • $N for Names(我们自己生成的方法名或者变量名等等)
  • $T for Types

具体api使用,可以参考,感谢感谢,写的挺好的:

代码生成如下:

  1.  
    private void autoSaveFile(String pkName, String activityName, ClassName activityClass,
  2.  
    String bindViewFiledName, int resId) {
  3.  
    // 创建方法
  4.  
    MethodSpec main = MethodSpec.methodBuilder("bind")
  5.  
    .addModifiers(Modifier.PUBLIC)//
  6.  
    .returns(void.class)
  7.  
    .addAnnotation(Override.class)
  8.  
    .addParameter(activityClass, "target")
  9.  
    .addStatement("$T.out.println($S)", System.class, "自动创建的")
  10.  
    .addStatement("target.$L = target.findViewById($L)", bindViewFiledName, resId)
  11.  
    .build();
  12.  
     
  13.  
    FieldSpec fieldSpec = FieldSpec.builder(int.class,"age", Modifier.PUBLIC).build();
  14.  
     
  15.  
    ClassName viewBinderClass = ClassName.get("com.example.inject", "ViewBinder");
  16.  
    // 创建类
  17.  
    TypeSpec ViewBinder = TypeSpec.classBuilder(activityName "$$ViewBinder")//
  18.  
    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)//
  19.  
    .addMethod(main)//
  20.  
    .addField(String.class, "sex", Modifier.PRIVATE)//增加成员变量
  21.  
    .addField(fieldSpec)
  22.  
    .addSuperinterface(ParameterizedTypeName.get(viewBinderClass, activityClass))
  23.  
    .build();
  24.  
     
  25.  
    //String packageName = processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString();
  26.  
    try {
  27.  
    //生成java文件
  28.  
    JavaFile javaFile = JavaFile.builder(pkName, ViewBinder)//
  29.  
    .addFileComment(" This codes are generated automatically. Do not modify!")//
  30.  
    .build();
  31.  
    javaFile.writeTo(filer);
  32.  
    } catch (IOException e) {
  33.  
    e.printStackTrace();
  34.  
    }
  35.  
     
  36.  
    }
学新通

调用如下:

  1.  
    public class MainActivity extends AppCompatActivity {
  2.  
     
  3.  
    //关键
  4.  
    @BindView(R.id.tv01)
  5.  
    TextView tv01;
  6.  
     
  7.  
    @Override
  8.  
    protected void onCreate(Bundle savedInstanceState) {
  9.  
    super.onCreate(savedInstanceState);
  10.  
    setContentView(R.layout.activity_main);
  11.  
    //关键
  12.  
    InjectView.bind(this);
  13.  
    tv01.setText(Html.fromHtml("DD<font color='red'>注入初始化*</font>"));
  14.  
     
  15.  
     
  16.  
    ViewBindId.setMsg(new String[]{"a","b","c"});
  17.  
    ViewBinderAuto.setMsg(new String[]{"D","E","F"});
  18.  
    try {
  19.  
    new ViewBindId().close();
  20.  
    } catch (Exception e) {
  21.  
    e.printStackTrace();
  22.  
    }
  23.  
    }
  24.  
    }
学新通

切记build.gradle添加相关引用:

implementation project(':inject')
//implementation project(':annotation')
annotationProcessor project(path: ':compiler')

手写拼接可以参考:EventBus,Dragger等开源库的基本原理。自动生成参考butterknife

参考:

Android关于AutoService、Javapoet讲解 - 帅气的码农 - 博客园

注解与APT注解处理器技术详解 - 简书

秒懂Android注解处理器(Android Annotation Processor)-蒲公英云

通过编译期生成代码方式实现的仿ButterKnife功能Demo

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfgcig
系列文章
更多 icon
同类精品
更多 icon
继续加载