Android自定义注解

Android注解以及自定义注解实例与解析 Android注解以及自定义注解实例与解析 1. 什么是注解 注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行标记,例如我们常见的@Override和@Deprecated, 一般常用的注解可以分为三类: 一类是Java自带的标准注解,包括@Override(标明重写某个方法)、@Deprecated(标明某个类或方法过时)和@SuppressWarnings(标明要忽略的警告),使用这些注解后编译器就会进行检查。 一类为元注解,元注解 阅读详情
仿写BufferKnife来熟悉自定义注解

1、创建一个Java Library:annotationprocessor

注意:一定要创建一个Java Library,而不是Android Library

因为在使用自定义AbstractProcessor需要使用到javax包中的相关类和接口,这个在android库中并不存在,所以需要使用到Java库。

image

build.gradle的配置:
apply plugin: 'java'

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.google.auto.service:auto-service:1.0-rc3'
    implementation 'com.squareup:javapoet:1.9.0'
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"

自定义注解处理器Processor:ZFViewBinderProcessor

通过运行时annotation预处理技术实现动态的生成代码

为了实现编译时生成代码需要配置:
依赖:

implementation 'com.squareup:javapoet:1.9.0'

自定义注解处理器注解:@AutoService(Processor.class)

@AutoService(Processor.class)
public class ZFViewBinderProcessor extends AbstractProcessor {
    //...
}

这样就完成了配置,会生成这样一个文件:

image

@AutoService(Processor.class)
public class ZFViewBinderProcessor extends AbstractProcessor {

    private Filer mFiler; //文件相关的辅助类
    private Elements mElementUtils; //元素相关的辅助类
    private Messager mMessager; //日志相关的辅助类
    private Map<String, AnnotatedClass> mAnnotatedClassMap;

