Android 编译时注解实现

编译时注解:在编译的时候,通过注解处理器处理对应的注解。
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();
}

最后给出最后所有的代码(BindView,OnClick,OnLongClick...)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容