编译期注解框架浅析
2016-07-20 16:31
411 查看
编译期注解框架浅析
简介
由于Android开发已经进入一定规模,所以开发效率和代码的简洁开始引发人们的注意,而android对于性能要求比较高,所以基于反射已经无法满足,所以编译期注解也就火了起来。首先理解下编译期注解的原理,编译期注解就是在代码编译后会生成一些全新的代码,而在执行阶段,就会使用这些生成的代码。所以编译期注解的本质就是“生成代码”,如何生成,说了可能都不相信,是拿字符串拼接出的。所以编译期注解不是解决一些问题的方法而仅仅是为了简化代码的方案。如果你愿意多写一些代码,完全可以不使用它。
编译期注解要素
1.注解2.处理注解的类(也是这个类生成代码)
3.api接口,这里要注意了编译期注解怎么也是一个静态动作,不是执行时候做的,所以你想让生成的代码工作肯定得调api了
白话流程
这里指的是我们的代码写完之后,框架的执行流程首先,编译期代码的时候,会生成一份规定格式的代码(后面简称”苦逼类”),然后当我们调用某个api的时候,就会通过反射去生成那个”苦逼类”,然后去执行对应的方法,但执行哪个方法呢,肯定是有个接口类了,无论我们生成哪个”苦逼类”都会掉哪个接口方法。
代码操练
由于我比较懒,就不写了,找到butterknife的几个核心类的核心方法,贴出来,到时候大家修改写也能写个。首先是注解:
@Retention(CLASS) @Target(FIELD) public @interface BindView { /** View ID to which the field will be bound. */ @IdRes int value(); }
处理注解的类:
@AutoService(Processor.class) public final class ButterKnifeProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment env) { super.init(env); elementUtils = env.getElementUtils(); typeUtils = env.getTypeUtils(); filer = env.getFiler(); trees = Trees.instance(processingEnv); } @Override public Set<String> getSupportedAnnotationTypes() { Set<String> types = new LinkedHashSet<>(); types.add(BindView.class); return types; } @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) { Map<TypeElement, BindingClass> targetClassMap = findAndParseTargets(env); for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) { TypeElement typeElement = entry.getKey(); BindingClass bindingClass = entry.getValue(); for (JavaFile javaFile : bindingClass.brewJava()) { try { javaFile.writeTo(filer); } catch (IOException e) { error(typeElement, "Unable to write view binder for type %s: %s", typeElement, e.getMessage()); } } } return true; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } }
ok,主要就是这四个方法,其中生成代码,使用了com.squareup.javapoet.JavaFile这个类帮助完成。
执行api:
public final class ButterKnife { public static Unbinder bind(@NonNull Activity target) { Class<?> targetClass = target.getClass(); ViewBinder<Object> viewBinder = BINDERS.get(cls); if (viewBinder != null) { if (debug) Log.d(TAG, "HIT: Cached in view binder map."); return viewBinder.bind(Finder.DIALOG, target, source); } String clsName = cls.getName(); if (clsName.startsWith("android.") || clsName.startsWith("java.")) { if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search."); return NOP_VIEW_BINDER.bind(Finder.DIALOG, target, source); } //noinspection TryWithIdenticalCatches Resolves to API 19+ only type. try { Class<?> viewBindingClass = Class.forName(clsName + "_ViewBinder"); //noinspection unchecked viewBinder = (ViewBinder<Object>) viewBindingClass.newInstance(); BINDERS.put(cls, viewBinder); return viewBinder.bind(Finder.DIALOG, target, source);; } }
其实就是通过类名反射生成一个”苦逼类”对象,然后执行它的bind方法。
说好的接口:
public interface ViewBinder<T> { Unbinder bind(Finder finder, T target, Object source); }
最后让我们来看下我生成的一个“苦逼类”源码
// Generated code from Butter Knife. Do not modify! import android.widget.TextView; import butterknife.Unbinder; import butterknife.internal.Finder; import butterknife.internal.ViewBinder; import java.lang.IllegalStateException; import java.lang.Object; import java.lang.Override; public class TestActivity$$ViewBinder<T extends TestActivity> implements ViewBinder<T> { @Override public Unbinder bind(Finder finder, T target, Object source) { return new InnerUnbinder<>(target, finder, source); } protected static class InnerUnbinder<T extends TestActivity> implements Unbinder { protected T target; protected InnerUnbinder(T target, Finder finder, Object source) { this.target = target; target.mtv = finder.findRequiredViewAsType(source, 2131492981, "field 'mtv'", TextView.class); } @Override public void unbind() { T target = this.target; if (target == null) throw new IllegalStateException("Bindings already cleared."); target.mtv = null; this.target = null; } } }
大家可以看到是实现了上面那个接口吧。
ok,原理其实并不复杂,希望大家能多用编译期注解时框架,写出更多简洁的代码。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories