IOC注入技术之编译时注入-Butterknife手写实现

butterknife Github地址:https://github.com/JakeWharton/butterknife

1 butterknife简单使用

在app的build.gradle中添加依赖:

    implementation 'com.jakewharton:butterknife:8.4.0'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'

使用如下:

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.tv_text)
    TextView tvText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        tvText.setText("被设置了");
    }
    @OnClick(R.id.btn_submit)
    void submit() {
        Toast.makeText(MainActivity.this,"点击了按钮",Toast.LENGTH_SHORT).show();
    }
}

MakeProject后,打开如下文件,说明butterknife在编译时自动为我们生成了代码。

在这里插入图片描述

我们简单看下源码

在这里插入图片描述
在这里插入图片描述

在Utils中的133行可以看出,最后还是调用了findViewById方法。


2 手写实现

在这之前可以参照以下文章:

  1. Android注解处理器(APT)简单实例:https://blog.csdn.net/hongxue8888/article/details/99820719

  2. Android组件化2-自定义注解处理器:https://blog.csdn.net/hongxue8888/article/details/97228827#ProcessingEnvironment_43

  3. javapoet:
    https://blog.csdn.net/hongxue8888/article/details/99737289

  4. Java 注解与注解处理器:
    https://blog.okclouder.cn/2019/09/08/java-annotation/

  5. Android编译时注解APT实战(AbstractProcessor)
    https://www.jianshu.com/p/07ef8ba80562

  6. 在线文档-jdk-zh :
    http://tool.oschina.net/apidocs/apidoc?api=jdk-zh


要实现的结构图如下:
在这里插入图片描述

2.1 annotations

新建一个名为annotations的Java Library,用于存放需要使用的注解

2.1.1 BindView

添加注解BindView:

@Target(ElementType.FIELD)  //声明我们定义的注解是放在什么上面的  也就是作用域
@Retention(RetentionPolicy.SOURCE)  //声明我们定义的注解的生命周期   java--->class-->run
public @interface BindView {
    int value();
}

2.2 annotation_compiler

新建一个名为annotation_compiler的Java Library,里面添加注解处理器,用于在编译期自动生成我们需要的代码。

2.2.1 注册注解处理器

在 processor 的 main 目录下新建 resources 目录,然后添加一个 META-INF/services 目录。

在 META-INF/services 目录下新建一个名叫 javax.annotation.processing.Processor 的文件。

文件内容如下:

com.hongx.annotation_compiler.AnnotationCompiler

2.2.2 使用AutoService注册

如果觉得上一步的注册 service 步骤麻烦,可以使用 google 的 auto service 自动注册。

在annotation_compiler的build.gradle配置如下:

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    // 注册我们的注解处理器
	// implementation 'com.google.auto.service:auto-service:1.0-rc3'
    // As-3.4.1 + gradle5.1.1-all + auto-service:1.0-rc4
    annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'//1
    compileOnly'com.google.auto.service:auto-service:1.0-rc4'

    implementation project(path: ':annotations')//2
}

sourceCompatibility = "7"
targetCompatibility = "7"

注释1:auto-service用于注册注解处理器
注释2:添加annotations依赖

auto-service使用只需在添加

  @AutoService(Processor.class)
  public class AnnotationCompiler extends AbstractProcessor {
		...
  }

Make Project后会自动生成javax.annotation.processing.Processor 文件,如下图:
在这里插入图片描述


2.2.3 AnnotationCompiler

具体的代码如下:

/**
 * 这个类就是APT
 */
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {

    //1.定义一个用于生成文件的对象
    Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnvironment.getFiler();
    }

    //2.需要确定当前APT处理所有模块中哪些注解
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new HashSet<>();
        types.add(BindView.class.getCanonicalName());
