目录

  1. 【Android】注解框架(一)-- 基础知识Java 反射
  2. 【Android】注解框架(二)-- 基础知识(Java注解)& 运行时注解框架
  3. 【Android】注解框架(三)-- 编译时注解,手写ButterKnife
  4. 【Android】注解框架(四)-- 一行代码注入微信支付

apt介绍

作为Android程序员应该绝大部分分人都用过ButterKnife,Retrofit等框架,这些框架只需要在用的时候使用注解,就可以直接使用了,非常方便。并且这些框架并没有减少性能。

那么这些框架做了哪些东西呢?

我们以ButterKnife为例:

@BindView(R.id.textview)TextView textview;private Unbinder bind;@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    bind = ButterKnife.bind(this);    textview.setText("dfjslafjsalfls");}@Overrideprotected void onDestroy() {    bind.unbind();    super.onDestroy();}

上面是我们平常使用的时候所使用的代码,这些代码到底做了什么呢?

  1. 我们给元素写上注解
  2. 在编译器执行的环节,系统收集我们所写注解的元素,并将这些元素组合成Java代码MainActivity_ViewBinding
  3. 当我们调用ButterKnife.bind的时候,通过动态注入的方式,将MainActivityMainActivity_ViewBinding关联起来,并给所有的注解所在的元素赋值。

而这些东西的核心就是在编译期间生成我们所需要的Java文件,这样我们在使用的时候就基本没有性能的影响。

网上找到的APT的流程图:

【Android】注解框架(三)-- 编译时注解,手写ButterKnife_第1张图片

手写ButterKnife

通过上面的图我们先生成这些module

【Android】注解框架(三)-- 编译时注解,手写ButterKnife_第2张图片 butterknife_流程图

app - 主项目
butterknife - android lib
annotaion - java lib
compiler - java lib

在这些module的配置文件中配置

  1. project的build.gradle

    // 如果你的gradle版本是3.0以上则不需要配置buildscript {        repositories {        google()        jcenter()        mavenCentral()    }    dependencies {        classpath 'com.android.tools.build:gradle:3.0.0'        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'    }}allprojects {    repositories {        google()        jcenter()        mavenCentral()    }}
  2. butterknife-compiler的build.gradle

    添加auto-service和javapoet这两个lib是用来方便我们生成Java代码的。

    auto-service: https://github.com/google/auto
    javapoet: http://blog.csdn.net/crazy1235/article/details/51876192

    dependencies {    implementation fileTree(include: ['*.jar'], dir: 'libs')    compile 'com.google.auto.service:auto-service:1.0-rc3'    compile 'com.squareup:javapoet:1.8.0'    implementation project(':butterknife-annotations')}
  3. app的build.gradle

    // gradle版本小于3.0apply plugin: 'com.neenbedankt.android-apt'compile project(':butterknife')compile project(':butterknife-annotations')apt project(':butterknife-compiler')// gradle版本大于3.0compile project(':butterknife')compile project(':butterknife-annotations')annotationProcessor project(':butterknife-compiler')

在annotation中写上注解

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

在compiler中编写注解生成器

