您的位置:首页 > 移动开发 > Android开发

编译期注解框架浅析

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,原理其实并不复杂,希望大家能多用编译期注解时框架,写出更多简洁的代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息