//        types.add(Override.class.getCanonicalName());
        return types;
    }

    //3.支持的JDK的版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 在这个方法中,我们去生成IBinder的实现类
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        //-------------------------注释1 开始------------------//
        //得到程序中所有写了BindView注解的元素的集合
        //类元素(TypeElement)
        //可执行元素(ExecutableElement)
        //属性元素(VariableElement)
        Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        //定义一个MAP用来分类
        Map<String, List<VariableElement>> map = new HashMap<>();

        //开始分类存入MAP中
        for (Element element : elementsAnnotatedWith) {
            VariableElement variableElement = (VariableElement) element;
            //获取activity的名字
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
            List<VariableElement> elementList = map.get(activityName);
            if (elementList == null) {
                elementList = new ArrayList<>();
                map.put(activityName, elementList);
            }
            elementList.add(variableElement);
        }
        //运行到这就已经完成了分类工作
        //-------------------------注释1结束------------------//

        if (map.size() > 0) {
            //开始写入文件
            Writer writer = null;
            //每一个activity都要生成一个对应的文件
            Iterator<String> iterator = map.keySet().iterator();
            while (iterator.hasNext()) {
                String activityName = iterator.next();
                List<VariableElement> elementList = map.get(activityName);

                //获取包名
                TypeElement enclosingElement = (TypeElement) elementList.get(0).getEnclosingElement();
                String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();

                try {
                    //生成文件
                    //包名.MainActivity_ViewBinding
                    JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + activityName + "_ViewBinding");
                    writer = sourceFile.openWriter();
                    //        package com.example.dn_butterknife;
                    writer.write("package " + packageName + ";\n\n");
                    //        import com.example.dn_butterknife.IBinder;
                    writer.write("import " + packageName + ".IBinder;\n\n");
                    //        public class MainActivity_ViewBinding implements IBinder<com.example.dn_butterknife.MainActivity>{
                    writer.write("public class " + activityName + "_ViewBinding implements IBinder<"
                            + packageName + "." + activityName + ">{\n\n");
                    //            @Override
                    writer.write("  @Override\n");
                    //            public void bind(com.example.dn_butterknife.MainActivity target) {
                    writer.write("  public void bind(" + packageName + "." + activityName + " target){\n\n");
                    // target.tvText=(android.widget.TextView)target.findViewById(2131165325);
                    for (VariableElement variableElement : elementList) {
                        //获取控件的名字
                        String variableName = variableElement.getSimpleName().toString();
                        //获取ID
                        int id = variableElement.getAnnotation(BindView.class).value();
                        //获取控件的类型
                        TypeMirror typeMirror = variableElement.asType();
                        writer.write("      target." + variableName + "=(" + typeMirror + ")target.findViewById(" + id + ");\n");

                    }
                    writer.write("\n}}");

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (writer != null) {
                        try {
                            writer.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
        }
        return false;
    }
}

init 被注解处理工具调用,并输入 processingEnvironment 参数。processingEnvironment 提供了很多工具类,如 Elements、Types、Filer 和 Messenger 等。
process 注解处理的主函数,这里处理扫描、评估和处理注解的代码,以及生产 Java 文件。
getSupportedAnnotationTypes 指明注解处理器是处理哪些注解的。
getSupportedSourceVersion 指明使用的 Java 版本,通常返回 SourceVersion.latestSupported。


下面解释下注释1代码块:

elementsAnnotatedWith包含了所有Activity中的BindView注解,所以我们使用一个Map对这些注解进行分类,而Map中的键就是activity名,值就是BindView注解的集合,原理如下图所示:
在这里插入图片描述


Elements:一个用来处理Element的工具类,源代码的每一个部分都是一个特定类型的Element,例如:

package com.example;    // PackageElement

public class Foo {        // TypeElement

    private int a;      // VariableElement
    private Foo other;  // VariableElement

    public Foo () {}    // ExecuteableElement

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

再解释下下面这句代码

  String activityName = variableElement.getEnclosingElement().
  getSimpleName().toString();

variableElement是属性元素,getEnclosingElement获取它外层的元素,也就是Activity。

具体api文档如下:
在这里插入图片描述

2.2.4 添加配置到app中

在app的build.gradle中配置如下:

 implementation project(path: ':annotations')//1
 annotationProcessor project(path: ':annotation_compiler')//2

注释1:添加annotations依赖。
注释2:annotation_compiler作为一个注解处理器添加到build.gradle中。

2.3 生成代码

新建IBinder接口

public interface IBinder<T> {
    void bind(T target);
}

待会会实现这个接口,用于绑定activity

在MainActivity中使用BindView注解,如下:

  public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.tvText)
        TextView textView;
    	...
    }

