ButterKnife 的简单实现:反射方式
InnerBinding辅助findViewByid
首先通过InnerBinding辅助实现Activity的findViewById,真机运行->生效
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_apt);
InnerBinding.bind(this);
haha();
}
复制代码
public class InnerBinding {
public static void bind(AptActivity activity){
activity.textView = activity.findViewById(R.id.textView);
}
}
复制代码
由于这样做的灵活性很差,所以可以通过反射改造InnerBinding,真机运行->生效
public class InnerBinding {
public static void bind(AptActivity activity){
//activity.textView = activity.findViewById(R.id.textView);
//通过反射获取到activity内的view
for (Field field : activity.getClass().getDeclaredFields()) {
BindView bindView = field.getAnnotation(BindView.class);
if (bindView!=null){
try {
View view = activity.findViewById(bindView.value());
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
复制代码
移植代码到module库lib_reflection
注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
复制代码
public class Binding {
public static void bind(Activity activity){
//通过反射获取到activity内的view
for (Field field : activity.getClass().getDeclaredFields()) {
BindView bindView = field.getAnnotation(BindView.class);
if (bindView!=null){
try {
//通过反射扩大访问权限->public
field.setAccessible(true);
View view = activity.findViewById(bindView.value());
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
复制代码
使用跟butterknife一样
public class AptActivity extends AppCompatActivity {
@BindView(R.id.textView) TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_apt);
Binding.bind(this);
}
}
复制代码
注解技术
分类:
- 普通注解
- @Override @Deprecated @SupressWarnings
- 元注解:注解其他注解的注解
- @Documented:被javaDoc工具记录
- @Target:使用范围
- @Rention:描述注解生命周期
- @Inherited:可继承,class子类
- 自定义注解
public @interface test()
- ButterKnife的BindView注解
- 作用域class,编译时生成,所以会影响编译速度,但是不影响运行时的速度
@Retention(CLASS) @Target(FIELD) public @interface BindView { /** View ID to which the field will be bound. */ @IdRes int value(); } 复制代码
注解处理器
- 注解处理器(Annotation Processor)是javac的一个工具, 编译时扫描和处理注解
- 每一个处理器都是继承于AbstractProcessor
- init方法:被注解处理工具调用
- process:处理器的主函数
- 注解处理器工作流程图
- ButterKnife注解APT工作流程:
- 声明的注解的生命周期为CLASS
- 继承于AbstractProcessor类
- 再调用AbstractProcessor的process方法
反射+运行时注解举例
- 1 判断任意一个对象所属的类
- 2 构造任意一个类的对象;
- 3 判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)
- 4 调用任意一个对象的方法
- 缺点:浪费性能,产生很多临时变量,造成频繁GC;同时反射出的代码无法被编译器优化;
APT技术对ButterKnife的简单实现
Annotation Processing
- 编译的时候扫描注解,并做相应的处理,生成java代码,生成 Java 代码是调用javapoet库生成的
- 调用 ButterKnife.bind(this);方法的时候,将ID与对应的上下文绑定在一起
依赖注入的概念
- 把依赖的决定权交给外部,即依赖注入.
- Dagger:外部的依赖图来决定依赖的值,对象自己只负责「索要」,而不负责 指定值,所以 Dagger 是依赖注入
- ButterKnife:自己决定依赖的的获取,只把执行过程交给 ButterKnife,所以只 是一个视图绑定库,而不是依赖注入
如dagger
@Inject Data data;
DaggerUtils.inject(this);
data.get();
data.set(newData);
复制代码
运行时自动创建辅助类AptActivityBinding的实现
通过InnerBinding在运行时反射创建AptActivityBinding辅助findViewById
public class InnerBinding {
private static final String TAG = "InnerBinding";
public static void bind(AptActivity activity){
String bindingClassName = activity.getClass().getCanonicalName()+"Binding";//com.dsh.txlessons.annotaionprocessing.AptActivityBinding
try {
Class bindingClass = Class.forName(bindingClassName);
Constructor constructor = bindingClass.getDeclaredConstructor(activity.getClass());
constructor.newInstance(activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
复制代码
生成类类似如下↓↓↓↓↓↓
public class AptActivityBinding {
public AptActivityBinding(AptActivity activity) {
activity.textView = activity.findViewById(R.id.textView);
}
}
复制代码
通过InnerBinding.bind(this)
调用,真机运行->生效
真正的Annotation Processing改造,编译期动态生成
AptActivityBinding是我们自己写好的,上面实测可行,所以下一步我们要让xxxActivityBinding这样的类能够自动生成
首先修改BindView注解
修改BindView,由于要在编译期生成辅助代码,所以修改Retention的值
@Retention(RetentionPolicy.SOURCE)//不保留只在编译的时候使用
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
复制代码
然后新建注解处理java库lib-processor
- Annotation Processing 用法:
- resources/META-INF/services/javax.annotation.processing.Processor
- 继承 AbstractProcessor
- 重写 getSupportedAnnotationTypes() 和 process()
- annotaions: 程序中出现的已注册的 Annotations;roundEnv:各个 java 文件
- 依赖:annotationProcessor
- 自动生成代码:
- 需要把 Annotation 单独拆成一个 java lib module,被主项目和 processor 分别依赖
- 还需要一个 lib module,依赖 annotation,把 bind 那些东⻄写在这里。 主项目依赖 lib,lib 依赖 annotations。最终主项目中有两个依赖:lib 和 processor
首先新建注解处理类BindingProcessor
package com.dsh.lib_processor;
...
public class BindingProcessor extends AbstractProcessor {
Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filer = processingEnvironment.getFiler();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//手动造一个已知类名等信息的
//String packageName = "com.dsh.txlessons.annotaionprocessing";
//ClassName className = ClassName.get(packageName,"AptActivityBinding");
//TypeSpec builtClass = TypeSpec.classBuilder(className)
// .addModifiers(Modifier.PUBLIC)
// .addMethod(MethodSpec.constructorBuilder()
// .addModifiers(Modifier.PUBLIC)
// .addParameter(ClassName.get(packageName,"AptActivity"),"activity")
// .addStatement("activity.textView = activity.findViewById(R.id.textView)")
// .build())
// .build();
//try {
// JavaFile.builder(packageName, builtClass)
// .build().writeTo(filer);
//} catch (IOException e) {
// e.printStackTrace();
//}
for (Element element : roundEnvironment.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 enclosedElement : element.getEnclosedElements()) {
if (enclosedElement.getKind() == ElementKind.FIELD) {
BindView bindView = enclosedElement.getAnnotation(BindView.class);
if (bindView != null) {
hasBinding = true;
constructorBuilder.addStatement("activity.$N = activity.findViewById($L)",
enclosedElement.getSimpleName(), bindView.value());
}
}
}
TypeSpec builtClass = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC)
.addMethod(constructorBuilder.build())
.build();
if (hasBinding) {
try {
JavaFile.builder(packageStr, builtClass)
.build().writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(BindView.class.getCanonicalName());
}
}
复制代码
然后固定写法新建文件javax.annotation.processing.Processor
目录结构:src/main/resources/META-INF/services/
文件内写入如下代码注册(猜测是注册使用)
com.dsh.lib_processor.BindingProcessor
复制代码
然后使用它
Binding.bind(this);
apt小结
在项目的app.build.gradle中依赖了注解所需的库
implementation project(':lib')
annotationProcessor project(':lib-processor')
复制代码
- 首先通过lib-processor中的BindingProcessor读取注解,并生成各个Activity对应的类,里面包含UI绑定的代码,这些是在编译期做的
public class AptActivityBinding { public AptActivityBinding(AptActivity activity) { activity.textView = activity.findViewById(2131231047); activity.layout = activity.findViewById(2131230906); } } 复制代码
- 在Activity中,lib中的Binding通过反射方式创建xxxActivityBinding对象
public class Binding { public static void bind(Activity activity){ String bindingClassName = activity.getClass().getCanonicalName()+"Binding";//com.dsh.txlessons.annotaionprocessing.AptActivityBinding try { Class bindingClass = Class.forName(bindingClassName); Constructor constructor = bindingClass.getDeclaredConstructor(activity.getClass()); constructor.newInstance(activity); } catch (xxxException e) { e.printStackTrace(); } ... } } 复制代码
- 这样Activity中的UI控件就能够绑定了
拓展
例:方法过时注解
如下,通过注解和注释,可以告诉开发者不要使用旧的方法
/**
* 方法过时,使用{@link #hehe()}来替代它
*/
@Deprecated
void haha(){
}
void hehe(){
}
复制代码