手写ButterKnife

 

前言

ButterKnife是一个依赖注入框架,8.0之前是通过反射的方式实现,具体实现可以参考这篇文章自定义注解,今天我们来看下8.0之后的编译时注解实现方式,编译时注解相比运行时注解效率高,是通过在编译时生成代码的方式来绑定控件。

结构

手写ButterKnife_第1张图片

app:我们的Android项目

butterknife-annotation:java library,定义注解

butterknife-compiler:java library,注解处理器

 

手写ButterKnife_第2张图片

编译时报错

手写ButterKnife_第3张图片

 

解决办法:

在annotation和compiler的gradle模块中,添加

tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

手写ButterKnife_第4张图片

手写ButterKnife_第5张图片

/**
 * 描述:用于绑定变量
 *
 * @author Create by zxy on 2018/5/10
 */
@Retention(RetentionPolicy.SOURCE) // 注解只在源码级别保留
@Target(ElementType.FIELD) // 注解用在字段上
public @interface BindView {
    int value();
}
/**
 * 描述:提供bind方法供生成的类调用
 *
 * @author Create by zxy on 2018/5/10
 */
public interface ViewBinder {
    void bind(T target);
}

build.gradle文件

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"

手写ButterKnife_第6张图片

/**
 * 描述:相当于一个监视者,监控源文件中的注解
 *
 * @author Create by zxy on 2018/5/10
 */