MakeProject后生成如下图代码:
在这里插入图片描述
代码如下:

package com.hongx.hxbutterknife;

import com.hongx.hxbutterknife.IBinder;

public class MainActivity_ViewBinding implements IBinder<com.hongx.hxbutterknife.MainActivity>{

  @Override
  public void bind(com.hongx.hxbutterknife.MainActivity target){

      target.tvText=(android.widget.TextView)target.findViewById(2131165326);

}}

接下来需要通过IBinder的bind方法,绑定activity:

public class HxButterKnife {

    public static void bind(Activity activity) {
        String name = activity.getClass().getName() + "_ViewBinding";
        try {
            Class<?> aClass = Class.forName(name);
            IBinder iBinder = (IBinder) aClass.newInstance();
            iBinder.bind(activity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

2.4 使用

在MainActivity的onCreate方法中绑定activity

import com.hongx.annotations.BindView;

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.tv_text)
    TextView tvText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        HxButterKnife.bind(this);
        tvText.setText("被设置了");
    }

}

2.5 JavaPoet

参考:

https://github.com/square/javapoet

https://square.github.io/javapoet/1.x/javapoet/

JavaPoet - 优雅地生成代码:
https://www.jianshu.com/p/fba2eec47976

JavaPoet 看这一篇就够了:
https://juejin.im/entry/58fefebf8d6d810058a610de

谈谈APT和JavaPoet的一些使用技巧和要点

以上的连接字符串工作是比较繁琐的,我们可以使用JavaPoet开源库进行编写,提升效率。

JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。这个框架功能非常有用,我们可以很方便的使用它根据注解、数据库模式、协议格式等来对应生成代码。通过这种自动化生成代码的方式,可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作。


项目主页及源码:https://github.com/square/javapoet

2.5.1 万能例子

参考:
JavaPoet 看这一篇就够了:
https://juejin.im/entry/58fefebf8d6d810058a610de

展示一段几乎涵盖你所常见 case 的例子 (仅为了展示 JavaPoet 用法, 生成的代码可能编译不过). 如果哪里不知道怎么生成的, 可以方便的在下面的生成代码里查找生成方法。

在annotation_compiler下新建CodeGenerate,注意:只能在JavaLibrary下使用

package com.hongx.annotation_compiler;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import com.squareup.javapoet.WildcardTypeName;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.lang.model.element.Modifier;

/**
 * @author: fuchenming
 * @create: 2019-09-10 10:23
 */
public class CodeGenerate {
    public static void main(String[] args) {
        TypeSpec clazz = clazz(builtinTypeField(),          // int
                arrayTypeField(),            // int[]
                refTypeField(),              // File
                typeField(),                 // T
                parameterizedTypeField(),    // List<String>
                wildcardTypeField(),         // List<? extends String>
                constructor(),               // 构造函数
                method(code()));             // 普通方法
        JavaFile javaFile = JavaFile.builder("com.hongx.javapoet", clazz).build();

        System.out.println(javaFile.toString());
    }

