Android 编译时注解

来源:互联网 发布:怎么搞垮一个淘宝店 编辑:程序博客网 时间:2024/05/22 01:58

demo地址

http://download.csdn.net/download/ty_phone8/9966875


1、简介

注解?

说说我所知的注解吧:

  • 标识,说到这儿可能就懵逼了,啥?
    举个例子吧,比如:@Override、SupportedOptions、SupportedSourceVersion、SuppressWarnings等等,当然需要具体了解可以查看更多

  • 运行时动态处理
    这个大家见得就多了去了,在运行时拿到类的Class对象,然后遍历其方法、变量,判断有无注解声明,然后做一些操作。这个东西性能低大多时候不建议使用,常见的框架有:xutils , afinal 等。

  • 编译时动态处理
    说道这个东西可能做Android都了解吧!butterknife 这个框架有木有没有使用过的?大多数人都是用过吧,没使用过也没关系,建议大家去看看。这类注解会在编译的时候,根据注解标识,动态生成一些类或者xml都可以,在运行时期,这类注解是没有的,会依靠动态生成的类做一些操作,因为没有反射,效率和直接调用方法没什么区别~~~

2、注解

http://blog.csdn.net/ty_phone8/article/details/77718519

或者看看其他的对注解属性介绍的博客,看过之后,才能更好的理解下面例子的注解

3、例子

那我们就说一下过程:

前期准备:我们先看一看项目结构吧

这里写图片描述

可以看出有两个 java的jar 和一个Android的jar


首先呢,我们需要在整个项目的build文件中添加一句话:

classpath group: 'com.neenbedankt.gradle.plugins', name: 'android-apt', version: '1.8'

接下来呢,我们也需要在app项目的build文件中添加一句话:

apply plugin: 'com.neenbedankt.android-apt'

接下来,我们就按照inject-annotation、inject-apt、inject-api的顺序创建jar包,当然不必在意jar包的名字,咱们想怎么命名就怎么命名。


inject-annotation 主要用来自定义注解

我这里呢准备了两个注解:
@Target(ElementType.FIELD)@Retention(RetentionPolicy.CLASS)public @interface BindView {    int value() default 0;}
@Target(ElementType.METHOD)@Retention(RetentionPolicy.CLASS)public @interface OnClick {    int[] value() default {};}

inject-apt 主要在编译时获取您的注解并且进行一些操作

我这里呢,定义的处理器如下:
@AutoService(Processor.class)@SupportedSourceVersion(SourceVersion.RELEASE_7)public class AgentProcessor extends AbstractProcessor {    ...    @Override    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {        ...        return false;    }}

这儿直接使用了

compile 'com.google.auto.service:auto-service:1.0-rc3'

这个开元jar包来去除,建立复杂的 META-INF/services/javax.annotation.processing.Processor文件