java文档: http://www.yq1012.com/api/index.html-overview-summary.html
javax.annotation.processing包
javax.lang.model.element包

  1. 创建类processor继承AbstractProcessor

    @AutoService(Processor.class)public class ButterKnifeProcessor extends AbstractProcessor {    private Elements elementUtils;    private Filer filer;    @Override    public synchronized void init(ProcessingEnvironment processingEnvironment) {        super.init(processingEnvironment);        elementUtils = processingEnvironment.getElementUtils();        filer = processingEnvironment.getFiler();    }    // 指定SourceVersion    @Override    public SourceVersion getSupportedSourceVersion() {        return SourceVersion.latestSupported();    }        }
  2. 指定我们所需要的annotation

    // 指定processortypepublic Set getSupportedAnnotationTypes() {    Set types = new LinkedHashSet<>();    Set> supportedAnnotations = getSupportedAnnotations();    for (Class<? extends Annotation> supportedAnnotation : supportedAnnotations) {        types.add(supportedAnnotation.getCanonicalName());    }    return types;}private Set> getSupportedAnnotations() {    Set> annotations = new LinkedHashSet<>();    annotations.add(BindView.class);    return annotations;}
  3. 在process中处理annotation

    首先将所有获取到的BindView细分到每个Activity中

    Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);// 将获取到的bindview细分到每个classMap> elementMap = new LinkedHashMap<>();for (Element element : elements) {    // 返回activity    Element enclosingElement = element.getEnclosingElement();    List bindViewElements = elementMap.get(enclosingElement);    if (bindViewElements == null) {        bindViewElements = new ArrayList<>();        elementMap.put(enclosingElement, bindViewElements);    }    bindViewElements.add(element);}

    通过遍历的方式处理每个细分的Activity

    // 生成代码for (Map.Entry> entrySet : elementMap.entrySet()) {    Element enclosingElement = entrySet.getKey();    List bindViewElements = entrySet.getValue();    // public final class xxxActivity_ViewBinding implements Unbinder    // 获取activity的类名    String activityClassNameStr = enclosingElement.getSimpleName().toString();    System.out.println("------------->" + activityClassNameStr);    ClassName activityClassName = ClassName.bestGuess(activityClassNameStr);    ClassName unBinderClassName = ClassName.get("com.fastaoe.butterknife", "Unbinder");    TypeSpec.Builder classBuilder =            TypeSpec.classBuilder(activityClassNameStr + "_ViewBinding")                    .addModifiers(Modifier.FINAL, Modifier.PUBLIC)                    .addSuperinterface(unBinderClassName)                    // 添加属性 private MainActivity target;                    .addField(activityClassName, "target", Modifier.PRIVATE);    // unbind()    ClassName callSuperClassName = ClassName.get("android.support.annotation", "CallSuper");    MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("unbind")            .addAnnotation(Override.class)            .addAnnotation(callSuperClassName)            .addModifiers(Modifier.PUBLIC, Modifier.FINAL);    // 构造函数    MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder()            .addParameter(activityClassName, "target")            .addModifiers(Modifier.PUBLIC)            // this.target = target            .addStatement("this.target = target");    for (Element bindViewElement : bindViewElements) {        // textview        String fieldName = bindViewElement.getSimpleName().toString();        // Utils        ClassName utilsClassName = ClassName.get("com.fastaoe.butterknife", "Utils");        // R.id.textview        int resourceId = bindViewElement.getAnnotation(BindView.class).value();        // target.textview = Utils.findViewById(target, R.id.textview)        constructorMethodBuilder.addStatement("target.$L = $T.findViewById(target, $L)", fieldName, utilsClassName, resourceId);        // target.textview = null        unbindMethodBuilder.addStatement("target.$L = null", fieldName);    }    classBuilder.addMethod(unbindMethodBuilder.build())            .addMethod(constructorMethodBuilder.build());    // 获取包名    String packageName = elementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();    try {        JavaFile.builder(packageName, classBuilder.build())                .addFileComment("自己写的ButterKnife生成的代码,不要修改!!!")                .build().writeTo(filer);    } catch (IOException e) {        e.printStackTrace();    }}

在butterknife中编写注入代码

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

使用

我们的使用方式就和正真的ButterKnife一样了:

public class MainActivity extends AppCompatActivity {    @BindView(R.id.textview)    TextView textview;    private Unbinder bind;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        bind = ButterKnife.bind(this);        textview.setText("修改后的文字");    }    @Override    protected void onDestroy() {        bind.unbind();        super.onDestroy();    }}

最后

附上代码

更多相关文章

  1. Android中的人脸检测的示例代码(静态和动态)
  2. 谷歌Android为何关闭源代码?
  3. Android 之 快速开发框架 afinal
  4. Android 简介:Android SDK 和开发框架简介
  5. android boot 代码流程 1
  6. Android 混淆代码有关问题总结
  7. Android 开发常用代码
  8. Android程序实现全屏代码

随机推荐

  1. 一个错误
  2. 【高通SDM660平台 Android(安卓)10.0】(1
  3. Android中将assets中的文件拷贝到sd卡
  4. Android 音量调节
  5. android 安装apk代码
  6. Android通过手势实现的缩放处理
  7. Android 音乐播放器。
  8. android proguard
  9. Android 悬浮按钮的简单实现
  10. Android(安卓)Zygote启动流程源码解析