注解与反射:Source级别

 
  1. 什么是注解?
    1. Annontation 是Java5开始引入的新特征,中文名称叫 注解 。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
    2. 注解本身没有任何意义,单独的注解就是一种注释,他需要结合其他如反射、插桩等技术才有意义。  Java 注解(Annotation)又称 Java 标注,是 JDK1.5 引入的一种注释机制。是元数据的一种形式,提供有关程序但不属于程序本身的数据。注解对他们注解的代码的操作没有直接影响
  2. 元注解
    1. @Target 所修饰的对象范围:
      1. CONSTRUCTOR:用于描述构造器
      2. FIELD:用于描述域即类成员变量
      3. METHOD:用于描述方法
      4. PARAMETER:用于描述参数
      5. TYPE:用于描述类、接口(包括注解类型) 或enum声明
    2. @Retention 所修饰对象的保留级别:
      1. SOURCE:注解仅保留在源码级别,并被编译器忽略
      2. CLASS:在编译器时由编译器保留,会被JVM忽略
      3. RUNTIME:由JVM保留,代码运行时会用到
 
用的最多是这俩元注解,其他的本文暂不做扩展,日后用到再补齐
 
Retention这块面试常会问道,试着写写
 
注解的应用场景
 

 

 
RetentionPolicy.SOURCE
 
运行在编译阶段
 
此模块主要用于语法检查和APT技术
 
 

先来个语法检查的栗子:

 
注解类
输入只能是 SUNNY,MONDAY
作用范围:类的成员变量、参数
保留级别:源码
需求:语法检查
 
@IntDef({SUNNY,MONDAY})
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
public @interface Wek {
}

实现类

public class MainActivity extends AppCompatActivity {

    public static final int SUNNY = 1;
    public static final int MONDAY = 2;

    @Wek
    private int current;

    @InjectView(R.id.text)
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //    正确的填入方式
        setCurrentDay(SUNNY);
    }

    private void setCurrentDay(@Wek int day){
        current = day;
    }

}

 

APT栗子再来一个:

apt全称 Android annotation tools ,在Javac执行前生成辅助代码文件,比如 ButterKnife框架,相比于 Android Annotations框架更轻量级,仅在编译期间多花点时间
 
项目实现
  • 创建 Java library Module,命名 apt-annotation
    • 这个module用于盛放注解类
  • 创建 Java library Module,命名为 apt-processor,依赖apt-annotation
    • 处理注解,在编译器生成辅助类
  • 创建 Android library Module,依赖 apt-annotation
    • 调用生成的辅助类,反射实现view绑定
  • app module 依赖上面三个
 
一步一步实现它
 
1.apt-annotation
 
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface BindView {
    //    获取填入的id
    int value();
}

2.apt-processor

 
加入依赖
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':apt-annotation')
}

ClassCreator.java辅助类

/**
*      辅助类
*/
public class ClassCreator {

    private String className;
    private String packageName;
    private TypeElement mTypeElement;
    private Map<Integer, VariableElement> mVariableElementMap;


    /**
     *      获取包名、类名
     * @param classElement
     * @param packageName
     */
    public ClassCreator(TypeElement classElement,String packageName) {
        mVariableElementMap = new HashMap<>();
        mTypeElement = classElement;
        this.packageName = packageName;
        className = classElement.getSimpleName().toString() + "_BindView";
    }


    /**
     *      id、view成对放入
     */
    public void setElement(int id,VariableElement element){
        mVariableElementMap.put(id, element);
    }


    /**
     *      生成 java 代码
     * @return
     */
    public String generateJavaCode(){
        StringBuilder builder = new StringBuilder();
        builder.append("package  " + packageName + ";\n\n");
        builder.append("import com.miss.apt_library.*;\n\n");
        builder.append("public class " + className);
        builder.append("{\n");
        generateMethods(builder);
        builder.append("\n}");
        return builder.toString();
    }