    /**
     * `public abstract class Clazz<T> extends String implements Serializable, Comparable<String>, Comparable<? extends String> {
     * ...
     * }`
     *
     * @return
     */
    public static TypeSpec clazz(FieldSpec builtinTypeField, FieldSpec arrayTypeField, FieldSpec refTypeField,
                                 FieldSpec typeField, FieldSpec parameterizedTypeField, FieldSpec wildcardTypeField,
                                 MethodSpec constructor, MethodSpec methodSpec) {
        return TypeSpec.classBuilder("Clazz")
                // 限定符
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                // 泛型
                .addTypeVariable(TypeVariableName.get("T"))

                // 继承与接口
                .superclass(String.class)
                .addSuperinterface(Serializable.class)
                .addSuperinterface(ParameterizedTypeName.get(Comparable.class, String.class))
                .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Map.class),
                        TypeVariableName.get("T"),
                        WildcardTypeName.subtypeOf(String.class)))

                // 初始化块
                .addStaticBlock(CodeBlock.builder().build())
                .addInitializerBlock(CodeBlock.builder().build())

                // 属性
                .addField(builtinTypeField)
                .addField(arrayTypeField)
                .addField(refTypeField)
                .addField(typeField)
                .addField(parameterizedTypeField)
                .addField(wildcardTypeField)

                // 方法 (构造函数也在此定义)
                .addMethod(constructor)
                .addMethod(methodSpec)

                // 内部类
                .addType(TypeSpec.classBuilder("InnerClass").build())

                .build();
    }

    /**
     * 内置类型
     */
    public static FieldSpec builtinTypeField() {
        // private int mInt;
        return FieldSpec.builder(int.class, "mInt", Modifier.PRIVATE).build();
    }

    /**
     * 数组类型
     */
    public static FieldSpec arrayTypeField() {
        // private int[] mArr;
        return FieldSpec.builder(int[].class, "mArr", Modifier.PRIVATE).build();
    }

    /**
     * 需要导入 import 的类型
     */
    public static FieldSpec refTypeField() {
        // private File mRef;
        return FieldSpec.builder(File.class, "mRef", Modifier.PRIVATE).build();
    }

    /**
     * 泛型
     */
    public static FieldSpec typeField() {
        // private File mT;
        return FieldSpec.builder(TypeVariableName.get("T"), "mT", Modifier.PRIVATE).build();
    }

    /**
     * 参数化类型
     */
    public static FieldSpec parameterizedTypeField() {
        // private List<String> mParameterizedField;
        return FieldSpec.builder(ParameterizedTypeName.get(List.class, String.class),
                "mParameterizedField",
                Modifier.PRIVATE)
                .build();
    }

    /**
     * 通配符参数化类型
     *
     * @return
     */
    public static FieldSpec wildcardTypeField() {
        // private List<? extends String> mWildcardField;
        return FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(List.class),
                WildcardTypeName.subtypeOf(String.class)),
                "mWildcardField",
                Modifier.PRIVATE)
                .build();
    }

    /**
     * 构造函数
     */
    public static MethodSpec constructor() {
        return MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .build();
    }

    /**
     * `@Override
     * public <T> Integer method(String string, T t, Map<Integer, ? extends T> map) throws IOException, RuntimeException {
     * ...
     * }`
     *
     * @param codeBlock
     * @return
     */
    public static MethodSpec method(CodeBlock codeBlock) {
        return MethodSpec.methodBuilder("method")
                .addAnnotation(Override.class)
                .addTypeVariable(TypeVariableName.get("T"))
                .addModifiers(Modifier.PUBLIC)
                .returns(int.class)
                .addParameter(String.class, "string")
                .addParameter(TypeVariableName.get("T"), "t")
                .addParameter(ParameterizedTypeName.get(ClassName.get(Map.class),
                        ClassName.get(Integer.class),
                        WildcardTypeName.subtypeOf(TypeVariableName.get("T"))),
                        "map")
                .addException(IOException.class)
                .addException(RuntimeException.class)
                .addCode(codeBlock)
                .build();
    }

    /**
     * ‘method’ 方法中的具体语句
     */
    public static CodeBlock code() {
        return CodeBlock.builder()
                .addStatement("int foo = 1")
                .addStatement("$T bar = $S", String.class, "a string")

                // Object obj = new HashMap<Integer, ? extends T>(5);
                .addStatement("$T obj = new $T(5)",
                        Object.class, ParameterizedTypeName.get(ClassName.get(HashMap.class),
                                ClassName.get(Integer.class),
                                WildcardTypeName.subtypeOf(TypeVariableName.get("T"))))

                // method(new Runnable(String param) {
                //   @Override
                //   void run() {
                //   }
                // });
                .addStatement("baz($L)", TypeSpec.anonymousClassBuilder("$T param", String.class)
                        .superclass(Runnable.class)
                        .addMethod(MethodSpec.methodBuilder("run")
                                .addAnnotation(Override.class)
                                .returns(TypeName.VOID)
                                .build())
                        .build())

                // for
                .beginControlFlow("for (int i = 0; i < 5; i++)")
                .endControlFlow()

                // while
                .beginControlFlow("while (false)")
                .endControlFlow()

                // do... while
                .beginControlFlow("do")
                .endControlFlow("while (false)")

                // if... else if... else...
                .beginControlFlow("if (false)")
                .nextControlFlow("else if (false)")
                .nextControlFlow("else")
                .endControlFlow()

                // try... catch... finally
                .beginControlFlow("try")
                .nextControlFlow("catch ($T e)", Exception.class)
                .addStatement("e.printStackTrace()")
                .nextControlFlow("finally")
                .endControlFlow()

                .addStatement("return 0")
                .build();
    }

}

