android编译时注解

前言

总结一下仿写的ButterKnife,以及所遇到的坑.

第一步 创建模块

创建两个Java Library 和一个Android Library

Java Library的一个是
butterknife-annotations —— 用来声明注解,
butterknife-compiler——用来自定义注解

Android Library
butterknife

第二步 导入依赖

导入依赖

butterknife-compiler中
.
annotationProcessor ‘com.google.auto.service:auto-service:1.0-rc6’
implementation ‘com.google.auto:auto-common:0.10’
implementation ‘com.squareup:javapoet:1.10.0’
implementation ‘com.google.auto.service:auto-service:1.0-rc6’
implementation project(path: ‘:butterknife-annotations’)

app中
.
implementation project(’:butterknife-annotations’)
annotationProcessor project(’:butterknife-compiler’)
implementation project(’:butterknife’)

第三步 编写代码

butterknife-annotations中

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
    int value();
}

butterknife-compiler中

@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
    private Filer mFiler;
    private Elements mElementUtils;

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

    //1。指定处理的版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    //2.需要处理的注解
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
            types.add(annotation.getCanonicalName());
        }
        return types;
    }

    //2.声明需要的注解
    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) {
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);

        //解析属性 activity -> List<Element>
        Map<Element, List<Element>> elementsMap = new LinkedHashMap<>();

        for (Element element : elements) {
            Element enclosingElement = element.getEnclosingElement();

            List<Element> viewBindElements = elementsMap.get(enclosingElement);
            if (viewBindElements == null) {
                viewBindElements = new ArrayList<>();
                elementsMap.put(enclosingElement, viewBindElements);
            }
            viewBindElements.add(element);
        }

        //生成代码
        for (Map.Entry<Element, List<Element>> entry : elementsMap.entrySet()) {
            Element enclosingElement = entry.getKey();
            List<Element> 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.example.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("androidx.annotation","CallSuper");
            MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("unbind")
                    .addAnnotation(Override.class)
                    .addAnnotation(callSuperClassName)
                    .addModifiers(Modifier.PUBLIC,Modifier.FINAL);

            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");
            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.example.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(constructorMethodBuilder.build());
            classBuilder.addMethod(unbindMethodBuilder.build());

            try {
                String packageName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
                JavaFile.builder(packageName,classBuilder.build())
                        .addFileComment("butterknife 自动生成")
                        .build().writeTo(mFiler);
                System.out.println("success!");
            } catch (IOException e) {
                System.out.println("翻车了!");
                e.printStackTrace();
            }
        }
        return false;
    }

}

butterknife中有三个类

public class ButterKnife {
    public static Unbinder bind(Activity activity) {
        try {
            Class<? extends Unbinder> bindClassName = (Class<? extends Unbinder>) Class.forName(activity.getClass().getName() + "_ViewBinding");
            //构造函数
            Constructor<? extends  Unbinder> bindConstructor = bindClassName.getDeclaredConstructor(activity.getClass());
            Unbinder unbinder = bindConstructor.newInstance(activity);
            return unbinder;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Unbinder.EMPTY;
    }
}
public interface Unbinder {
    @UiThread
    void unbind();

    Unbinder EMPTY = new Unbinder() {
        @Override
        public void unbind() {
        }
    };
}
public class Utils {
    public static <T extends View> T findViewById(Activity activity,int viewId){
        return activity.findViewById(viewId);
    }
}

app模块中

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.tv1)
    TextView tv1;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }
}

以上就是用到的所有代码。

第四步 自动生成的结果

下面代码就是编译后自动生成的目录在

app\build\generated\ap_generated_sources\debug\out\包名

// butterknife 自动生成
public final class MainActivity_ViewBinding implements Unbinder {
  private MainActivity target;

  MainActivity_ViewBinding(MainActivity target) {
    this.target = target;
    target.tv1 = Utils.findViewById(target, 2131165359);
  }

  @Override
  @CallSuper
  public final void unbind() {
    MainActivity target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    target.tv1 = null;
  }
}

最后总结下遇到的坑

第一个就是这个方法没有调用

public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {}

解决方法

把所有需要的依赖到进去即可

第二个是编译以后没有没有生成预期效果

解决方法

在app的类中做些修改再重新编译即可

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值