android apt 作用,Android Apt技术简析与ButterKnife

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:处理器的主函数

注解处理器工作流程图

d954be736573e967a53b29ba81914c28.png

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 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(){

}

复制代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值