右键-Run CodeGenerate.main() 后 查看输出:

package com.hongx.javapoet;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.Comparable;
import java.lang.Exception;
import java.lang.Integer;
import java.lang.Object;
import java.lang.Override;
import java.lang.Runnable;
import java.lang.RuntimeException;
import java.lang.String;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class Clazz<T> extends String implements Serializable, Comparable<String>, Map<T, ? extends String> {
  static {
  }

  private int mInt;

  private int[] mArr;

  private File mRef;

  private T mT;

  private List<String> mParameterizedField;

  private List<? extends String> mWildcardField;

  {
  }

  public Clazz() {
  }

  @Override
  public <T> int method(String string, T t, Map<Integer, ? extends T> map) throws IOException,
      RuntimeException {
    int foo = 1;
    String bar = "a string";
    Object obj = new HashMap<Integer, ? extends T>(5);
    baz(new Runnable(String param) {
      @Override
      void run() {
      }
    });
    for (int i = 0; i < 5; i++) {
    }
    while (false) {
    }
    do {
    } while (false);
    if (false) {
    } else if (false) {
    } else {
    }
    try {
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
    }
    return 0;
  }

  class InnerClass {
  }
}

2.5.2 使用JavaPoet实现

如果想调试注解处理器,可以参考这篇文章:
AndroidStudio调试(debug)注解处理器(AnnotationProcessor):
https://blog.csdn.net/hongxue8888/article/details/99710884

先看使用,在MainActivity和SecondActivity都添加了两个注解

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.tv_text)
    TextView tvText;

    @BindView(R.id.tv_text2)
    TextView tvText2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        HxButterKnife.bind(this);
        tvText.setText("被设置了");
        tvText2.setText("被设置了2");
    }

    public void toSecond(View view) {
        Intent intent = new Intent(MainActivity.this,SecondActivity.class);
        startActivity(intent);

    }
}
public class SecondActivity extends AppCompatActivity {
    @BindView(R.id.tv_text3)
    TextView tvText3;

    @BindView(R.id.tv_text4)
    TextView tvText4;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        HxButterKnife.bind(this);
        tvText3.setText("被设置了3");
        tvText4.setText("被设置了4");

    }
}

查看注解处理器生成的代码:

package com.hongx.hxbutterknife;

import android.widget.TextView;
import java.lang.Override;

public class MainActivity_ViewBinding2 implements IBinder<MainActivity> {
  @Override
  public void bind(MainActivity target) {
    target.tvText = (TextView)target.findViewById(2131165326);
    target.tvText2 = (TextView)target.findViewById(2131165327);
  }
}
package com.hongx.hxbutterknife;

import android.widget.TextView;
import java.lang.Override;

public class SecondActivity_ViewBinding2 implements IBinder<SecondActivity> {
  @Override
  public void bind(SecondActivity target) {
    target.tvText3 = (TextView)target.findViewById(2131165328);
    target.tvText4 = (TextView)target.findViewById(2131165329);
  }
}


具体实现代码如下:

package com.hongx.annotation_compiler;

import com.google.auto.service.AutoService;
import com.hongx.annotations.BindView;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
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.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;


/**
 * 这个类就是APT
 */
@AutoService(Processor.class)
public class AnnotationCompiler2 extends AbstractProcessor {

