必备知识 - 注解基本使用(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();
}
}
}