IOC架构设计之ButterKnife源码&原理(二)上篇,跳槽字节跳动

View view;
target.title = Utils.findRequiredViewAsType(source, R.id.title, "field 'title'", TextView.class);
target.subtitle = Utils.findRequiredViewAsType(source, R.id.subtitle, "field 'subtitle'", TextView.class);
view = Utils.findRequiredView(source, R.id.hello, "field 'hello', method 'sayHello', and method 'sayGetOffMe'");
target.hello = Utils.castView(view, R.id.hello, "field 'hello'", Button.class);
view2130968578 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
  @Override
  public void doClick(View p0) {
    target.sayHello();
  }
});
view.setOnLongClickListener(new View.OnLongClickListener() {
  @Override
  public boolean onLongClick(View p0) {
    return target.sayGetOffMe();
  }
});
view = Utils.findRequiredView(source, R.id.list_of_things, "field 'listOfThings' and method 'onItemClick'");
target.listOfThings = Utils.castView(view, R.id.list_of_things, "field 'listOfThings'", ListView.class);
view2130968579 = view;
((AdapterView<?>) view).setOnItemClickListener(new AdapterView.OnItemClickListener() {
  @Override
  public void onItemClick(AdapterView<?> p0, View p1, int p2, long p3) {
    target.onItemClick(p2);
  }
});
target.footer = Utils.findRequiredViewAsType(source, R.id.footer, "field 'footer'", TextView.class);
target.headerViews = Utils.listOf(
    Utils.findRequiredView(source, R.id.title, "field 'headerViews'"), 
    Utils.findRequiredView(source, R.id.subtitle, "field 'headerViews'"), 
    Utils.findRequiredView(source, R.id.hello, "field 'headerViews'"));

}

@Override
@CallSuper
public void unbind() {
T target = this.target;
if (target == null) throw new IllegalStateException(“Bindings already cleared.”);

target.title = null;
target.subtitle = null;
target.hello = null;
target.listOfThings = null;
target.footer = null;
target.headerViews = null;

view2130968578.setOnClickListener(null);
view2130968578.setOnLongClickListener(null);
view2130968578 = null;
((AdapterView<?>) view2130968579).setOnItemClickListener(null);
view2130968579 = null;

this.target = null;

}
}

我们看到了我们熟悉的代码,虽然比较乱(因为是生成的), 
可以看出 在构造中findview 在unbind中进行置null处理,让告诉gc在合适的机会回收占用的内存 ;但是这是后面真正生成代码我们看不到的,没关系 嘻嘻。

## 3.1 整体的原理-(编译时期-注解处理器)
在java代码的编译时期,javac 会调用java注解处理器来进行处理。因此我们可以定义自己的注解处理器来干一些事情。一个特定注解的处理器以 java 源代码(或者已编译的字节码)作为输入,然后生成一些文件(通常是.java文件)作为输出。因此我们可以在用户已有的代码上添加一些方法,来帮我们做一些有用的事情。这些生成的 java 文件跟其他手动编写的 java 源代码一样,将会被 javac 编译。(个人参考及个人理解)

## 3.2 定义处理器 继承AbstractProcessor
在java中定义自己的处理器都是继承自AbstractProcessor 
前3个方法都试固定写法,主要是process方法。

public class MyProcessor extends AbstractProcessor {

//用来指定你使用的 java 版本。通常你应该返回                                SourceVersion.latestSupported()

@Override
public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
}

//会被处理器调用,可以在这里获取Filer,Elements,Messager等辅助类,后面会解释
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
}

//这个方法返回stirng类型的set集合,集合里包含了你需要处理的注解
@Override
public Set getSupportedAnnotationTypes() {
Set annotataions = new LinkedHashSet();
annotataions.add(“com.example.MyAnnotation”);
return annotataions;
}

//核心方法,这个一般的流程就是先扫描查找注解,再生成 java 文件
//这2个步骤设计的知识点细节很多。

@Override
public boolean process(Set<? extends TypeElement> annoations,
        RoundEnvironment env) {
    return false;
}

}

