Android编译时注解
APT(Annotation Processing Tool)注解处理器,编译过程中读源码,然后生成新的代码文件,再放在一起进行编译
用反射实现 butterknife
创建 @BindView 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
通过反射获取 Filed[],找到添加了 @BindView 的字段,调用 findViewById() 来给指定 View 绑定赋值
public class Binding {
/**
* 通过反射绑定View视图Id
* @param activity
*/
public static void bind(Activity activity) {
Field[] fields = activity.getClass().getDeclaredFields();
for (Field field : fields) {
BindView bindView = field.getAnnotation(BindView.class);
if (bindView != null) {
try {
field.setAccessible(true);// 私有字段强制反射
field.set(activity, activity.findViewById(bindView.value()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
依赖注入
把依赖的决定权交给外部,即依赖注入
Dagger:外部的依赖图决定依赖的值,对象自己只负责索要,而不负责指定值,所以Dagger是依赖注入
ButterKnife:自己决定依赖的获取,只是把执行过程交给了 ButterKnife,所以ButterKnife只是一个视图绑定库,而不是依赖注入
用 Annotation Processing 实现 butterknife
创建 lib-annotations 的 java-library 库,用于编写注解代码
创建 @BindView 注解
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
创建 lib-processor 的 java-library 库
添加依赖
dependencies {
implementation project(':lib-annotations')
implementation 'com.squareup:javapoet:1.12.1'// 用于生成java文件
}
创建注解处理器,继承AbstractProcessor
在编译期,对使用了 @BindView 的 XXActivity 对应生成一个 XXActivityBinding 的 java 文件
public class BindingProcessor extends AbstractProcessor {
Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
}
// 处理注解
// annotations:程序中出现已注册的Annotations
// roundEnv:各个java文件
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//System.out.println("process run!");
for (Element element: roundEnv.getRootElements()) {
String packageStr = element.getEnclosingElement().toString();// 包名
String classStr = element.getSimpleName().toString();// 类名
ClassName className = ClassName.get(packageStr, classStr + "Binding");
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ClassName.get(packageStr, classStr), "activity");
boolean hasBinding = false;
for (Element element2 : element.getEnclosedElements()) {
if (element2.getKind() == ElementKind.FIELD) {// 是加了注解的字段
BindView bindView = element2.getAnnotation(BindView.class);// 获取注解
if (bindView != null) {
hasBinding = true;
constructorBuilder.addStatement("activity.$N = activity.findViewById($L)"
, element2.getSimpleName(), bindView.value());
}
}
}
TypeSpec classBuilder = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC)
.addMethod(constructorBuilder.build())
.build();
if (hasBinding) {
try {
JavaFile.builder(packageStr, classBuilder)
.build()
.writeTo(filer);// 构造出文件并写到指定位置
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {// 支持的注解,必须重写
return Collections.singleton(BindView.class.getCanonicalName());
}
}
配置注解处理器
在main下新建 resources/META-INF/services/javax.annotation.processing.Processor 文件
文件内容为 BindingProcessor 的包名 + “.” + 类名,比如:com.chen.lib_processor.BindingProcessor
创建 lib-bindview 的 android-library 库
添加依赖
api project(':lib-annotations')
创建Binding,通过反射调用编译期生成的 XXActivityBinding
public class Binding {
/**
* 通过反射调用 XXActivity 对应的 XXActivityBinding 构造方法
*
* @param activity
*/
public static void bind(Activity activity) {
try {
Class<?> bindingClass = Class.forName(activity.getClass().getCanonicalName() + "Binding");
Constructor<?> constructor = bindingClass.getDeclaredConstructor(activity.getClass());
constructor.newInstance(activity);
} catch (ClassNotFoundException
| NoSuchMethodException
| InvocationTargetException
| IllegalAccessException
| InstantiationException e) {
e.printStackTrace();
}
}
}
在 app 模块中使用
添加依赖
implementation project(':lib-bindview')
annotationProcessor project(':lib-processor')
在Activity中使用
public class MainActivity extends AppCompatActivity {
@BindView(R.id.layout)
ConstraintLayout layout;
@BindView(R.id.textView)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Binding.bind(this);
layout.setBackgroundColor(Color.CYAN);
textView.setText("Hello BindView");
}
}