注解
注解单独存在是没有意义的,需要和其他连用比如:
注解+APT 用于生成一些java文件,如: butterknife dagger2 hilt databinding
注解+动态代理
注解+代码埋点 如:AspectJ Arouter
注解+反射 如:xutils lifecycle
注解的作用:
1、降低项目的耦合度
2、自动完成一些规律性的代码
3、自动生成一些java代码,减轻开发者工作量
自定义注解的创建:
public @interface BindView {
int value();
}
注:BindView是可以随意起的类名,注解在创建的时候选择Annotation创建,关键标识是@interface。
注解里成员的定义:
1.当只有一个成员可以将其定义为value,使用时可以只写注解标识 @BindView
2.当有多个成员的时候需要在使用注解的时候@BindView(key = value,key2 = value2)形式。
元注解:可以修饰自定义注解
元注解常用的4中注解:@Target、@Retention、@Inherited、@Documented
@Target :表示注解类的作用对象
TYPE // 类、接口、枚举类
FIELD // 成员变量(包括:枚举常量)
METHOD // 成员方法
PARAMETER // 方法参数
CONSTRUCTOR // 构造方法
LOCAL_VARIABLE // 局部变量
ANNOTATION_TYPE // 注解类
PACKAGE // 可用于修饰包
TYPE_PARAMETER // 类型参数,JDK 1.8 新增
TYPE_USE //使用类型的任何地方,JDK 1.8新增
@Retention:表示注解的作用域
RetentionPolicy.RUNTIME // 注解保留到运行时
RetentionPolicy.CLASS // 注解保留到编译时
RetentionPolicy.SOURCE //注解保留到源码阶段(编译后.class文件不会有这个注解)
@Documented : javac的工具文档化,一般不关心
@Inherited:标明所修饰的注解,在所作用的类上,是否可以被继承。例:
//定一个Inherited注解的自定义注解
@Inherited
public @interface InheritAnnotation {
}
//使用这个自定义注解
@InheritAnnotation
public class Human {
}
//被注解修饰的类可以直接被其他类继承使用
class Man extends Human{
}
APT:(Annotation Processing Tool)注解处理工具,主要是生成一些java类
使用关键词:AbstractProcessor、@AutoService(Processor.class)
接下来用代码事例看是如何使用的:
1、首先在安卓工程中创建两个JAVA类型的module:
2、app的工程中依赖这两个java module:
implementation project(path:':apt-annotation')
annotationProcessor project(path:':apt-processor')
3、apt-processor依赖于apt-annotation,并在apt-processor的build.gradle中添加Google自动生成文件的第三方:
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
如图:
准备工作做好了!下面这个例子是仿ButterKnife
在apt-annotation中创建BindView注解类
/**
* Created by:zx
* on 9/7/21
* 自定义一个注解类
**/
@Target(ElementType.FIELD)//用在字段上
@Retention(RetentionPolicy.SOURCE)//源码范围
public @interface BindView {
int value();
}
在apt-processor中创建一个类AnnotationCompiler并继承AbstractProcessor,且使用注解@AutoService(Processor.class) 目的是注册注解处理器。
package com.zx.apt_processor;
import com.google.auto.service.AutoService;
import com.zx.apt_annotation.BindView;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
/**
* Created by:zx
* on 9/7/21
* 这个注解处理器APT 用于自动生成java文件
* 会在编译的时候运行
**/
@AutoService(Processor.class)//注册注解处理器 注意:Processor这个别写错了
public class AnnotationCompiler extends AbstractProcessor {
//支持的版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
//能够处理哪个注解
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindView.class.getCanonicalName());
// types.add(Override.class.getCanonicalName());
return types;
//这里要注意返回types
}
//定义一个用来生成APT目录下面的文件的对象
Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
//初始化filer
filer = processingEnvironment.getFiler();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "zx----------------"+set);
//获取app中所有写了BindView注解的对象的集合
//混合了所有的标注的类型:
// TypeElement 类
// ExecutableElement 方法
// VariableElement 属性
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);
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
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;
}
}
创建完成之后需要build一下工程,让它自动生成build里的文件。
在主工程app中创建IBinder一个接口
/**
* Created by:zx
* on 9/7/21
* 自定义一个接口用来绑定activity
**/
public interface IBinder {
void bind(T target);
}
自定义一个MyButterknife类
/**
* Created by:zx
* on 9/8/21
**/
public class MyButterknife {
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();
}
}
}
解释 :
Class aClass = Class.forName(name);
IBinder iBinder = (IBinder) aClass.newInstance();
这两句的使用相当于是:IBinder IBinder= new IBinder();
但是区别在于:
1.new出来的对象可以调用当前类里所有public方法,而newInstance()实例化出来的对象只能调用的是此类无参的构造方法;
2.好处在于newInstance()这种形式需要分成两部,第一步是forName(“权限定名”)加载链接上,之后才能使用newInstance()实例化,有解耦的作用
在Activity中使用:
public class AnnotationActivityextends AppCompatActivity {
//使用自己定义的注解
@BindView(R.id.test)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_annotation);
//传入Activity用于绑定
MyButterknife.bind(this);
textView.setText("赋值成功");
}
}
最后已经完成了注解+APT的结合,可以查看一下工程的app/build/generated/ap_generated_sources/debug/out/包名/自动生成的Activity的文件。
另外一个样例用于类似于枚举使用:
注:AS的版本是3.5.2,gradle版本是5.4.1。