@AutoService(Processor.class) // 注册注解处理器
public class ButterKnifeProcessor extends AbstractProcessor {
    //写文件的对象
    private Filer mFiler;
    private Elements mElementUtils;


    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mElementUtils = processingEnv.getElementUtils();
        mFiler = processingEnv.getFiler();
    }

    /**
     * 注解处理器支持的Java版本
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 注解处理器支持的注解名称
     */
    @Override
    public Set getSupportedAnnotationTypes() {
        Set annotationTypes = new HashSet<>();
        annotationTypes.add(BindView.class.getCanonicalName());
        return annotationTypes;
    }

    /**
     * @param roundEnv
     *
     * package practice.lxn.cn.androidpractice.pojo; // PackageElement
     *   public class Book implements Parcelable{ // TypeElement
     *      private int bookId; // VariableElement
     *      private String bookName; // VariableElement
     *      public int getBookId() { // ExecutableElement
     *          return bookId;
     *      }
     */
    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        // 获取所有包含BindView注解的元素
        Set elements = roundEnv.getElementsAnnotatedWith(BindView.class);
        // 需要对不同Activity中的注解进行分类,因为Set集合中包含了所有Activity中的注解
        Map> activityElementMap = new HashMap<>();
        for (Element element : elements) {
            VariableElement variableElement = (VariableElement) element;
            //获取当前元素对应的Activity名称
            String activityName = getActivityName(variableElement);
            List elementList = activityElementMap.get(activityName);
            if (elementList == null) {
                elementList = new ArrayList<>();
                //将Activity名称和它对应的元素集合存放到一起
                activityElementMap.put(activityName,elementList);
            }
            elementList.add(variableElement);
        }

        // 开始产生Java文件
        for (String activityName : activityElementMap.keySet()) {
            // 获取Activity对应的带注解的成员
            List elementList = activityElementMap.get(activityName);
            // 获取包名
            String packageName = getPackageName(elementList.get(0));
            // 获取最后生成的文件的名称package practice.lxn.cn.testapp.MainActivity_ViewBinder;
//            String viewBinderName = activityName + "_ViewBinder";
            /* 需要生成文件的格式
            package practice.lxn.cn.testapp;
            import practice.lxn.cn.testapp.ViewBinder
            public class MainActivity_ViewBinder implements ViewBinder {
                @Override
                public void bind(MainActivity target) {
                    target.btn = (Button)target.findViewById(1231123423432);
                }
            }*/
            //===========================================================================
           /* Writer writer;
            //MainActivity_ViewBinder
            String simpleName = elementList.get(0).getEnclosingElement().getSimpleName().toString() + "_ViewBinder";
            try {
                // 方式一:通过原生的JavaFileObject拼接
                JavaFileObject javaFileObject = mFiler.createSourceFile(viewBinderName);
                writer = javaFileObject.openWriter();
                writer.write("package " + packageName +";");
                writer.write("\n");
                writer.write("import " + packageName + ".ViewBinder;");
                writer.write("\n");
                writer.write("public class " + simpleName + " implements ViewBinder<" + activityName + "> {");
                writer.write("\n");
                writer.write("public void bind(" + activityName + " target) {");
                writer.write("\n");
                for (VariableElement element : elementList) {
                    String variableName = element.getSimpleName().toString();
                    TypeMirror typeMirror = element.asType();
                    int id = element.getAnnotation(BindView.class).value();
                    writer.write("target." + variableName + " = (" + typeMirror + ")target.findViewById(" + id + ");");
                    writer.write("\n");
                    writer.write("}");
                    writer.write("\n");
                    writer.write("}");
                  
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                writer.close(); // 写完需要关闭
             }*/
            //方式二:通过JavaPoet提供的API
              /* 需要生成文件的格式
            package practice.lxn.cn.testapp;
            import practice.lxn.cn.testapp.ViewBinder
            public class MainActivity_ViewBinder implements ViewBinder {
                @Override
                public void bind(MainActivity target) {
                    target.btn = (Button)target.findViewById(1231123423432);
                }
            }*/
            String simpleName = elementList.get(0).getEnclosingElement().getSimpleName().toString();
            ClassName viewBinderName = ClassName.get(ViewBinder.class.getPackage().getName(), ViewBinder.class.getSimpleName());
            ClassName activityClassName = ClassName.bestGuess(simpleName);
            TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(activityClassName + "_ViewBinder")
                    .addModifiers(Modifier.PUBLIC)
                    .addSuperinterface(ParameterizedTypeName.get(viewBinderName,activityClassName));
            MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind")
                    .addModifiers(Modifier.PUBLIC)
                    .addParameter(TypeVariableName.get(activityName),"target")
                    .returns(TypeName.VOID);
            for (VariableElement element : elementList) {
                String variableName = element.getSimpleName().toString();
                TypeMirror typeMirror = element.asType();
                int id = element.getAnnotation(BindView.class).value();
                methodBuilder.addStatement("target." + variableName +"= (" + typeMirror + ")" + "target.findViewById(" + id + ");");
            }
            MethodSpec bind = methodBuilder.build();
            TypeSpec MainActivity_ViewBinder = typeBuilder.addMethod(bind).build();
            JavaFile javaFile = JavaFile.builder(packageName,MainActivity_ViewBinder)
                    .build();
            try {
                javaFile.writeTo(mFiler);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 获取包名
     */
    private String getPackageName(VariableElement variableElement) {
        TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
        PackageElement packageElement = mElementUtils.getPackageOf(typeElement);
        return packageElement.getQualifiedName().toString();
    }

    /**
     * 获取Activity名称
     */
    private String getActivityName(VariableElement variableElement) {
        TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
        String packageName = getPackageName(variableElement);
        // package practice.lxn.cn.testapp.MainActivity
        return packageName + "." + typeElement.getSimpleName().toString();
    }
}
apply plugin: 'java-library'

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation project(':butterknife-annotation')
    implementation 'com.google.auto.service:auto-service:1.0-rc3'
    // 动态生成Java源文件的API
    implementation 'com.squareup:javapoet:1.9.0'
}

tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"

在ButterKnifeProcessor中,我们可以通过两种方式生成最终的源文件,通过原生的JavaFileObject或者JavaPoet,JavaPoet是square开源的一个项目,通过下面方式引入

 

implementation 'com.squareup:javapoet:1.9.0'

主项目中

最终生成的源文件

 

手写ButterKnife_第7张图片

 

package practice.lxn.cn.testapp;

import practice.lxn.cn.butterknife_annotation.ViewBinder;

public class MainActivity_ViewBinder implements ViewBinder {
  public void bind(practice.lxn.cn.testapp.MainActivity target) {
    target.btn= (android.widget.Button)target.findViewById(2131427445);;
  }
}

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(手写ButterKnife)