对注解字段或者方法的操作:
//Gets all the elements of the "BindView" annotationSet<? extends Element> bindViewElement = roundEnv.getElementsAnnotatedWith(BindView.class);for (Element element : bindViewElement) {    //Because the "BindView" annotation is a field, the strong is converted to the "VariableElement" type    VariableElement variableElement = (VariableElement) element;    //Provides access to information about types and their members    TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();    String qualifiedName = typeElement.getQualifiedName().toString();    ProxyEntity entity = proxyEetityMap.get(qualifiedName);    if (null == entity) {        entity = new ProxyEntity(elementUtils, typeElement);        proxyEetityMap.put(qualifiedName, entity);    }    BindView bindView = element.getAnnotation(BindView.class);    int value = bindView.value();    entity.setVariableElementMapValue(value, variableElement);}
//Gets all the elements of the "OnClick" annotationSet<? extends Element> onClickElement = roundEnv.getElementsAnnotatedWith(OnClick.class);for (Element element : onClickElement) {    //Because the "OnClick" annotation is a method, the strong is converted to the "ExecutableElement" type    ExecutableElement executableElement = (ExecutableElement) element;    //Provides access to information about types and their members    TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement();    String qualifiedName = typeElement.getQualifiedName().toString();    ProxyEntity entity = proxyEetityMap.get(qualifiedName);    if (null == entity) {        entity = new ProxyEntity(elementUtils, typeElement);        proxyEetityMap.put(qualifiedName, entity);    }    OnClick onClick = element.getAnnotation(OnClick.class);    int[] values = onClick.value();    for (int value : values) {        entity.setExecutableElementMapValue(value, executableElement);    }}
这里呢,我建立了一个实体类来实现我需要进行的操作:
/*    在这里我使用了 javapoet 来生成我需要的java文件    有需要可以区简单的了解一下 javapoet 的用法*/public class ProxyEntity {    public static final String PROXY = "ViewBinding";    private String qualifiedName;//Full name of the class    private String packageName;//The package name    private String className;//taxon    private String proxyClassName;//The proxy class name    public ProxyEntity(Elements elementUtils, TypeElement typeElement) {        qualifiedName = typeElement.getQualifiedName().toString();        PackageElement packageElement = elementUtils.getPackageOf(typeElement);        packageName = packageElement.getQualifiedName().toString();        className = qualifiedName.substring(packageName.length() + 1).replace(".", "_");        proxyClassName = className + "_" + PROXY;    }    public JavaFile GenerateProxyClasses() {        ...        return javaFile;    }    private Map<Integer, VariableElement> variableElementMap;    private Map<Integer, ExecutableElement> executableElementMap;    public void setVariableElementMapValue(Integer key, VariableElement value) {        if (null == variableElementMap) {            variableElementMap = new LinkedHashMap<>();        }        variableElementMap.put(key, value);    }    public void setExecutableElementMapValue(Integer key, ExecutableElement value) {        if (null == executableElementMap) {            executableElementMap = new LinkedHashMap<>();        }        executableElementMap.put(key, value);    }}

inject-api 在运行时的时候调用

这里建立了一个接口,和一个获取代理类的类

代理类需要实现的接口:

public interface ViewBinding<T> {    void bind(T t, Object source);    void unbind();}

获取代理类的类

public class AgentInject {    private static final String SUFFIX = "_ViewBinding";    public static void injectView(Activity activity) {        ViewBinding proxyActivity = findProxyActivity(activity);        View view = activity.getWindow().getDecorView();        proxyActivity.bind(activity, view);    }    private static ViewBinding findProxyActivity(Object activity) {        try {            Class clazz = activity.getClass();            Class injectorClazz = Class.forName(clazz.getName() + SUFFIX);            return (ViewBinding) injectorClazz.newInstance();        } catch (ClassNotFoundException e) {            e.printStackTrace();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }        throw new RuntimeException(String.format("can not find %s , something when compiler.", activity.getClass().getSimpleName() + SUFFIX));    }}

在app中调用

public class MainActivity extends AppCompatActivity {    @BindView(R.id.text1)    TextView text1;    @BindView(R.id.text2)    Button text2;    @BindView(R.id.text3)    TextView text3;    @BindView(R.id.text4)    Button text4;    @BindView(R.id.text5)    TextView text5;    @BindView(R.id.text6)    Button text6;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        AgentInject.injectView(this);        text1.setText("This is my first TextView.");        text2.setText("This is my first Button.");        text3.setText("This is my second TextView.");        text4.setText("This is my second Button.");        text5.setText("This is my third TextView.");        text6.setText("This is my third Button.");    }    private static final String TAG = "MainActivity";    @OnClick({R.id.text1, R.id.text3, R.id.text5})    public void TestOnClickTxt(View v) {        switch (v.getId()) {            case R.id.text1:                Log.d(TAG, "TestOnClickTxt:  1 - " + v);                break;            case R.id.text3:                Log.d(TAG, "TestOnClickTxt:  2 - " + v);                break;            case R.id.text5:                Log.d(TAG, "TestOnClickTxt:  3 - " + v);                break;        }    }    @OnClick({R.id.text2, R.id.text4, R.id.text6})    public void TestOnClickBtn(View v) {        switch (v.getId()) {            case R.id.text2:                Log.d(TAG, "TestOnClickTxt:  1 - " + v);                break;            case R.id.text4:                Log.d(TAG, "TestOnClickTxt:  2 - " + v);                break;            case R.id.text6:                Log.d(TAG, "TestOnClickTxt:  3 - " + v);                break;        }    }}

demo地址

http://download.csdn.net/download/ty_phone8/9966875


知识贵在分享!