首先butterknife源码分析:使用butterknife之后会自动生成这个类
package com.peakmain.butterkinfe;
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(MainActivity target, View source) {
this.target = target;
target.textView1 = Utils.findRequiredViewAsType(source, R.id.tv1, "field 'textView1'", TextView.class);
target.textView2 = Utils.findRequiredViewAsType(source, R.id.tv2, "field 'textView2'", TextView.class);
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.textView1 = null;
target.textView2 = null;
}
}
app关联三个model,并修改其中的compiler和添加插件
compile project(':butterknife_annotations')
apt project(':butterknife_compiler')
compile project(':butterknif
apply plugin: 'com.neenbedankt.android-apt'
complier关联annotation(主要用来放注解)
@Retention(RetentionPolicy.CLASS)//编译时注解
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
ButterknifeProcessor生成器:添加依赖
compile 'com.google.auto.service:auto-service:1.0-rc3'
compile 'com.squareup:javapoet:1.8.0'
/*支持中文*/
tasks.withType(JavaCompile){
options.encoding='UTF-8'
}
//ButterKnifeProcessor 源码
AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
private Filer mFiler;
private Elements mElementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
mElementUtils = processingEnvironment.getElementUtils();
}
// 1. 指定处理的版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
// 2. 给到需要处理的注解
@Override
public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
for (Class extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
private Set<Class extends Annotation>> getSupportedAnnotations() {
Set<Class extends Annotation>> annotations = new LinkedHashSet<>();
// 需要解析的自定义注解 BindView OnClick
annotations.add(BindView.class);
return annotations;
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
// System.out.println("------------------------>");
// System.out.println("------------------------>");
// System.out.println("------------------------>");
// System.out.println("------------------------>");
// process 方法代表的是,有注解就都会进来 ,但是这里面是一团乱麻
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
/*for (Element element : elements) {
Element enclosingElement = element.getEnclosingElement();
System.out.println("------------------------>"+element.getSimpleName().toString()+" "+enclosingElement.getSimpleName().toString());
}*/
// 解析 属性 activity -> List
MapList > elementsMap = new LinkedHashMap<>();
for (Element element : elements) {
Element enclosingElement = element.getEnclosingElement();
List viewBindElements = elementsMap.get(enclosingElement);
if (viewBindElements == null) {
viewBindElements = new ArrayList<>();
elementsMap.put(enclosingElement, viewBindElements);
}
viewBindElements.add(element);
}
// 生成代码
for (Map.EntryList > entry : elementsMap.entrySet()) {
Element enclosingElement = entry.getKey();
List viewBindElements = entry.getValue();
// public final class xxxActivity_ViewBinding implements Unbinder
String activityClassNameStr = enclosingElement.getSimpleName().toString();
ClassName activityClassName = ClassName.bestGuess(activityClassNameStr);
ClassName unbinderClassName = ClassName.get("com.butterknife","Unbinder");
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(activityClassNameStr+"_ViewBinding")
.addModifiers(Modifier.FINAL,Modifier.PUBLIC).addSuperinterface(unbinderClassName)
.addField(activityClassName,"target",Modifier.PRIVATE);
// 实现 unbind 方法
// android.support.annotation.CallSuper
ClassName callSuperClassName = ClassName.get("android.support.annotation","CallSuper");
MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("unbind")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC,Modifier.FINAL)
.addAnnotation(callSuperClassName);
unbindMethodBuilder.addStatement("$T target = this.target",activityClassName);
unbindMethodBuilder.addStatement("if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");");
// 构造函数
MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder()
.addParameter(activityClassName,"target");
// this.target = target;
constructorMethodBuilder.addStatement("this.target = target");
// findViewById 属性
for (Element viewBindElement : viewBindElements) {
// target.textView1 = Utils.findRequiredViewAsType(source, R.id.tv1, "field 'textView1'", TextView.class);
// target.textView1 = Utils.findViewById(source, R.id.tv1);
String filedName = viewBindElement.getSimpleName().toString();
ClassName utilsClassName = ClassName.get("com.butterknife","Utils");
int resId = viewBindElement.getAnnotation(BindView.class).value();
constructorMethodBuilder.addStatement("target.$L = $T.findViewById(target, $L)",filedName,utilsClassName,resId);
// target.textView1 = null;
unbindMethodBuilder.addStatement("target.$L = null",filedName);
}
classBuilder.addMethod(unbindMethodBuilder.build());
classBuilder.addMethod(constructorMethodBuilder.build());
// 生成类,看下效果
try {
String packageName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
JavaFile.builder(packageName,classBuilder.build())
.addFileComment("butterknife 自动生成")
.build().writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
System.out.println("翻车了!");
}
}
return false;
}
最后model是Butterknife目的如butterknife插件使用的使用的时候butterknife.bind(this);
public class ButterKnife {
public static Unbinder bind(Activity activity){
// xxxActivity_ViewBinding viewBinding = new xxxActivity_ViewBinding(this)
try {
Class extends Unbinder> bindClassName=(Class extends Unbinder>)
Class.forName(activity.getClass().getName()+"_ViewBinding");
//构造函数 new MainActivity_ViewBinding()
Constructor extends Unbinder> bindConstructor = bindClassName.getDeclaredConstructor(activity.getClass());
Unbinder unbinder = bindConstructor.newInstance();
return unbinder;
} catch (Exception e) {
e.printStackTrace();
return Unbinder.EMPTY;
}
}
}
Unbinder是复制butterknife的源码
public interface Unbinder {
@UiThread
void unbind();
Unbinder EMPTY = new Unbinder() {
@Override public void unbind() { }
};
}
public class Utils {
public static T findViewById(Activity activity,int viewId){
return (T) activity.findViewById(viewId);
}
}
使用
@BindView(R.id.tv1)
TextView textView1;
@BindView(R.id.tv2)
TextView textView2;
@BindView(R.id.tv2)
TextView textView3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Unbinder unbinder = ButterKnife.bind(this);
/*textView1.setText("textView1");
textView2.setText("textView2");*/
}
这时候就完成了但是这时候的生成的id是一串整形数据,这时候我们需要根据id的数字获得到R.id.xxx,复制butterknife的相关源码即可
package com.butterknife.annotation;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import com.sun.source.tree.ClassTree;
import com.sun.source.util.Trees;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeScanner;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/**
* 作者:Peakmain
* 版本:1.0
* 创建日期:2018/3/6 22:31
* 邮件:[email protected]
* 描述:
*/
@AutoService(Processor.class)
public class ButterknifeProcessor extends AbstractProcessor {
private Filer mFiler;
private Elements mElementUtils;
private final Map symbols = new LinkedHashMap<>();
private Trees trees;
private static final List SUPPORTED_TYPES = Arrays.asList(
"array", "attr", "bool", "color", "dimen", "drawable", "id", "integer", "string"
);
private Types typeUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
mElementUtils = processingEnv.getElementUtils();
trees = Trees.instance(processingEnv);
typeUtils = processingEnv.getTypeUtils();
}
//1.获得当前版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();//最大版本
}
// 2. 给到需要处理的注解
@Override
public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
for (Class extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
private Set> getSupportedAnnotations() {
Set> annotations = new LinkedHashSet<>();
//添加注解
annotations.add(BindView.class);
return annotations;
}
//所有注解都会走向这里
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
/* System.out.println("------------------------>");
System.out.println("------------------------>");
System.out.println("------------------------>");
System.out.println("------------------------>");*/
// process 方法代表的是,有注解就都会进来 ,但是这里面是一团乱麻
/*Set extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);
for (Element element : elements) {//element.getSimpleName().toString() 自己定义的名字
Element enclosingElement = element.getEnclosingElement();//代表MainActivity或者LoginActivity
System.out.println("------------------------>"+
element.getSimpleName().toString()+" "+enclosingElement.getSimpleName().toString());
}*/
//解析属性 activity->list
scanForRClasses(roundEnv);
Set extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);
Map> elementsMap = new LinkedHashMap<>();
for (Element element : elements) {
Element enclosingElement = element.getEnclosingElement();//获得的是activity的名字
List viewBindElement = elementsMap.get(enclosingElement);//根据key获得所有注解下自己定义的名字,默认是空
if (viewBindElement == null) {
viewBindElement = new ArrayList<>();
elementsMap.put(enclosingElement, viewBindElement);
}
viewBindElement.add(element);//添加节点
}
//生成代码
for (Map.Entry> entry : elementsMap.entrySet()) {
Element enclosingElement = entry.getKey();//获得key
List viewBindElement = entry.getValue();//获得value
//生成public final class xxxActivity_ViewBinding implements Unbinder
String activityClassNameStr = enclosingElement.getSimpleName().toString();//如MainActivity
ClassName activityName = ClassName.bestGuess(activityClassNameStr);
ClassName unbinderClassName = ClassName.get("com.butterknife", "Unbinder");//获得类名
//报错需要添加依赖 生成public final class xxxActivity_ViewBinding implements Unbinder
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(activityClassNameStr + "_ViewBinding")
.addModifiers(Modifier.FINAL, Modifier.PUBLIC)
.addSuperinterface(unbinderClassName)
.addField(activityName, "target", Modifier.PRIVATE);//添加属性
// 实现 unbind 方法
// android.support.annotation.CallSuper
ClassName callSuperClassName = ClassName.get("android.support.annotation", "CallSuper");
MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("unbind")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addAnnotation(callSuperClassName);
//添加构造
MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder()
.addParameter(activityName, "target");
// this.target = target
constructorMethodBuilder.addStatement(" this.target = target");
//findvById
for (Element element : viewBindElement) {
// target.textView1 = Utils.findRequiredViewAsType(source, R.id.tv1, "field 'textView1'", TextView.class);
// target.textView1 = Utils.findViewById(source, R.id.tv1);
String filedName = element.getSimpleName().toString();//属性的名字,如tv_name
ClassName utilsClassName = ClassName.get("com.butterknife", "Utils");
int resId = element.getAnnotation(BindView.class).value();
QualifiedId qualifiedId = elementToQualifiedId(element, resId);
Id id = getId(qualifiedId);
CodeBlock codeBlock = id.code;
constructorMethodBuilder.addStatement("target.$L =$L.findViewById(target,$L)", filedName, utilsClassName, codeBlock);
// target.textView1 = null;
unbindMethodBuilder.addStatement("target.$L = null", filedName);
}
classBuilder.addMethod(unbindMethodBuilder.build());
classBuilder.addMethod(constructorMethodBuilder.build());
//生成类,看下效果
try {
//生成包名
String packName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
JavaFile.builder(packName, classBuilder.build())
.addFileComment("butterknife 自动生成")
.build().writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
System.out.println("生成失败!");
}
}
return false;
}
private QualifiedId elementToQualifiedId(Element element, int id) {
return new QualifiedId(mElementUtils.getPackageOf(element), id);
}
private Id getId(QualifiedId qualifiedId) {
if (symbols.get(qualifiedId) == null) {
symbols.put(qualifiedId, new Id(qualifiedId.id));
}
return symbols.get(qualifiedId);
}
private void scanForRClasses(RoundEnvironment env) {
if (trees == null) return;
RClassScanner scanner = new RClassScanner();
for (Class extends Annotation> annotation : getSupportedAnnotations()) {
for (Element element : env.getElementsAnnotatedWith(annotation)) {
JCTree tree = (JCTree) trees.getTree(element, getMirror(element, annotation));
if (tree != null) { // tree can be null if the references are compiled types and not source
scanner.setCurrentPackage(mElementUtils.getPackageOf(element));
tree.accept(scanner);
}
}
}
for (Map.Entry> packageNameToRClassSet
: scanner.getRClasses().entrySet()) {
PackageElement respectivePackageName = packageNameToRClassSet.getKey();
for (Symbol.ClassSymbol rClass : packageNameToRClassSet.getValue()) {
parseRClass(respectivePackageName, rClass, scanner.getReferenced());
}
}
}
private void parseRClass(PackageElement respectivePackageName, Symbol.ClassSymbol rClass,
Set referenced) {
TypeElement element;
try {
element = rClass;
} catch (MirroredTypeException mte) {
element = (TypeElement) typeUtils.asElement(mte.getTypeMirror());
}
JCTree tree = (JCTree) trees.getTree(element);
if (tree != null) { // tree can be null if the references are compiled types and not source
IdScanner idScanner =
new IdScanner(symbols, mElementUtils.getPackageOf(element), respectivePackageName,
referenced);
tree.accept(idScanner);
} else {
parseCompiledR(respectivePackageName, element, referenced);
}
}
private void parseCompiledR(PackageElement respectivePackageName, TypeElement rClass,
Set referenced) {
for (Element element : rClass.getEnclosedElements()) {
String innerClassName = element.getSimpleName().toString();
if (SUPPORTED_TYPES.contains(innerClassName)) {
for (Element enclosedElement : element.getEnclosedElements()) {
if (enclosedElement instanceof VariableElement) {
String fqName = mElementUtils.getPackageOf(enclosedElement).getQualifiedName().toString()
+ ".R."
+ innerClassName
+ "."
+ enclosedElement.toString();
if (referenced.contains(fqName)) {
VariableElement variableElement = (VariableElement) enclosedElement;
Object value = variableElement.getConstantValue();
if (value instanceof Integer) {
int id = (Integer) value;
ClassName rClassName =
ClassName.get(mElementUtils.getPackageOf(variableElement).toString(), "R",
innerClassName);
String resourceName = variableElement.getSimpleName().toString();
QualifiedId qualifiedId = new QualifiedId(respectivePackageName, id);
symbols.put(qualifiedId, new Id(id, rClassName, resourceName));
}
}
}
}
}
}
}
private static class RClassScanner extends TreeScanner {
// Maps the currently evaluated rPackageName to R Classes
private final Map> rClasses = new LinkedHashMap<>();
private PackageElement currentPackage;
private Set referenced = new HashSet<>();
@Override public void visitSelect(JCTree.JCFieldAccess jcFieldAccess) {
Symbol symbol = jcFieldAccess.sym;
if (symbol != null
&& symbol.getEnclosingElement() != null
&& symbol.getEnclosingElement().getEnclosingElement() != null
&& symbol.getEnclosingElement().getEnclosingElement().enclClass() != null) {
Set rClassSet = rClasses.get(currentPackage);
if (rClassSet == null) {
rClassSet = new HashSet<>();
rClasses.put(currentPackage, rClassSet);
}
referenced.add(getFqName(symbol));
rClassSet.add(symbol.getEnclosingElement().getEnclosingElement().enclClass());
}
}
Map> getRClasses() {
return rClasses;
}
Set getReferenced() {
return referenced;
}
void setCurrentPackage(PackageElement packageElement) {
this.currentPackage = packageElement;
}
}
private static String getFqName(Symbol rSymbol) {
return rSymbol.packge().getQualifiedName().toString()
+ ".R."
+ rSymbol.enclClass().name.toString()
+ "."
+ rSymbol.name.toString();
}
private static class IdScanner extends TreeScanner {
private final Map ids;
private final PackageElement rPackageName;
private final PackageElement respectivePackageName;
private final Set referenced;
IdScanner(Map ids, PackageElement rPackageName,
PackageElement respectivePackageName, Set referenced) {
this.ids = ids;
this.rPackageName = rPackageName;
this.respectivePackageName = respectivePackageName;
this.referenced = referenced;
}
@Override public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
for (JCTree tree : jcClassDecl.defs) {
if (tree instanceof ClassTree) {
ClassTree classTree = (ClassTree) tree;
String className = classTree.getSimpleName().toString();
if (SUPPORTED_TYPES.contains(className)) {
ClassName rClassName = ClassName.get(rPackageName.getQualifiedName().toString(), "R",
className);
VarScanner scanner = new VarScanner(ids, rClassName, respectivePackageName, referenced);
((JCTree) classTree).accept(scanner);
}
}
}
}
}
private static class VarScanner extends TreeScanner {
private final Map ids;
private final ClassName className;
private final PackageElement respectivePackageName;
private final Set referenced;
private VarScanner(Map ids, ClassName className,
PackageElement respectivePackageName, Set referenced) {
this.ids = ids;
this.className = className;
this.respectivePackageName = respectivePackageName;
this.referenced = referenced;
}
@Override public void visitVarDef(JCTree.JCVariableDecl jcVariableDecl) {
if ("int".equals(jcVariableDecl.getType().toString())) {
String resourceName = jcVariableDecl.getName().toString();
if (referenced.contains(getFqName(jcVariableDecl.sym))) {
int id = Integer.valueOf(jcVariableDecl.getInitializer().toString());
QualifiedId qualifiedId = new QualifiedId(respectivePackageName, id);
ids.put(qualifiedId, new Id(id, className, resourceName));
}
}
}
}
private static AnnotationMirror getMirror(Element element,
Class extends Annotation> annotation) {
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
if (annotationMirror.getAnnotationType().toString().equals(annotation.getCanonicalName())) {
return annotationMirror;
}
}
return null;
}
}
Id
public final class Id {
private static final ClassName ANDROID_R = ClassName.get("android", "R");
final int value;
final CodeBlock code;
final boolean qualifed;
Id(int value) {
this.value = value;
this.code = CodeBlock.of("$L", value);
this.qualifed = false;
}
Id(int value, ClassName className, String resourceName) {
this.value = value;
this.code = className.topLevelClassName().equals(ANDROID_R)
? CodeBlock.of("$L.$N", className, resourceName)
: CodeBlock.of("$T.$N", className, resourceName);
this.qualifed = true;
}
@Override public boolean equals(Object o) {
return o instanceof Id && value == ((Id) o).value;
}
@Override public int hashCode() {
return value;
}
@Override public String toString() {
throw new UnsupportedOperationException("Please use value or code explicitly");
}
}
QualifiedId
public final class QualifiedId {
final PackageElement packageName;
final int id;
QualifiedId(PackageElement packageName, int id) {
this.packageName = packageName;
this.id = id;
}
@Override public String toString() {
return "QualifiedId{packageName='" + packageName + "', id=" + id + '}';
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof QualifiedId)) return false;
QualifiedId other = (QualifiedId) o;
return id == other.id
&& packageName.equals(other.packageName);
}
@Override public int hashCode() {
int result = packageName.hashCode();
result = 31 * result + id;
return result;
}