    /**
     * 该方法主要用于一些初始化的操作,通过该方法的参数ProcessingEnvironment可以获取一些列有用的工具类。
     * @param processingEnv
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mFiler = processingEnv.getFiler();
        mElementUtils = processingEnv.getElementUtils();
        mMessager = processingEnv.getMessager();
        mAnnotatedClassMap = new TreeMap<>();
    }

     /**
     * 注解处理器的核心方法,处理具体的注解
     * @param set
     * @param roundEnv
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
        mAnnotatedClassMap.clear();
        try {
            processBindView(roundEnv);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            error(e.getMessage());
        }

        for (AnnotatedClass annotatedClass : mAnnotatedClassMap.values()) {
            try {
                annotatedClass.generateFile().writeTo(mFiler);
            } catch (IOException e) {
                error("Generate file failed, reason: %s", e.getMessage());
            }
        }
        return true;
    }

    private void processBindView(RoundEnvironment roundEnv) throws IllegalArgumentException {

        for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {
            AnnotatedClass annotatedClass = getAnnotatedClass(element);
            BindViewField bindViewField = new BindViewField(element);
            annotatedClass.addField(bindViewField);
        }
    }

    private AnnotatedClass getAnnotatedClass(Element element) {
        TypeElement typeElement = (TypeElement) element.getEnclosingElement();
        String fullName = typeElement.getQualifiedName().toString();
        AnnotatedClass annotatedClass = mAnnotatedClassMap.get(fullName);
        if (annotatedClass == null) {
            annotatedClass = new AnnotatedClass(typeElement, mElementUtils);
            mAnnotatedClassMap.put(fullName, annotatedClass);
        }
        return annotatedClass;
    }

    /**
     * 返回此 Processor 支持的注释类型的名称。
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(BindView.class.getCanonicalName());
        return types;
    }
    private void error(String msg, Object... args) {
        mMessager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args));
    }
    
    /**
     * 返回此注释 Processor 支持的最新的源版本,该方法可以通过注解@SupportedSourceVersion指定。
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}
注解类:BindView
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface BindView {
    int value() default -1;
}
预编译时动态生成Java类:AnnotatedClass
public class AnnotatedClass {
    private static class TypeUtil {
        static final ClassName BINDER = ClassName.get("com.zf.annotationapi", "ViewBinder");
        static final ClassName PROVIDER = ClassName.get("com.zf.annotationapi", "ViewFinder");
    }

    private TypeElement mTypeElement;
    private ArrayList<BindViewField> mFields;
    private Elements mElements;

    public AnnotatedClass(TypeElement mTypeElement, Elements mElements) {
        this.mTypeElement = mTypeElement;
        this.mFields = new ArrayList<>();
        this.mElements = mElements;
    }

    void addField(BindViewField field) {
        mFields.add(field);
    }

    JavaFile generateFile() {
        //generateMethod
        MethodSpec.Builder bindViewMethod = MethodSpec.methodBuilder("bindView")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Override.class)
                .addParameter(TypeName.get(mTypeElement.asType()), "host")
                .addParameter(TypeName.OBJECT, "source")
                .addParameter(TypeUtil.PROVIDER, "finder");

        for (BindViewField field : mFields) {
            // find views
            bindViewMethod.addStatement("host.$N = ($T)(finder.findView(source, $L))",
                    field.getFieldName(), ClassName.get(field.getFieldType()), field.getResId());
        }

        MethodSpec.Builder unBindViewMethod = MethodSpec.methodBuilder("unBindView")
                .addModifiers(Modifier.PUBLIC)
                .addParameter(TypeName.get(mTypeElement.asType()), "host")
                .addAnnotation(Override.class);
        for (BindViewField field : mFields) {
            unBindViewMethod.addStatement("host.$N = null", field.getFieldName());
        }

        //generaClass
        TypeSpec injectClass = TypeSpec.classBuilder(mTypeElement.getSimpleName() + "$$ViewBinder")
                .addModifiers(Modifier.PUBLIC)
                .addSuperinterface(ParameterizedTypeName.get(TypeUtil.BINDER, TypeName.get(mTypeElement.asType())))
                .addMethod(bindViewMethod.build())
                .addMethod(unBindViewMethod.build())
                .build();

        String packageName = mElements.getPackageOf(mTypeElement).getQualifiedName().toString();

        return JavaFile.builder(packageName, injectClass).build();
    }

}
每个绑定项的元素信息类:BindViewField
class BindViewField {
    private VariableElement mVariableElement;
    private int mResId;

    BindViewField(Element element) throws IllegalArgumentException {
        if (element.getKind() != ElementKind.FIELD) {
            throw new IllegalArgumentException(String.format("Only fields can be annotated with @%s",
                    BindView.class.getSimpleName()));
        }
        mVariableElement = (VariableElement) element;

        BindView bindView = mVariableElement.getAnnotation(BindView.class);
        mResId = bindView.value();
        if (mResId < 0) {
            throw new IllegalArgumentException(
                    String.format("value() in %s for field %s is not valid !", BindView.class.getSimpleName(),
                            mVariableElement.getSimpleName()));
        }
    }

    /**
     * 获取变量名称
     *
     * @return
     */
    Name getFieldName() {
        return mVariableElement.getSimpleName();
    }

    /**
     * 获取变量id
     *
     * @return
     */
    int getResId() {
        return mResId;
    }

    /**
     * 获取变量类型
     *
     * @return
     */
    TypeMirror getFieldType() {
        return mVariableElement.asType();
    }
}

2、创建一个Android Library:annotationapi

public class ActivityViewFinder implements ViewFinder {
    @Override
    public View findView(Object object, int id) {
        return ((Activity) object).findViewById(id);
    }
}
public class ZFViewBinder {
    private static final ActivityViewFinder activityFinder = new ActivityViewFinder();//默认声明一个Activity查找器
    private static final Map<String, ViewBinder> binderMap = new LinkedHashMap<>();//

    /**
     * Activity的注解绑定
     *
     * @param activity
     */
    public static void bind(Activity activity) {
        bind(activity, activity, activityFinder);
    }

