Android 编译时注解

本文详细介绍了一种基于注解的视图绑定方案,包括自定义注解、编译时处理注解及运行时调用流程。通过实例展示了如何实现视图绑定与点击事件处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


demo地址

http://download.youkuaiyun.com/download/ty_phone8/9966875


1、简介

注解?

说说我所知的注解吧:

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

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

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

2、注解

http://blog.youkuaiyun.com/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" annotation
Set<? 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" annotation
Set<? 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.youkuaiyun.com/download/ty_phone8/9966875


知识贵在分享!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值