    //1.定义一个用于生成文件的对象
    Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnvironment.getFiler();
    }

    //2.需要确定当前APT处理所有模块中哪些注解
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new HashSet<>();
        types.add(BindView.class.getCanonicalName());
        return types;
    }

    //3.支持的JDK的版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 在这个方法中,我们去生成IBinder的实现类
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        //-------------------------注释1 开始------------------//
        //得到程序中所有写了BindView注解的元素的集合
        //类元素(TypeElement)
        //可执行元素(ExecutableElement)
        //属性元素(VariableElement)
        Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        //定义一个MAP用来分类
        Map<String, List<VariableElement>> map = new HashMap<>();

        //开始分类存入MAP中
        for (Element element : elementsAnnotatedWith) {
            VariableElement variableElement = (VariableElement) element;
            //获取activity的名字
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
            List<VariableElement> elementList = map.get(activityName);
            if (elementList == null) {
                elementList = new ArrayList<>();
                map.put(activityName, elementList);
            }
            elementList.add(variableElement);
        }
        //运行到这就已经完成了分类工作
        //-------------------------注释1结束------------------//

        if (map.size() > 0) {

//            package com.hongx.hxbutterknife;
//
//            import com.hongx.hxbutterknife.IBinder;
//
//            public class MainActivity_ViewBinding implements IBinder<com.hongx.hxbutterknife.MainActivity>{
//
//                @Override
//                public void bind(com.hongx.hxbutterknife.MainActivity target){
//
//                    target.tvText=(android.widget.TextView)target.findViewById(2131165326);
//
//                }
//
//            }

            //每一个activity都要生成一个对应的文件
            Iterator<String> iterator = map.keySet().iterator();
            while (iterator.hasNext()) {
                String activityName = iterator.next();
                List<VariableElement> elementList = map.get(activityName);

                //获取包名
                TypeElement enclosingElement = (TypeElement) elementList.get(0).getEnclosingElement();
                String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();


                List<CodeBlock> codeBlockList = new ArrayList<>();

                for (VariableElement variableElement : elementList) {
                    //获取控件的名字
                    String variableName = variableElement.getSimpleName().toString();
                    //获取ID
                    int resourceId = variableElement.getAnnotation(BindView.class).value();
                    //获取控件的类型
                    TypeMirror typeMirror = variableElement.asType();

                    CodeBlock singleCodeBlock = codeBlock(variableName, typeMirror, resourceId);

                    codeBlockList.add(singleCodeBlock);

                }
                CodeBlock allCodeBlock = getAllBlock(codeBlockList);


                /**
                 * $L $S $T $N 都是占位符。
                 *
                 * $L:常量
                 * $S:String类型
                 * $T:变量指定类型,可以通过ClassName来指定外部类名
                 * $N:生成的方法名或者变量名
                 */
                MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind")
                        .addModifiers(Modifier.PUBLIC)
                        .returns(void.class)
                        // 重写的方法
                        .addAnnotation(Override.class)
                        .addParameter(ClassName.get(packageName, activityName), "target")
                        .addCode(allCodeBlock);

                ClassName IBinderClass = ClassName.get("com.hongx.hxbutterknife", "IBinder");
                ClassName superinterface = ClassName.bestGuess(ClassName.get(packageName, activityName).toString());

                TypeSpec activityClass = TypeSpec.classBuilder(activityName + "_ViewBinding2")
                        .addModifiers(Modifier.PUBLIC)

                        // 添加接口,ParameterizedTypeName的参数1是接口,参数2是接口的泛型
                        .addSuperinterface(ParameterizedTypeName.get(IBinderClass, superinterface))

                        .addMethod(methodBuilder.build())
                        .build();

                JavaFile javaFile = JavaFile.builder(packageName, activityClass).build();
                try {
                    javaFile.writeTo(filer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

	 /**
     * 生成多条代码
     */
    public static CodeBlock getAllBlock(List<CodeBlock> codeBlockList) {
        CodeBlock.Builder codeBlockBuilder = CodeBlock.builder();
        for (CodeBlock codeBlock : codeBlockList) {
            codeBlockBuilder.add(codeBlock);
        }
        return codeBlockBuilder.build();
    }

    public static CodeBlock codeBlock(String variableName, TypeMirror typeMirror, int resourceId) {

        CodeBlock.Builder codeBlockBuilder = CodeBlock.builder();

        CodeBlock codeBlock = CodeBlock.builder()
                .addStatement("$N.$L = ($T)target.findViewById($L)", "target", variableName, typeMirror, resourceId)
                .build();

        codeBlockBuilder.add(codeBlock);

        return codeBlockBuilder.build();
    }

}

从生成的代码中可以看出bind中会有多条代码,如下图所示,所以我们可以在getAllBlock()方法实现多条代码。
在这里插入图片描述

代码Github地址:https://github.com/345166018/AndroidIOC/tree/master/HxButterknife

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值