    /**
     * '注解绑定
     *
     * @param host   表示注解 View 变量所在的类,也就是注解类
     * @param object 表示查找 View 的地方,Activity & View 自身就可以查找,Fragment 需要在自己的 itemView 中查找
     * @param finder ui绑定提供者接口
     */
    public static void bind(Object host, Object object, ViewFinder finder) {
        try {
            ViewBinder binder = findViewBinderForClass(host.getClass());
            if (binder != null) {
                binder.bindView(host, object, finder);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static ViewBinder<Object> findViewBinderForClass(Class<?> cls)
            throws IllegalAccessException, InstantiationException {
        if (cls == null) return null;
        String clsName = cls.getName();
        ViewBinder<Object> viewBinder = binderMap.get(clsName);
        if (viewBinder != null) {
            return viewBinder;
        }
        try {
            Class<?> viewBindingClass = Class.forName(clsName + "$$ViewBinder");
            viewBinder = (ViewBinder<Object>) viewBindingClass.newInstance();
        } catch (ClassNotFoundException e) {
            viewBinder = findViewBinderForClass(cls.getSuperclass());
        }
        binderMap.put(clsName, viewBinder);
        return viewBinder;
    }

    public static void unBind(Object host) {
        String className = host.getClass().getName();
        ViewBinder binder = binderMap.get(className);
        if (binder != null) {
            binder.unBindView(host);
        }
        binderMap.remove(className);
    }
}
public interface ViewFinder {
    View findView(Object object, int id);
}
public interface ViewBinder<T> {
    void bindView(T host, Object object, ViewFinder finder);

    void unBindView(T host);
}

3、在主项目的Activity中使用

gradle中引用:

implementation project(':annotationapi')
implementation project(':annotationprocessor')
annotationProcessor project(':annotationprocessor')

Activity中使用:

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.tv)
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ZFViewBinder.bind(this);
        if (tv!=null){
            tv.setText("绑定成功");
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ZFViewBinder.unBind(this);
    }
}

github代码链接

Android 自定义注解详解 我们在开发中经常会需要计算一个方法所要执行的时间,以此来直观的比较哪个实现方式最优,常用方法是开始结束时间相减但是当方法多的时候,是不是减来减去都要减的怀疑人生啦,哈哈,那么下面我就来写一个运行时注解来打印方法执行的时间。 阅读详情

相关推荐

Android开发中,自定义注解的两种应用方式

1.基于反射使用注解首先我们的目标是制作一个用户信息表,是一个User对象,表中有其对应的属性,将注解和属性等关联,然后再通过反射拿到对应的注解值和属性值打印,思路如下:1.1. 首先定义两个注解UserAnnotation和UserAttribute,创建一个注解遵循: public @interface 注解名 {方法参数}1.2. 将注解和User对象关联给用户赋值。1.3. 获取注解,打印注解的注解值和属性值。定义注解代码如下将注解和User对象绑定主要通过获取注解打印。

m0_70749039的博客 741

Java注解编译期处理AbstractProcessor详解

我们接触的注解主要分为以下两类运行时注解:通过反射在运行时动态处理注解的逻辑编译时注解:通过注解处理器在编译期动态处理相关逻辑平时我们接触的框架大部分都是运行时注解,比如:@Autowire @Resoure @Bean 等等。那么我们平时有接触过哪些编译期注解呢,@Lombok等等像这些编译时注解的作用都是自动生成代码,一是为了提高编码的效率,二是避免在运行期大量使用反射,通过在编译期利用反射生成辅助类和方法以供运行时使用。那这些编译器注解都是如何运行的呢?又是怎么自动生成代码的呢?

LoveSummer 3万+

Ubuntu 24.04 极速部署 Dify:从零到一的保姆级实践

本文详细介绍了在Ubuntu 24.04系统上快速部署Dify的完整教程,从环境准备到部署验证,再到深度优化和安全加固,提供了一套保姆级的实践指南。通过使用Docker和Docker Compose,结合国内镜像源加速,帮助用户高效完成Dify的安装和配置,确保系统稳定运行。

weixin_42662293的博客 244

Java进阶--编译时注解处理器(APT)详解

上篇文章我们使用注解+反射实现了一个仿ButterKnife功能的示例。考虑到反射是在运行时完成的,多少会影响程序性能。因此,ButterKnife本身并非基于注解+反射来实现的,而是用Annotation Processor在编译时处理注解的。Annotation Processor是javac的一个工具,它用来在编译时扫描和处理注解。 一、了解Annotation Processor 自定......

smily的博客 3万+

海康SDK实战:如何从PS流中提取H264裸流并实现前端播放(附完整代码)

本文详细介绍了如何利用海康SDK从PS流中提取H264裸流,并构建无插件前端播放的完整链路。通过解析PS流结构、提取H264 ES流,结合WebSocket实时传输与WFS.js前端播放器,实现了低延迟、高可控性的视频流处理方案,为开发者提供了从数据源到前端渲染的实战代码与清晰指南。

weixin_29051811的博客 476

Zabbix Trigger actions访问出现500

本篇博客详细介绍了Zabbix访问'Trigger actions'出现的错误状况,访问界面出现500,并提供了解决方案。

格子的博客 562

AutoService注解无法生成META-INF文件

背景 在写注解处理器时,首先就是要继承AbstractProcessor,并且按照如下步骤声明: 需要在 processors 库的 main 目录下新建 resources 资源文件夹; 在 resources文件夹下建立 META-INF/services 目录文件夹; 在 META-INF/services 目录文件夹下创建 javax.annotation.p...

王温暖的博客 9047

免Root抓包神器Packet Capture:安卓App数据捕获全攻略

本文详细介绍了免Root安卓抓包神器Packet Capture的安装与使用全攻略。通过利用安卓系统的VPN Service API,该工具无需Root权限即可捕获并解密App的HTTP/HTTPS网络流量。文章从核心原理、证书安装、实战抓包步骤到结果分析与常见问题排查,提供了完整指南,是移动端开发、安全测试和数据分析的实用利器。

weixin_29232507的博客 1073

安卓注解处理器-processor

安卓注解处理器-processor 最近在学习安卓开源框架发现,很多的开源框架都使用到了注解处理器,例如EventBus3.0。本文通过一个简单的Demo来介绍如何使用注解处理器。如果喜欢的话,欢迎大家给star。 Demo需求描述 用户通过执行一个传入参数为A(类对象)的静态方法,该方法会最终把参数A中加了特定注解的所有方法执行一遍。 需求实现 整个项目分为四个部分: 注解–要使用的注解类型...

小菜鸟的专栏 1830

pynq-z2 HLS开发

定制Overlay一.用HLS生成一个定制IP功啊啊是合理的创建标题,有助于目录的生成如何改变是式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 一.用HLS生成一个定制IP ...

qq_38769280的博客 4156

新版ThingsBoard 4.2.0在windows上用IDEA编译记录

编译thingsboard4.2.0在windows上使用Idea

hssjf的专栏 1482

解释器android,编写android 注解解释器

@AutoService(Processor.class)public class RouterProcessor extends AbstractProcessor {private Filer mFiler;private Messager mMessager;private Map mStaticRouterMap = new HashMap<>();private List m...

weixin_26949673的博客 244

AI绘画与医疗可视化:用AI生成医学插图的实践

医疗可视化是医学教育、临床沟通和学术研究的核心工具。传统医学插图依赖手工绘制或专业3D建模,存在制作周期长、成本高、更新迭代慢等问题。随着深度学习技术的发展,AI绘画工具(如Stable Diffusion、DALL-E)展现出强大的图像生成能力,但医疗领域对插图的解剖准确性、标注规范性和语义完整性有极高要求,通用AI模型难以直接应用。本文聚焦AI技术与医疗领域的交叉创新,系统阐述如何通过数据预处理、模型定制化训练和后处理校验,构建符合医疗标准的插图生成系统。

大模型应用工坊 1788

Android 自定义注解

1.自定义注解 注解是一种元数据,,可以添加到java代码中。类、方法、变量、参数、包都可以被注解,注解对注解的代码没有直接影响,之所以产生作用,是对其解析后做了相应的处理。。 注解其实就是一个标注而已,后期通过这个标注拿到对应的函数、变量,然后做一些操作(通常是反射)。所以,注解并没有什么魔法, 之所以产生作用,是对其解析后做了相应的处理. 注解仅仅只是个标记罢了。 定义一个注解需要使用关键字:@interface 定义一个注解,名字是MyAnnotation,其中 @Documented、.

zenmela2011的专栏 1722

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Android进阶之自定义注解

Android进阶之自定义注解 本篇文章内容包括: 注解的概念 元注解 自定义注解 Android自定义编译时注解 如果使用过ButterKnife, EventBus, Retrofit, Dagger等框架, 你对注解一定不会陌生. 但是注解背后究竟有什么魔法, 可以做这么不可思议的事情. 什么是注解 先来看看Java文档中的定义 An annotation is a form of m...

weixin_43901866的博客 208

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

安卓自定义注解支持和示例实现

开头 编码时使用注解,可以提高编码效率、简化代码增强可读性等优点;使用注解还是代码静态扫描的一部分,促进代码规范。安卓注解使用介绍一文中介绍了JDK/SDK提供的注解和support/ButterKnife等第三方提供的注解库,还有其他的一些库,这些基本已经能够满足需求。 support/ButterKnife是应用很广的注解库,它们也是属于“自定义注解”的范畴,只是有因为使用的多了,实际上成为...

weixin_34090562的博客 321

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Android 注解原理学习及自定义注解

Android 注解原理学习及自定义注解

weixin_43863193的博客 1729

android 自定义注解

在开发中注解是随处可见的,比如我们常见的@Override,@Deprecated和@SuppressWarnings。注解有很多的好处,我们也可以自定义注解。

数据结构和算法 333

Android 使用自定义注解标注当前类

举个例子1. 自定义注解`FragmentSetting`2. 抽象基类`BaseFragment`3. 具体的`Fragment`类4. 测试类自定义注解是如何定义的定义语法基础在Java中,自定义注解是通过@interface关键字来定义的。例如,就定义了一个名为的注解。注解本质上是一种特殊的接口,它的成员变量(也称为元素)默认是publicstatic和final的。元注解的使用@Target这个元注解用于指定自定义注解可以应用的目标元素类型。例如。

嗯... 934

Android进阶之注解解析和自定义注解

一:元注解 元注解的作用就是负责注解其他注解 1.@Target 说明: 用来指明注解所修饰的目标,包括packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数) 取值: 值 作用 英文描述 CONSTRUCTOR 用于描...

风花散却隽世间,雪月寥落化雨田 1063

Android自定义注解不被混淆,自定义Android注解Part3:绑定

上一节我们已经将自动生成注解代码部分介绍完毕,今天这篇文章是自定义Android注解系列的最后一篇文章。希望大家这一路走来有所收获。经过前面的了解,我们三大部分:butterknife-annotations、butterknife-compiler与butterknife-bind。现在就剩下最后一部分butterknife-bind。该部分是对我们前面定义的注解变量与自动生成的代码进行绑定,即...

weixin_39732316的博客 455

Android自定义注解的运用

网上很多讲自定义注解的,而且都讲得挺好,原理这些我就略过了,简单讲一下它的运用。经典的场景就是baseActivity里解析子activity的注解标签来做相应操作。比如我们定义如下注解: import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;

xiaoerye的专栏 392
上一篇: Window flag
下一篇: Android开发中的一些问题
azhoufeng
博客等级 码龄10年 10粉丝 38原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值