【Android】注解框架(三)-- 编译时注解,手写ButterKnife
目录
- 【Android】注解框架(一)-- 基础知识Java 反射
- 【Android】注解框架(二)-- 基础知识(Java注解)& 运行时注解框架
- 【Android】注解框架(三)-- 编译时注解,手写ButterKnife
- 【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();}
上面是我们平常使用的时候所使用的代码,这些代码到底做了什么呢?
- 我们给元素写上注解
- 在编译器执行的环节,系统收集我们所写注解的元素,并将这些元素组合成Java代码
MainActivity_ViewBinding
类 - 当我们调用
ButterKnife.bind
的时候,通过动态注入的方式,将MainActivity
和MainActivity_ViewBinding
关联起来,并给所有的注解所在的元素赋值。
而这些东西的核心就是在编译期间生成我们所需要的Java文件,这样我们在使用的时候就基本没有性能的影响。
网上找到的APT的流程图:
手写ButterKnife
通过上面的图我们先生成这些module
butterknife_流程图app - 主项目
butterknife - android lib
annotaion - java lib
compiler - java lib
在这些module的配置文件中配置
-
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() }}
-
butterknife-compiler的build.gradle
添加auto-service和javapoet这两个lib是用来方便我们生成Java代码的。
auto-service: https://github.com/google/auto
javapoet: http://blog.csdn.net/crazy1235/article/details/51876192dependencies { 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')}
-
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包
-
创建类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(); } }
-
指定我们所需要的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;} -
在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(); }}
最后
附上代码
更多相关文章
- Android中的人脸检测的示例代码(静态和动态)
- Android(安卓)TextView文字链接4中方法
- android 使用eclipse编译 FBReaderJ流程
- 谷歌Android为何关闭源代码?
- Android内核和驱动篇-Android内核介绍
- android文件存储
- mono for android 百度map binding项目
- Android中设置控件透明度的方法
- Android(安卓)简介:Android(安卓)SDK 和开发框架简介