## 3.3 注册你的处理器
要像jvm调用你写的处理器,你必须先注册,让他知道。怎么让它知道呢,其实很简单,google 为我们提供了一个库,简单的一个注解就可以。 
首先是依赖

compile ‘com.google.auto.service:auto-service:1.0-rc2’

@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
//…省略非关键代码

## 3.4 基本概念
Elements:一个用来处理Element的工具类
Types:一个用来处理TypeMirror的工具类
Filer:你可以使用这个类来创建.java文件
# 四、源码分析
分析之前呢先要有写基本的概念

可以看到AutoService注解

@AutoService(Processor.class)
public final class ButterKnifeProcessor extends AbstractProcessor {
//…

**(1)init 方法,这个主要是获取一些辅助类**

private Filer mFiler; //文件相关的辅助类
private Elements mElementUtils; //元素相关的辅助类
private Messager mMessager; //日志相关的辅助类

@Override public synchronized void init(ProcessingEnvironment env) {
super.init(env);

elementUtils = env.getElementUtils();
typeUtils = env.getTypeUtils();
filer = env.getFiler();
try {
  trees = Trees.instance(processingEnv);
} catch (IllegalArgumentException ignored) {
}

}

**(2)getSupportedSourceVersion()方法就是默认的,获取你该处理器使用的java版本**

@Override public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}

**(3)接下来就是process方法,这个方法是核心方法。**

@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//1.查找所有的注解信息,并形成BindingClass(是什么 后面会讲) 保存到 map中
Map<TypeElement, BindingClass> targetClassMap = findAndParseTargets(env);

//2.遍历步骤1的map 的生成.java文件也就是上文的 类名_ViewBinding 的java文件

for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingClass bindingClass = entry.getValue();

  JavaFile javaFile = bindingClass.brewJava();
  try {
    javaFile.writeTo(filer);
  } catch (IOException e) {
    error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
  }
}

return true;

}

显然分两步执行,下面再仔细走一下该方法流程。

**每个注解的查找与解析-findAndParseTargets**

private Map<TypeElement, BindingClass> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingClass> targetClassMap = new LinkedHashMap<>();
Set erasedTargetNames = new LinkedHashSet<>();

scanForRClasses(env);
//...下面是每个注解的解析

// Process each @BindArray element.
for (Element element : env.getElementsAnnotatedWith(BindArray.class)) {
if (!SuperficialValidation.validateElement(element)) continue;
try {
parseResourceArray(element, targetClassMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindArray.class, e);
}
}

 // Process each @BindView element.
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, targetClassMap, erasedTargetNames);
  } catch (Exception e) {
    logParsingError(element, BindView.class, e);
  }
}

//…

  // Process each annotation that corresponds to a listener.
for (Class<? extends Annotation> listener : LISTENERS) {
  findAndParseListener(env, listener, targetClassMap, erasedTargetNames);
}

// Try to find a parent binder for each.
for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {
  TypeElement parentType = findParentType(entry.getKey(), erasedTargetNames);
  if (parentType != null) {
    BindingClass bindingClass = entry.getValue();
    BindingClass parentBindingClass = targetClassMap.get(parentType);
    bindingClass.setParent(parentBindingClass);
  }
}

return targetClassMap;

}

**第一步**
我们先看一下参数 RoundEnvironment 这个是什么呢?个人理解是注解框架里的一个工具什么工具呢?一个可以在处理器处理该处理器 用来查询注解信息的工具,当然包含你在getSupportedAnnotationTypes注册的注解。 
**第二步**
创建了一个LinkedHashMap保证了先后的顺序就是先注解的先生成java文件,其实也没有什么先后无所谓。最后将其返回。 
我们这里只分析一个具有代表性的BindView注解,其它的都是一样的,连代码都一毛一样。 
在这之前先看一下Element这个类我们看一下官方注释
  • Represents a program element such as a package, class, or method.
  • Each element represents a static, language-level construct
  • (and not, for example, a runtime construct of the virtual machine).
在注解处理器中,我们扫描 java 源文件,源代码中的每一部分都是Element的一个特定类型。换句话说:Element代表程序中的元素,比如说 包,类,方法。每一个元素代表一个静态的,语言级别的结构. 
比如:

