编译时注解:在编译的时候,通过注解处理器处理对应的注解。
1.新建一个项目,然后在新建两个Java Module(annotation和processor)和另一个android module(api)结构图如下
project.png
2.建立依赖关系和第三方库
a.在项目build.gradle里面添加apt插件
classpath >'com.neenbedankt.gradle.plugins:android-apt:1.8'
和
mavenCentral()
如图:
project.png
b.在app module的build.gradle里面添加
apply plugin: 'com.neenbedankt.android-apt'
和注解处理
apt project(':javaprocessor')
以及注解申明
compile project(':javaannotation')
接口调用
compile project(':api')
如图
app.png
c.在javaprocessor的build.gradle里面添加注解以及生成代码的库文件
compile 'com.google.auto:auto-common:0.8'
compile 'com.google.auto.service:auto-service:1.0-rc3'
compile 'com.squareup:javapoet:1.8.0'
compile project(':javaannotation')
如图:
javaprocessor.png
3贴出部分代码:
complier
ProxyProcessor
@AutoService(Processor.class)
public class ProxyProcessor extends AbstractProcessor {
private Filer filer;
private Elements elementUtils;
private Messager messager;
private Map<String, ProxyClass> mProxyClassMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
/**
* java version
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
/**
* deal with the annotation below
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = new HashSet<>();
set.add(BindView.class.getCanonicalName());
set.add(OnClick.class.getCanonicalName());
set.add(OnLongClick.class.getCanonicalName());
set.add(OnItemClick.class.getCanonicalName());
set.add(OnTouch.class.getCanonicalName());
set.add(OnPageChange.class.getCanonicalName());
set.add(BindStringResource.class.getCanonicalName());
return set;
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//bindView
for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {
if (!isValid(BindView.class, "fields", element)) {
return true;
}
dealWithBindView(element);
}
//to build java class file
for (Map.Entry<String, ProxyClass> entry : mProxyClassMap.entrySet()) {
try {
entry.getValue().generteFile().writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
mProxyClassMap.clear();
return true;
}
private void dealWithBindView(Element element) {
FieldViewBinding fieldViewBinding = new FieldViewBinding(element,BindView.class);
ProxyClass proxyClass = getProxyClass(element);
proxyClass.addBindView(fieldViewBinding);
}
//create proxy class
private ProxyClass getProxyClass(Element element) {
String typeElementName;
typeElementName = element.getEnclosingElement().toString();
ProxyClass proxyClass = mProxyClassMap.get(typeElementName);
if (proxyClass == null) {
proxyClass = new ProxyClass(elementUtils, element);
mProxyClassMap.put(typeElementName, proxyClass);
}
return proxyClass;
}
/**
* check is valid
* @param annotationClass
* @param targetThing
* @param element
* @return
*/
private boolean isValid(Class<? extends Annotation> annotationClass, String targetThing, Element element) {
boolean isVaild = true;
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
String qualifiedName = enclosingElement.getQualifiedName().toString();
Set<Modifier> modifiers = element.getModifiers();
//private or static
if (modifiers.toString().contains(Modifier.PRIVATE.name().toLowerCase()) || modifiers.toString().contains(Modifier.STATIC.name().toLowerCase())) {
messager.printMessage(Diagnostic.Kind.ERROR, String.format("@%s %s must not be private or static. (%s.%s)",
annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
element.getSimpleName()), element);
isVaild = false;
}
// parent is not class
if (enclosingElement.getKind() != ElementKind.CLASS) {
messager.printMessage(Diagnostic.Kind.ERROR, String.format("@%s %s may only be contained in classes. (%s.%s)",
annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
element.getSimpleName()), enclosingElement);
isVaild = false;
}
//android framework
if (qualifiedName.startsWith("android.")) {
messager.printMessage(Diagnostic.Kind.ERROR, String.format("@%s-annotated class incorrectly in Android framework package. (%s)",
annotationClass.getSimpleName(), qualifiedName), element);
isVaild = false;
}
//java framework
if (qualifiedName.startsWith("java.")) {
messager.printMessage(Diagnostic.Kind.ERROR, String.format("@%s-annotated class incorrectly in Java framework package. (%s)",
annotationClass.getSimpleName(), qualifiedName), element);
isVaild = false;
}
return isVaild;
}
}
FieldViewBinding
/**
* deal with field
*/
public class FieldViewBinding {
private VariableElement mElement;
private int mResId;
private String mVariableName;
private TypeMirror mTypeMirror;
private int getValues(Element element, Class<? extends Annotation> annotationCls) {
if (annotationCls.getSimpleName().equals(BindView.class.getSimpleName())) {
return element.getAnnotation(BindView.class).value();
} else if (annotationCls.getSimpleName().equals(BindStringResource.class.getSimpleName())) {
return element.getAnnotation(BindStringResource.class).value();
} else {
return -1;
}
}
public FieldViewBinding(Element element, Class<? extends Annotation> annotationCls) {
mElement = (VariableElement) element;
BindView bindView = element.getAnnotation(BindView.class);
mResId =getValues(element,annotationCls);
mVariableName = element.getSimpleName().toString();
mTypeMirror = element.asType();
}
public int getmResId() {
return mResId;
}
public String getmVariableName() {
return mVariableName;
}
public TypeMirror getmTypeMirror() {
return mTypeMirror;
}
public VariableElement getmElement() {
return mElement;
}
}
ProxyClass
public class ProxyClass {
private Set<FieldViewBinding> bindViews = new HashSet<>();
private Elements elementUtils;
private Element element;
public ProxyClass(Elements elementUtils, Element element) {
this.elementUtils = elementUtils;
this.element = element;
}
public void addBindView(FieldViewBinding fieldViewBinding) {
bindViews.add(fieldViewBinding);
}
public JavaFile generteFile() {
//activity
TypeElement typeElement = (TypeElement) element.getEnclosingElement();
//packageName
String packageName = elementUtils.getPackageOf(typeElement).getQualifiedName().toString();
//className
String className = typeElement.getQualifiedName().toString().substring(packageName.length() + 1).replace(".", "$");
//ClassName(className)
ClassName cls = ClassName.bestGuess(className);
//innerClass for T
String classNameT = typeElement.getQualifiedName().toString().substring(packageName.length() + 1).replace("$", ".");
//ClassName(classNameT)
ClassName clsT = ClassName.bestGuess(classNameT);
//class for IProxy
ClassName proxtCls = ClassName.get(Configure.PROXYPACKAGE, Configure.PROXYNAME);
//class type spec
TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(className + Configure.CLASSNAME_BUFFER)
.addModifiers(Modifier.PUBLIC)
.addTypeVariable(TypeVariableName.get("T", clsT))
.addSuperinterface(ParameterizedTypeName.get(proxtCls, clsT))
// for unbind
.addField(clsT,Configure.P_NAME1,Modifier.PRIVATE)
.addField(Configure.VIEW,Configure.P_NAME2,Modifier.PRIVATE)
;
//bind method type spec
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(Configure.BIND)
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.VOID)
.addParameter(clsT, Configure.P_NAME1, Modifier.FINAL)
.addParameter(Configure.VIEW, Configure.P_NAME2, Modifier.FINAL)
//for unbind
.addStatement("this."+Configure.P_NAME1+"="+Configure.P_NAME1)
.addStatement("this."+Configure.P_NAME2+"="+Configure.P_NAME2)
.addAnnotation(Override.class);
//for unbind
MethodSpec.Builder unbind=MethodSpec.methodBuilder("unbind")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(TypeName.VOID)
.addStatement("this."+Configure.P_NAME1+"="+Configure.NULL)
.addStatement("this."+Configure.P_NAME2+"="+Configure.NULL);
for (FieldViewBinding fieldViewBinding : bindViews) {
//target.$L=($L)root.findViewById($L);
methodBuilder.addStatement(Configure.P_NAME1 + ".$L = ($L)" + Configure.P_NAME2 + "." + Configure.FINDVIEWBYID + "($L)"
, fieldViewBinding.getmVariableName(), fieldViewBinding.getmTypeMirror(), fieldViewBinding.getmResId());
}
typeBuilder.addMethod(methodBuilder.build());
//for unbind
typeBuilder.addMethod(unbind.build());
clear();
return JavaFile.builder(packageName, typeBuilder.build())
.addFileComment("this is auto create file !")
.build();
}
private void clear() {
bindViews.clear();
}
}
annotation
BindView
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
api
Inject
public class Inject {
private static IProxy proxy;
public static void inject(Activity target) {
View view = target.getWindow().getDecorView();
inject(target, view);
}
public static void unbind() {
//for unbind
if (proxy != null)
proxy.unbind();
}
public static void inject(Object target, View root) {
String clsName = target.getClass().getName();
Log.e("========",clsName);
try {
Class proxyCls = Class.forName(clsName + "$$IProxy");
proxy = (IProxy) proxyCls.newInstance();
proxy.bind(target, root);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
IProxy
public interface IProxy<T> {
void bind(T target,View root);
void unbind();
}