1.ButterKnife是使用APT(AnnotationProcessor注解处理器,Javac的工具)进行注解,不是使用运行时注解,所以性能没有影响,但对编译器有点时间成本的影响
2.注解分为
普通注解 @Override等
元注解 注解其它注解的注解
1.@Document 应该被Java记录
2.@Target 注解的使用范围
3.@Retention 描述注解的生命周期
4.@inherited 表明注解可继承
3.自定义注解
@Documented
@Target(ElementType.TYPE)//ElementType.TYPE表示用来描述类或者接口(ElementType是枚举类)
@Retention(RetentionPolicy.RUNTIME)//RetentionPolicy.RUNTIME表明 运行时有效(RetentionPolicy是枚举类,也有英文注释)
@Inherited
public @interface metaTest { // @interface 表明自定义注解
public String doTest();
}
看下ElementType
public enum ElementType { //有英文注释
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
看下@BindView源码
@BindView(R.id.tv_info)
TextView tv_info;
@Retention(RUNTIME) //注释将由编译器记录在类文件中,并且在运行时由VM保留,因此可以反射地读取它们
@Target(FIELD) //字段声明(包括枚举常量)
public @interface BindView {
/** View ID to which the field will be bound. */
@IdRes int value();
}
注解处理器APT,每一个注解处理器都是继承于AbstractProcessor(注解处理器不能改变Java类)
看下AbstractProcessor这个类
package javax.annotation.processing;
public abstract class AbstractProcessor implements Processor {//四个核心方法(整个注解处理器是运行在自己的Java虚拟上的)
protected ProcessingEnvironment processingEnv;
private boolean initialized = false;
protected AbstractProcessor() { //AbstractProcessor 构造函数是空的,它的初始化在init()方法中
}
.
.
.
public synchronized void init(ProcessingEnvironment var1) {
if (this.initialized) {
throw new IllegalStateException("Cannot call init more than once.");
} else {
Objects.requireNonNull(var1, "Tool provided null ProcessingEnvironment");
this.processingEnv = var1;
this.initialized = true;
}
}
public abstract boolean process(Set<? extends TypeElement> var1, RoundEnvironment var2);//最重要的抽象方法,重要程度相当于Main函数,最后会生成Java代码
.
public Set<String> getSupportedAnnotationTypes() { //返回支持的注解的类型
SupportedAnnotationTypes var1 = (SupportedAnnotationTypes)this.getClass().getAnnotation(SupportedAnnotationTypes.class);
if (var1 == null) {
if (this.isInitialized()) {
this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedAnnotationTypes annotation found on " + this.getClass().getName() + ", returning an empty set.");
}
return Collections.emptySet();//通过集合来判断
} else {
return arrayToSet(var1.value());
}
}
public SourceVersion getSupportedSourceVersion() { //用来指定所支持的Java版本
SupportedSourceVersion var1 = (SupportedSourceVersion)this.getClass().getAnnotation(SupportedSourceVersion.class);
SourceVersion var2 = null;
if (var1 == null) {
var2 = SourceVersion.RELEASE_6;
if (this.isInitialized()) {
this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedSourceVersion annotation found on " + this.getClass().getName() + ", returning " + var2 + ".");
}
} else {
var2 = var1.value();
}
return var2;
}
}
看下ProcessingEnvironment这个接口
public interface ProcessingEnvironment {
Map<String, String> getOptions();
Messager getMessager();
Filer getFiler();//用于创建文件,Filer,Elements,Types 和butterKnife有关
Elements getElementUtils(); //处理源文件的工具类,扫描Java的所有源文件,可以将所有的源文件想象成Elements的全部
Types getTypeUtils();//获取源代码中的类型信息
SourceVersion getSourceVersion();
Locale getLocale();
}
反射(反射会生成大量临时对象,造成大量GC,GC会造成UI卡顿,对性能有影响)
1.判断任意一个对象所属的类‘
2.构造任意一个类的对象
3.判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)
4.调用任意一个对象的方法
举例(运行时注解加反射,相当于自己写的ButtferKnife,但是性能不好,ButtferKnife使用的编译时注解,对性能没有影响)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestInterface {
int value() default 100;
}
public class ReflectMain{
@TestInterface(10)
pubic int age;
public static void main(String[] args){
ReflectMain reflectMain = new ReflectMain();
TestInterface testInterface;
try{
Class class=reflectMain.getClass();
Field field=class.getField("age");
testInterface=field.getAnnotation(TestInterface.class);
System.out.print(testInterface.value());
}catch(NoSuchFieldException e){
System.out.print("no such field");
}
}
}
ButterKnife的工作原理
1.编译时候扫描注解,并做相应的处理,生成Java代码,生成Java代码是调用javapoet库生成的
2.调用butterKnife.bind(this)方法的时候,将ID与对应的上下文绑定在一起
看下ButterKnifeProcessor 这个类,AbstractProcessor 前面讲过的
public final class ButterKnifeProcessor extends AbstractProcessor {
.
.
.
@Override public synchronized void init(ProcessingEnvironment env) { //init(ProcessingEnvironment env)这个方法在初始的时候调用一次,ProcessingEnvironment参数前面讲过,会提供几个工具类
super.init(env);
String sdk = env.getOptions().get(OPTION_SDK_INT);
if (sdk != null) {
try {
this.sdk = Integer.parseInt(sdk);
} catch (NumberFormatException e) {
env.getMessager()
.printMessage(Kind.WARNING, "Unable to parse supplied minSdk option '"
+ sdk
+ "'. Falling back to API 1 support.");
}
}
debuggable = !"false".equals(env.getOptions().get(OPTION_DEBUGGABLE));
typeUtils = env.getTypeUtils();//用来处理TypeElement,TypeElement就是Element的类型
filer = env.getFiler();//用来处理辅助文件
.
.
.
}
@Override public Set<String> getSupportedAnnotationTypes() { //返回所支持的注解类型
Set<String> types = new LinkedHashSet<>();
for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName()); //在types集合里
}
return types;
}
private Set<Class<? extends Annotation>> getSupportedAnnotations() { //主要是指定Processor是注册给哪些注解的
Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
annotations.add(BindAnim.class);//这都是ButterKnife自定义好的注解
annotations.add(BindArray.class);
annotations.add(BindBitmap.class);
annotations.add(BindBool.class);
annotations.add(BindColor.class);
annotations.add(BindDimen.class);
annotations.add(BindDrawable.class);
annotations.add(BindFloat.class);
annotations.add(BindFont.class);
annotations.add(BindInt.class);
annotations.add(BindString.class);
annotations.add(BindView.class);
annotations.add(BindViews.class);
annotations.addAll(LISTENERS);//把所有的LISTENERS也添加进去
return annotations;
}
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {//是整个方法的入口
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);//就是拿到所有的注解信息之后,会存在一个Map集合当中
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {//遍历集合做相应的处理
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava(sdk, debuggable);//最后生成Java代码
try {
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
}
看下findAndParseTargets(env)方法
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {//针对每一个自定义好的注解,做处理
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
// we don't SuperficialValidation.validateElement(element)
// so that an unresolved View type can be generated by later processing rounds
try {
parseBindView(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindView.class, e);
}
}
}
看下 parseBindView(element, builderMap, erasedTargetNames)方法
```java
private void parseBindView(Element element, Map builderMap,
Set erasedTargetNames) {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();//源文件对象所对应的Element的类型
boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
|| isBindingInWrongPackage(BindView.class, element);//isInaccessibleViaGeneratedCode方法,判断是否在被注解的属性上,如果这个属性被private,static等修饰,就会出错范湖FALSE,同时包名是Android或者Java开头的,就会出错
TypeMirror elementType = element.asType();//TypeMirror就是TypeElement 的父类信息获取不到,通过TypeMirror来获取,element.asType()验证类型是否继承于View,也就是判断Element元素是否是view的子类
.
.
.
if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) {//isSubtypeOfType判断元素是否是View以及它的子类;isInterface判断是否是接口
if (elementType.getKind() == TypeKind.ERROR) {
note(element, "@%s field with unresolved type (%s) "
+ "must elsewhere be generated as a View or interface. (%s.%s)",
BindView.class.getSimpleName(), elementType, qualifiedName, simpleName);
} else {
error(element, "@%s fields must extend from View or be an interface. (%s.%s)",
BindView.class.getSimpleName(), qualifiedName, simpleName);
hasError = true;
}
}
if (hasError) {
return;
int id = element.getAnnotation(BindView.class).value();//获取需要绑定的View的ID
BindingSet.Builder builder = builderMap.get(enclosingElement);
Id resourceId = elementToId(element, BindView.class, id);
if (builder != null) {
String existingBindingName = builder.findExistingBindingName(resourceId);//判断有没有被绑定
if (existingBindingName != null) {
error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)",
BindView.class.getSimpleName(), id, existingBindingName,
enclosingElement.getQualifiedName(), element.getSimpleName());
return;
}
} else {
builder = getOrCreateBindingBuilder(builderMap, enclosingElement); //创建新的BindingSet.Builder实例
}
.
.
.
builder.addField(resourceId, new FieldViewBinding(name, type, required));//通过addField方法,添加到Map集合当中
// Add the type-erased version to the valid binding targets set.
erasedTargetNames.add(enclosingElement);
}
看下FieldViewBinding(name, type, required))类,跟踪源码到getOrCreateViewBindings(Id id)
private ViewBinding.Builder getOrCreateViewBindings(Id id) {
ViewBinding.Builder viewId = viewIdMap.get(id);
if (viewId == null) {
viewId = new ViewBinding.Builder(id);
viewIdMap.put(id, viewId);//添加到Map集合当中
}
return viewId;
}
看下binding.brewJava(sdk, debuggable)方法
JavaFile brewJava(int sdk, boolean debuggable) {
TypeSpec bindingConfiguration = createType(sdk, debuggable);
return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
看下createType(sdk, debuggable)方法
private TypeSpec createType(int sdk, boolean debuggable) {
TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
.addModifiers(PUBLIC)
.addOriginatingElement(enclosingElement);//om.squareup.javapoet.TypeSpec通过javapoet库来生成代码
if (isFinal) {//判断是否是Final类型的变量,如果是就添加Final修饰符
result.addModifiers(FINAL);
}
if (parentBinding != null) {
result.superclass(parentBinding.getBindingClassName());//添加父类的类名
} else {
result.addSuperinterface(UNBINDER);
}
if (hasTargetField()) {//Activity的成员变量有没有被绑定到,有的话添加private属性
result.addField(targetTypeName, "target", PRIVATE);
}
if (isView) {//判断是否是View或者View的子类,如果是的话,添加相应的构造方法给View
result.addMethod(createBindingConstructorForView());
} else if (isActivity) {//判断是否是Activity或者Activity的子类,如果是的话,添加相应的构造方法给Activity
result.addMethod(createBindingConstructorForActivity());
} else if (isDialog) {//判断是否是Dialog,如果是的话,添加相应的构造方法给Dialog
result.addMethod(createBindingConstructorForDialog());
}
if (!constructorNeedsView()) {//如果构造方法不需要View参数,一定要添加含有View参数的构造方法,这是必须的
// Add a delegating constructor with a target type + view signature for reflective use.
result.addMethod(createBindingViewDelegateConstructor());
}
result.addMethod(createBindingConstructor(sdk, debuggable));//可以把注解换算成findViewById代码,生成所需要的Java代码
if (hasViewBindings() || parentBinding == null) {
result.addMethod(createBindingUnbindMethod(result));
}
return result.build();
}
看下ddMethod(createBindingConstructor(sdk, debuggable))方法
private MethodSpec createBindingConstructor(int sdk, boolean debuggable) {
//判断是否有监听,有监听的话,就会将View设置成final,把ViewBinding遍历一遍,通过addViewBinding方法,生成findViewById这个方法
MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
.addAnnotation(UI_THREAD)
.addModifiers(PUBLIC);
if (hasMethodBindings()) {//判断有没有方法绑定
constructor.addParameter(targetTypeName, "target", FINAL);
} else {
constructor.addParameter(targetTypeName, "target");
}
if (constructorNeedsView()) {//判断是不是有注解的View
constructor.addParameter(VIEW, "source");
} else {
constructor.addParameter(CONTEXT, "context");
}
if (hasUnqualifiedResourceBindings()) {
// Aapt can change IDs out from underneath us, just suppress since all will work at runtime.
constructor.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType")
.build());
}
if (hasOnTouchMethodBindings()) {//判断如果需要绑定OnTouch方法
constructor.addAnnotation(AnnotationSpec.builder(SUPPRESS_LINT)
.addMember("value", "$S", "ClickableViewAccessibility")
.build());
}
if (parentBinding != null) {
if (parentBinding.constructorNeedsView()) {
constructor.addStatement("super(target, source)");
} else if (constructorNeedsView()) {
constructor.addStatement("super(target, source.getContext())");
} else {
constructor.addStatement("super(target, context)");
}
constructor.addCode("\n");
}
if (hasTargetField()) {//添加成员变量
constructor.addStatement("this.target = target");
constructor.addCode("\n");
}
if (hasViewBindings()) {
if (hasViewLocal()) {
// Local variable in which all views will be temporarily stored.
constructor.addStatement("$T view", VIEW);
}
for (ViewBinding binding : viewBindings) {
addViewBinding(constructor, binding, debuggable);//把每个View绑定的代码生成出来
}
for (FieldCollectionViewBinding binding : collectionBindings) {
constructor.addStatement("$L", binding.render(debuggable));
}
if (!resourceBindings.isEmpty()) {
constructor.addCode("\n");
}
}
if (!resourceBindings.isEmpty()) {
if (constructorNeedsView()) {
constructor.addStatement("$T context = source.getContext()", CONTEXT);
}
if (hasResourceBindingsNeedingResource(sdk)) {
constructor.addStatement("$T res = context.getResources()", RESOURCES);
}
for (ResourceBinding binding : resourceBindings) {
constructor.addStatement("$L", binding.render(sdk));
}
}
return constructor.build();
}
看下addViewBinding(constructor, binding, debuggable)方法
private void addViewBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
if (binding.isSingleFieldBinding()) {
// Optimize the common case where there's a single binding directly to a field.
FieldViewBinding fieldBinding = requireNonNull(binding.getFieldBinding());
CodeBlock.Builder builder = CodeBlock.builder()
.add("target.$L = ", fieldBinding.getName());
boolean requiresCast = requiresCast(fieldBinding.getType());
if (!debuggable || (!requiresCast && !fieldBinding.isRequired())) {
if (requiresCast) {
builder.add("($T) ", fieldBinding.getType());
}
builder.add("source.findViewById($L)", binding.getId().code);//将findViewById($L)添加到代码中
} else {
builder.add("$T.find", UTILS);
builder.add(fieldBinding.isRequired() ? "RequiredView" : "OptionalView");
if (requiresCast) {
builder.add("AsType");
}
builder.add("(source, $L", binding.getId().code);
if (fieldBinding.isRequired() || requiresCast) {
builder.add(", $S", asHumanDescription(singletonList(fieldBinding)));
}
if (requiresCast) {
builder.add(", $T.class", fieldBinding.getRawType());
}
builder.add(")");
}
result.addStatement("$L", builder.build());
return;
}