public class ClassA { // TypeElement
private int var_0; // VariableElement
public ClassA() {} // ExecuteableElement

public void setA( // ExecuteableElement
        int newA // TypeElement
) {
}

}

可以看到类为TypeElement,变量为VariableElement,方法为ExecuteableElement 
这些都是Element的子类,核对源码。

TypeElement aClass ;
for (Element e : aClass.getEnclosedElements()){ //获取所有的子节点
Element parent = e.getEnclosingElement(); // 获取父节点
}

Elements代表源代码,TypeElement代表源代码中的元素类型,例如类。然后,TypeElement并不包含类的相关信息。你可以从TypeElement获取类的名称,但你不能获取类的信息,比如说父类。这些信息可以通过TypeMirror获取。你可以通过调用element.asType()来获取一个Element的TypeMirror。

这个是对于理解源码的基础。 
继续,我们看到了for循环查找所有包含BindView的注解。

parseBindView(element, targetClassMap, erasedTargetNames);

把element和targetClassMap传入

private void parseBindView(Element element, Map<TypeElement, BindingClass> targetClassMap,
Set erasedTargetNames) {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

 //1.检查用户使用的合法性
// Start by verifying common generated code restrictions.
boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
    || isBindingInWrongPackage(BindView.class, element);

// Verify that the target type extends from View.
TypeMirror elementType = element.asType();
if (elementType.getKind() == TypeKind.TYPEVAR) {
  TypeVariable typeVariable = (TypeVariable) elementType;
  elementType = typeVariable.getUpperBound();
}
if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) {
  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, enclosingElement.getQualifiedName(),
        element.getSimpleName());
  } else {
    error(element, "@%s fields must extend from View or be an interface. (%s.%s)",
        BindView.class.getSimpleName(), enclosingElement.getQualifiedName(),
        element.getSimpleName());
    hasError = true;
  }
}

// 不合法的直接返回
if (hasError) {
  return;
}


//2.获取id 值

// Assemble information on the field.
int id = element.getAnnotation(BindView.class).value();

// 3.获取 BindingClass,有缓存机制, 没有则创建,下文会仔细分析

BindingClass bindingClass = targetClassMap.get(enclosingElement);
if (bindingClass != null) {
  ViewBindings viewBindings = bindingClass.getViewBinding(getId(id));
  if (viewBindings != null && viewBindings.getFieldBinding() != null) {
    FieldViewBinding existingBinding = viewBindings.getFieldBinding();
    error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)",
        BindView.class.getSimpleName(), id, existingBinding.getName(),
        enclosingElement.getQualifiedName(), element.getSimpleName());
    return;
  }
} else {
  bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement);
}

//4 生成FieldViewBinding 实体

String name = element.getSimpleName().toString();
TypeName type = TypeName.get(elementType);
boolean required = isFieldRequired(element);

FieldViewBinding binding = new FieldViewBinding(name, type, required);
 //5。加入到 bindingClass 成员变量的集合中
bindingClass.addField(getId(id), binding);

// Add the type-erased version to the valid binding         targets set.
erasedTargetNames.add(enclosingElement);

}

element.getEnclosingElement();是什么呢?是父节点。就是上面我们说的。 
基本的步骤就是上面的5步 
**1.检查用户使用的合法性**

// Start by verifying common generated code restrictions.
boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, “fields”, element)
|| isBindingInWrongPackage(BindView.class, element);

private boolean isInaccessibleViaGeneratedCode(Class<? extends Annotation> annotationClass,
String targetThing, Element element) {
boolean hasError = false;
// 得到父节点
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

//判断修饰符,如果包含private or static 就会抛出异常。
// Verify method modifiers.
Set modifiers = element.getModifiers();
if (modifiers.contains(PRIVATE) || modifiers.contains(STATIC)) {
error(element, “@%s %s must not be private or static. (%s.%s)”,
annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
/判断修饰符,如果包含private or static 就会抛出异常。
// Verify method modifiers.
Set modifiers = element.getModifiers();
if (modifiers.contains(PRIVATE) || modifiers.contains(STATIC)) {
error(element, “@%s %s must not be private or static. (%s.%s)”,
annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值