    /**
     *      把要 find id 写进去
     * @param builder
     */
    private void generateMethods(StringBuilder builder) {
        builder.append("public void bind(" + mTypeElement.getQualifiedName() + " host){ \n");
        for (int id : mVariableElementMap.keySet()) {
            VariableElement element = mVariableElementMap.get(id);
            String name = element.getSimpleName().toString();
            builder.append("host." + name + " = ");
            builder.append("((android.app.Activity)host).findViewById(" + id + ");\n");
        }
        builder.append("};");
    }

    public String getClassFullName() {
        return packageName + "." + className;
    }

    public TypeElement getTypeElement() {
        return mTypeElement;
    }
}

BindViewProcessor.java 获取注解中的id,生成注解类

/**
*  获取注解中的id,生成辅助类
* Created by Vola on 2020/8/2.
*/
@SupportedAnnotationTypes("com.miss.apt_annotation.BindView")
public class BindViewProcessor extends AbstractProcessor {


    private Messager messager;
    private Elements elementUtils;
    private Map<String, ClassCreator> mProxyMap = new HashMap<>();


    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        messager = processingEnv.getMessager();
        elementUtils = processingEnv.getElementUtils();
    }


    /**
     * 用来指定你使用的Java版本
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }


    /**
     * 这里必须指定,这个注解处理器是注册给哪个注解的。
     * 注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。
     * 换句话说,在这里定义你的注解处理器注册到哪些注解上。
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> supportTypes = new LinkedHashSet<>();
        supportTypes.add(BindView.class.getCanonicalName());
        return supportTypes;
    }


    /**
     * 这相当于每个处理器的主函数main()。
     * 在这里写扫描、评估和处理注解的代码,以及生成Java文件。
     * @param set
     * @param roundEnvironment 输入参数RoundEnviroment,可以让查询出包含特定注解的被注解元素。
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        messager.printMessage(Diagnostic.Kind.NOTE, "process---->");
        mProxyMap.clear();
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        for (Element element : elements) {
            //因为 BindView 的作用对象是 FIELD,因此 element 可以直接转化为 VariableElement
            VariableElement variableElement = (VariableElement) element;
            TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
            String classFullName = classElement.getQualifiedName().toString();


            ClassCreator classCreator = mProxyMap.get(classFullName);
            if (classCreator == null) {
                String packageName = elementUtils.getPackageOf(classElement).getQualifiedName().toString();
                classCreator = new ClassCreator(classElement, packageName);
                mProxyMap.put(classFullName, classCreator);
            }
            BindView bindView = variableElement.getAnnotation(BindView.class);
            int id = bindView.value();
            classCreator.setElement(id, variableElement);
        }
        //通过遍历mProxyMap,创建java文件
        for (String key : mProxyMap.keySet()) {
            ClassCreator classCreator = mProxyMap.get(key);
            try {
                messager.printMessage(Diagnostic.Kind.NOTE, " --> " + classCreator.generateJavaCode());


                JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(classCreator.getClassFullName(), classCreator.getTypeElement());
                Writer writer = fileObject.openWriter();
                writer.write(classCreator.generateJavaCode());
                //  清空缓冲区数据
                writer.flush();
                writer.close();
            } catch (IOException e) {
                messager.printMessage(Diagnostic.Kind.NOTE, " --> create " + classCreator.getClassFullName() + "error");
            }
        }


        return true;
    }
}

注册processor

src/ resources/META-INF/services/javax.annotation.processing.Processor(下划线部分是写死的)
 
com.miss.apt_processor.BindViewProcessor

3.apt-library

 
加入依赖
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation project(':apt-annotation')
}

BindViewTool.java  

public class BindViewTool {

    /**
     *  实现 view 绑定
     */
    public static void bind(Activity activity) {
        
        Class clazz = activity.getClass();
        try {
            Log.e("aa", "----" + clazz.getName());
            Class bindClass = Class.forName(clazz.getName() + "_BindView");
            Method method = bindClass.getMethod("bind", activity.getClass());
            method.invoke(bindClass.newInstance(), activity);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

4.app

 
依赖
dependencies {
    implementation project(':apt-library')
    implementation project(':apt-annotation')
    annotationProcessor project(':apt-processor')
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.text)
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        BindViewTool.bind(this);
        textView.setText("Success");
    }
}

 

 
 
 
 
 
 
 
 
 
 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值