必备知识 - 注解基本使用(Android)

154 阅读3分钟

必备知识 - 注解基本使用(Android)

注解基本知识

1.注解的作用

  • 注解就是一种标签,注释,单独使用作用不大
  • 一般使用场景
  • 注解 + APT(注释处理器),在编译器生成一写代码
  • 注解 + AMS 插装、埋点
  • 注解 + 反射,Hook代码注入

2.元注解

我们需要通过元注解来定义自定义注解,我们常用的元注解就四个@Target@Documented@Retention@Inherited

  • @Target:用来指定注解作用域
public enum ElementType {
    TYPE,  //类、接口、枚举等。
    FIELD, //字段。
    METHOD, //方法。
    PARAMETER, //方法参数。
    CONSTRUCTOR, //构造函数。
    LOCAL_VARIABLE, //局部变量。
    ANNOTATION_TYPE, //注解。
    PACKAGE,  //包。
    TYPE_PARAMETER, // 类型参数声明(常用于泛型的类型参数进行注解)
    TYPE_USE; // 类型使用声明(常用于泛型的类型参数进行注解)
}
  • @Documented:指定被标注的注解显示到javadoc中
  • @Retention:指定注解在什么时候保留
public enum RetentionPolicy {
    SOURCE, //注解仅在源代码中保留,编译后不会包含在生成的字节码中。
    CLASS,  //注解在编译时保留,会包含在生成的字节码中,但在运行时不可用。  
    RUNTIME; //注解在运行时保留,可以通过反射在运行时访问  

}
  • @Inherited:指定子类可以继承父类的注解,只能是类上的注解,方法和字段的注解不能被继承

3、声明注解

//指定注解在什么时候保留  
@Retention(RetentionPolicy.RUNTIME)  
//指定注解作用域  
@Target(ElementType.FIELD)  
public @interface MyAnnotate {  
//通过该函数来获取被注解锁修饰的内容  
String value() default "test";  
}  
  

4、使用注解 (注解+反射)

public class MyClass {
    //声明注解  
    @MyAnnotate
    String content = "my annotate test";
}

//声明对象  
MyClass myClass = new MyClass();

inject(myClass);

//反射获取注解和变量值  
private void inject(MyClass myClass) throws IllegalAccessException {
    Class<? extends MyClass> aClass = myClass.getClass();
    //获取所有field  
    Field[] fields = aClass.getDeclaredFields();
    for (Field field : fields) {
        //设置访问权限  
        if (field.isAccessible()) field.setAccessible(true);
        //判断是否包含MyAnnotate注解  
        if (field.isAnnotationPresent(MyAnnotate.class)) {
            MyAnnotate annotation = field.getAnnotation(MyAnnotate.class);
            //获取注解中的value中的值  
            Log.i(TAG, "inject - annotationValue: " + annotation.value());
            //获取field(变量值)  
            Log.i(TAG, "inject - content: " + field.get(myClass));
        }
    }
}
4.1、小结
  • 通过@Retention和@Target 来指定注解的保留策略和作用域以控制注解的使用方式和访问性

5、注解 + APT(注释处理器) 仿Butterknife

  • 将需要bind的view变量进行注解标注
  • 在编译期使用APT找到被注解中所指向的viewID和View引用
  • 通过这些信息生成findViewById注入类,内部帮助用户乘船findViewById的代码,生成View,并赋值给View变量,
  • 在Activity-onCreate中调用注解类.inject进行注入,注入成功之后将view设置给activity中的view变量

5.1、 用注解标注需要处理的变量

1、 用注解标注需要处理的变量
public class MainActivity extends AppCompatActivity {
    //将需要bind的view变量进行注解标注 
    @BindView(R.id.tvText)
    TextView textView;

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

5.2、 在编译期使用APT找到被注解中所指向的viewID和View引用


/**
 * 注解处理器,用来生成代码的
 * 使用前需要注册
 */
@AutoService(Processor.class)
public class AnnotationsCompiler extends AbstractProcessor {
    //1.支持的版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    //2.能用来处理哪些注解
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new HashSet<>();
        types.add(BindView.class.getCanonicalName());
        return types;
    }

    //3.定义一个用来生成APT目录下面的文件的对象
    Filer filer;
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnvironment.getFiler();
    }


    /**
     * 所有的坏事都在这个方法中实现
     *
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {


        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,"jett---------------"+set);
        //获取APP中所有用到了BindView注解的对象
        Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);

//        TypeElement//类
//        ExecutableElement//方法
//          VariableElement//属性
        //开始对elementsAnnotatedWith进行分类
        Map<String, List<VariableElement>> map = new HashMap<>();
        for (Element element : elementsAnnotatedWith) {
            VariableElement variableElement = (VariableElement) element;
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
            Class aClass = variableElement.getEnclosingElement().getClass();
            List<VariableElement> variableElements = map.get(activityName);
            if (variableElements == null) {
                variableElements = new ArrayList<>();
                map.put(activityName, variableElements);
            }
            variableElements.add(variableElement);
        }

        if (map.size() > 0) {
            Writer writer = null;
            Iterator<String> iterator = map.keySet().iterator();
            while (iterator.hasNext()) {
                String activityName = iterator.next();
                List<VariableElement> variableElements = map.get(activityName);
                //得到包名
                TypeElement enclosingElement = (TypeElement) variableElements.get(0).getEnclosingElement();
                String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();
                try {
                    JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + activityName + "_ViewBinding");
                    writer = sourceFile.openWriter();
                    //        package com.example.dn_butterknife;
                    writer.write("package " + packageName + ";\n");
                    //        import com.example.dn_butterknife.IBinder;
                    writer.write("import " + packageName + ".IBinder;\n");
                    //        public class MainActivity_ViewBinding implements IBinder<
                    //        com.example.dn_butterknife.MainActivity>{
                    writer.write("public class " + activityName + "_ViewBinding implements IBinder<" +
                            packageName + "." + activityName + ">{\n");
                    //            public void bind(com.example.dn_butterknife.MainActivity target) {
                    writer.write(" @Override\n" +
                            " public void bind(" + packageName + "." + activityName + " target){");
                    //target.tvText=(android.widget.TextView)target.findViewById(2131165325);
                    for (VariableElement variableElement : variableElements) {
                        //得到名字
                        String variableName = variableElement.getSimpleName().toString();
                        //得到ID
                        int id = variableElement.getAnnotation(BindView.class).value();
                        //得到类型
                        TypeMirror typeMirror = variableElement.asType();
                        writer.write("target." + variableName + "=(" + typeMirror + ")target.findViewById(" + id + ");\n");
                    }

                    writer.write("\n}}");

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (writer != null) {
                        try {
                            writer.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return false;
    }
}

5.3、 生成的代码会存放在build/javac/文件中


package com.example.butterknife_framework_demo;

import android.widget.TextView;

public class MainActivity_ViewBinding implements IBinder<MainActivity> {
    public MainActivity_ViewBinding() {
    }

    public void bind(MainActivity target) {
        target.textView = (TextView)target.findViewById(2131165359);
    }
}

5.4、将Activity传到注入类开始注入即可

public class JettButterknife {
    public static void bind(Activity activity){
        String name=activity.getClass().getName()+"_ViewBinding";
        try{
            Class<?> aClass=Class.forName(name);
            IBinder iBinder=(IBinder)aClass.newInstance();
            iBinder.bind(activity);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

初步理解,有问题请大家随时指出,谢谢