Android 注解和反射实践

Android开发秘籍:深度掌握Kotlin注解与反射实战精髓 本文深入解析Kotlin注解与反射两大元编程核心技术,构建包含16个模块的完整知识体系。主要内容包括:1)注解系统的声明规范、保留策略及工业级应用案例;2)Kotlin反射特性与Java反射的关键差异及实战操作;3)注解与反射协同实现依赖注入、权限代理等典型场景;4)跨平台序列化、热修复等工业解决方案;5)性能优化策略与安全实践。通过多个代码示例和面试题解析,帮助开发者掌握从基础到高阶的应用能力,提升Android开发中的元编程水平。 阅读详情
Android 注解和反射实践


上篇说了 Java 自定义注解,这里来看下自定义注解和反射结合的实践例子

这里我们需要做到两点:

  1. A页面intent传递数据并跳转到B 页面,通过注解和反射实现数据的自动赋值
  2. 通过注解和反射实现Activity页面的findViewById()功能和onClick()功能
数据自动赋值

首先来看下我们需要达到的效果:

public class SecondActivity extends AppCompatActivity {

    @GetParam(value = "name")
    private String name;
    @GetParam(value = "age")
    private int age;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        GetParamUtils.get(this);

//        name = getIntent().getStringExtra("name");
//        age = getIntent().getIntExtra("age", 0);
        
        // 能正常打印出数据
        Log.d("yao", "name=" + name + ", age=" + age);
    }
}

从上面的结果可以看出实现这个效果需要有一个自定义注解@GetParam,还需要有一个GetParamUtils来解析注解的信息。

  1. 首先来看下@GetParam类中的实现:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GetParam {
    String value();
}

从上面可以看出注解GetParam是作用在变量上的,并且保留界别是运行时。

  1. 在SecondActivity中,需要自动赋值的变量上面加上自定义的注解@GetParam,并指定value的值,这个值就是intent传递数据里面的key。

  2. 解析成员变量上的注解,完成自动赋值

public class GetParamUtils {

    public static void get(Activity activity) {
        Intent intent = activity.getIntent();
        Bundle extras = intent.getExtras();
        if (extras == null) return;

        Class<? extends Activity> aClass = activity.getClass();
        // 获取所有的成员变量
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            // 判断是否带有指定的注解
            if (field.isAnnotationPresent(GetParam.class)) {
                GetParam annotation = field.getAnnotation(GetParam.class);
                // 获取key
                String key = annotation.value();
                if (TextUtils.isEmpty(key)) {
                    key = field.getName();
                }

                if (extras.containsKey(key)) {
                    Object obj = extras.get(key);

                    //  Parcelable数组类型不能直接设置,其他的都可以.
                    // 获得数组单个元素类型
                    Class<?> componentType = field.getType().getComponentType();
                    //当前属性是数组并且是 Parcelable(子类)数组
                    if (field.getType().isArray() && Parcelable.class.isAssignableFrom(componentType)) {
                        Object[] objects = (Object[]) obj;
                        //创建对应类型的数组并由objects拷贝
                        Object[] objects1 = Arrays.copyOf(objects, objects.length, (Class<? extends Object[]>) field.getType());
                        obj = objects1;
                    }
                    // 设置是否可以读写
                    field.setAccessible(true);
                    try {
                        field.set(activity, obj);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

  1. 在onCreate中直接绑定,然后打印日志看看结果。
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        GetParamUtils.get(this);

//        name = getIntent().getStringExtra("name");
//        age = getIntent().getIntExtra("age", 0);

        // 能正常打印出数据
        Log.d("yao", "name=" + name + ", age=" + age);
    }

>>>yao: name=yao, age=20

ok, 自动赋值就完成了,接下来看下控件的解析。

实现findViewById的控件注解
  1. 先写下自定义注解的类,由于findViewById最后需要赋值给变量,定义如下:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
    @IdRes int value();
}
  1. 在需要使用的变量上引用
@InjectView(R.id.btn_jump)
private Button btnJump;
  1. 使用反射解析控件
public class InjectUtils {

    public static void inject(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        // 获取当前类的所有变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 是否有injectView注解
            if (field.isAnnotationPresent(InjectView.class)) {
                InjectView annotation = field.getAnnotation(InjectView.class);
                int resId = annotation.value();

                try {
                    // 获取findViewById方法
                    Method findViewById = clazz.getMethod("findViewById", int.class);
                    // 执行findViewById方法
                    Object o = findViewById.invoke(activity, resId);
                    field.setAccessible(true);
                    // 赋值
                    field.set(activity, o);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. 在onCreate中使用
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    InjectUtils.inject(this);
 }

启动页面button正常展示。

实现onClick方法的绑定
  1. 自定义注解@OnClick
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventClickType(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener")
public @interface OnClick {

    @IdRes int[] value();
}

在这里有定义了一个注解@EventClickType来注解OnClick,具体实现如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventClickType {
    /**
     * 监听的class类型
     * @return
     */
    Class listenerType();

    /**
     * 设置监听的方法名
     * @return
     */
    String listenerSetter();
}
  1. 定义一个方法加上@OnClick注解
@OnClick({R.id.btn_jump})
public void onClick(View view) {
    switch (view.getId()) {
        case R.id.btn_jump:
            jump();
            break;
    }
}
  1. 解析注解,并且会用到动态代理
        // 设置点击监听
		// 获取当前类的所有变量
        Field[] fields = clazz.getDeclaredFields();
        // 获取所有的方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {

            // 获取方法上所有的注解
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                Class<? extends Annotation> annotationType = annotation.annotationType();
                // 是否有EventClickType 注解
                if (annotationType.isAnnotationPresent(EventClickType.class)) {
                    EventClickType eventClickType = annotationType.getAnnotation(EventClickType.class);
                    Class listenerType = eventClickType.listenerType();
                    String listenerSetter = eventClickType.listenerSetter();

                    // 获取onclick注解上的value方法
                    try {
                        Method valueMethod = annotationType.getDeclaredMethod("value");
                        int[] viewIds = (int[]) valueMethod.invoke(annotation);

                        method.setAccessible(true);
                        EventInvocationHandler<Activity> invocationHandler = new EventInvocationHandler<>(activity, method);
                        Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, invocationHandler);

                        // 遍历onclick注解上的value
                        for (int viewId : viewIds) {
                            View view = activity.findViewById(viewId);
                            // 获取setOnClickListener方法, listenerSetter是方法名, listenerType=View.OnclickListener
                            Method listenerMethod = view.getClass().getMethod(listenerSetter, listenerType);
                            // 执行方法
                            listenerMethod.invoke(view, proxy);

                        }

                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }

            }
        }

    /**
     * InvokationHandler是Java 反射包里面的一个接口。
     * InvokationHandler通过用户类来实现,来激发一个动态代理类的方法
     * @param <T>
     */
    static class EventInvocationHandler<T> implements InvocationHandler {

        private T target;
        private Method method;

        public EventInvocationHandler(T target, Method method) {
            this.target = target;
            this.method = method;
        }

        /**
         *
         * @param o 实现方法的代理对象
         * @param method 代理实例激发的方法,Porxy参数中的接口方法
         * @param objects 传递给方法的一系列参数
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            return this.method.invoke(target, objects);
        }
    }
  1. 在onCreate方法中绑定即可
总结

自定义注解一般伴随着反射使用,在日常开发中多用多熟练会有更好的理解。

RLHF的数学表示 RLHF(Reinforcement Learning with Human Feedback)是一种结合了强化学习和人类反馈的机器学习方法。它通常用于训练智能体(agent)在复杂环境中做出决策,同时利用人类的反馈来指导和改进学习过程。RLHF 没有一个统一的数学表示式,因为它依赖于具体的应用场景和实现细节。然而,我们可以从强化学习和人类反馈的角度来描述RLHF的一些关键数学概念。 阅读详情

相关推荐

实战分享:一文读懂RS-232总线

RS-232标准接口(又称EIARS-232)是常用的串行通信接口标准之一,它是由美国电子工业协会(Electronic IndustryAssociation,EIA)联合贝尔系统公司、调制解调器厂家及计算机终端生产厂家于1970年共同制定,其全名是“数据终端设备(DTE)和数据通信设备(DCE)之间串行二进制数据交换接口技术标准”。

阿荣嵌入式 8516

Java反射机制简介

反射操作类 反射示例 反射库提供了丰富的工具集,以便编写能动态操纵Java代码的程序(JavaBean中即大量应用)。通过反射,可以知道任意类的所有属性和方法,调用对象的属性和方法。反射机制可以: 在运行时分析类的能力; 在运行时查看对象; 实现通用的数组操作代码; 利用Method对象:调用函数; 反射操作类 java.lang.reflect包中封装了反射相关的操作类: ...

飞鹰技术 317

手把手教你使用Segformer训练自己的数据

SegFormer 基于Swin transformer 修改

weixin_38353277的博客 2万+

Android 反射○

1.JAVA反射机制 JAVA有一个非常突出的动态相关机制:反射(Reflection)。运用反射我们可以在运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载在运行时才得知名称的class,获悉其完整构造方法,并生成其对象实体、或对其属性设值、或唤起其成员方法。 要让Java程序能够运行,就得让Java类被Java虚拟机加载。Java类如果不被Java虚拟机加载就不能正常运行。正常情况下,我们运行的所有的程序在编译期时候就已经把那个类加载了。 Java的反射机制是在编译时

zenmela2011的专栏 7112

Mixer-First Receiver适用于宽带阵列应用的接收机技术1

Mixer-First Receiver适用于宽带阵列应用的接收机技术1技术的背景单元级数字化阵列传统链路到Mixer-First链路的逻辑考虑三种场景小结 技术的背景 在文章《吊打全球的顶级毫米波数字阵列项目-MIDAS》中提到了DARPA在MIDAS项目中在毫米波实现宽带单元级数字化阵列用到了一个关键的叫做N-Path filter的技术,而这个技术同样出现在了《让微波射频工程师颤抖的芯片-MATRICs》的MATRICs芯片中。 为什么DARPA的各个合作研究机构选择这种技术?这篇来自Universi

ICONO的博客 2167

[Linux项目实践] LCD Panel的硬件分析及调试

LCD硬件分析 项目使用3.5” TFT-LCD并且支持触摸控制。LCD的硬件可从几个方面学习:1)查看LCD的硬件连线。2)查看数据手册了解寄存器的控制。3)查看LCD DATASHEET了解硬件操作 1)LCD硬件连线 在图中有33个引脚,22根数据引脚和9根控制引脚。 LCD的工作原理:像素点是从左至右,从上至下逐个显示,LCD控制寄存器会从从内存中的framebuffer通

Terry_Yuan的技术专栏 3023

Java android 反射

反射工具类

huyangvista的博客 327

前端使用二开Thingsboard一、二、三事(四)-部件库

以项目实战为基础,逐步介绍前端在thingsboard系统二次开发中的一些注意事项以及一些thingsboard一些页面功能讲解,为前端人员开发使用thingsboard提供一种可靠思路,打造一个完美的物联网系统。

weixin_43163457的博客 739

Android 中的反射与注解

反射定义 Java反射机制是指在运行状态中 对于任意一个类,都能知道这个类的所有属性和方法; 对于任何一个对象,都能够调用它的任何一个方法和属性; 这样动态获取新的以及动态调用对象方法的功能就叫做反射。 主要的几个类 类用途 java.lang.Class编译后的class文件的对象 java.lang.reflect.Constructor构造方法 java.lang.reflec...

本然233的博客 619

yolov26改进 | 融合改进篇 | 基于 MobileNetV4 和双向特征金字塔网络BiFPN的融合改进(全网独家首发)

本文介绍BiFPN与MobileNetV4融合改进的YOLOv26。许多模块单独使用能够涨点,但直接叠加后可能因功能重复、通道不匹配、尺度冲突而掉点,因此融合前必须理解各模块作用。MobileNetV4利用UIB与Mobile MQA构建轻量主干,提升特征提取能力;BiFPN通过可学习权重完成双向多尺度融合,强化大、中、小目标的信息交互。两者优化Backbone与Neck,功能互补、结构冲突较少。本文提供代码、配置、消融对比及Codex修改思路,MobileNetV4也可替换其他主干,便于组合实验与二次创新

Snu77的博客 239

轨道平面系与轨道姿态系

文章摘要: 轨道平面系与轨道姿态系是航天任务中两个核心坐标系。轨道平面系(惯性系)以地心为原点,Z轴沿轨道角动量方向,用于轨道确定和机动规划;轨道姿态系(如LVLH系)以航天器质心为原点,Z轴指向地心,用于姿态控制和对地观测。两者通过旋转矩阵相互转换:轨道平面系基于轨道根数构建,保持静态;而LVLH系随轨道运动连续旋转。前者适用于全局轨道分析,后者支撑局部任务执行。理解二者的定义差异(原点、基准轴、惯性属性)及数学转换关系,对航天器姿态轨道控制系统的设计与操作至关重要。

ScilogyHunter的博客 88

Android通过注解来初始化控件

Android通过注解来初始化控件      对于Android大神的思想与能力我只能膜拜与学习了,这里就从大神哪里学来的通过注解来初始化控件简单的概述一下,省去了那么繁琐的findViewById步骤(找到了还要强转,真心醉了)。 原理    原理是在Activity加载好后通过找到Activity中使用注解的字段,再通过Java反射的方式,动态的给这个字段设置值。 一、定义注解类Vie

xyTianZhao 2782

DataX对接数据如何在ODS加密脱敏数据(插件开发)

datax对接mysql数据,对姓名只保留姓,名变成**。对这种简单的脱敏,可以不用修改datax源码,直接在配置文件上实现。

大模型大数据攻城狮的专栏 993

Android系统中的反射和注解机制

反射:提供无与伦比的运行时动态能力,是框架和高级特性的基石,但性能开销和安全隐患是其阿喀琉斯之踵。能不用则不用,用则需优化、缓存、谨防混淆和 API 限制。注解:尤其是结合编译时处理 (APT/KSP),已成为现代 Android 开发的支柱。它在保证性能、类型安全和开发效率的前提下,实现了代码生成、依赖管理、行为配置等复杂功能,是首选方案。运行时注解结合反射提供了灵活性,但需警惕性能代价。终极趋势:编译时代替运行时。

JMY_2016的博客 1095

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Android注解

资料整理: Android注解及反射实战--手写ButterKnife 使用整理:

a1003434346的博客 159

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

如何应对Android面试官->Java中的注解、反射、手写ButterKnife核心实现

Target(ElementType.TYPE) // 声明注解作用域@Retention(RetentionPolicy.CLASS) // 声明注解的生命周期 存在时间// 可以定义默认值@interface 关键字来声明一个注解;使用 @BindPath 作用在类上面的时候,需要指定 value 的值, key 的值可以不用指定,因为有默认值;// 当使用 value 关键字的时候,可以不用指定value作为 key 来赋值。

IT_Android的博客 1157

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

java 反射 泛型 构造函数,java-反射、注解、泛型

反射作用:能够做一般做不到的事情。使用场景:插件式换肤,插件式开发所有的反射功能都是基于字节码(class),一个类的class在内存宏应该只有一份,而且class其实也是一个对象。反射创建对象//反射创建对象try {//getDeclaredConstructor从所有构造方法里找,getConstructor从公共构造方法里找Constructor constructor = TestBea...

weixin_33504929的博客 249

手把手教你实现Android编译期注解

详细阐述了实现一个Android编译期注解sdk的步骤以及注意事项,并简要分析了运行时注解以及字节码技术在生成代码上与编译期注解的不同与优劣

vivo互联网技术 354

Android应用中使用运行时注解(1)

try {3. Activity中使用注解:site_namesite_logoviewPager@Override@Override})case R.id.break;一、布局文件的注解。

w5jaLw的博客 578

通过Java反射让Android优雅起来

Android也可以这样优雅 一、前言 本编我介绍如何利用Java反射原理去设计IOC注入,遵循享元模式设置IOC容器,并且如何利用Java反射的原理对MVP繁琐的回调方法进行优化。这是我写的一个多芬音乐的例子dfMusic。 希望大家参数这篇优秀文章 IOC注入. 二、IOC注入 要想让人容易理解,最好的办法是举个栗子,所有废话不多说展示代码。 public class FragmentMy extends BaseFragment { //下面的代码,与findViewById是等价的

weixin_44595763的博客 247

Java 注解及其在 Android 中的应用

Linux编程点击右侧关注,免费入门到精通!作者丨WngShhnghttps://juejin.im/post/5b824b8751882542f105447d一般的,注...

<sdffdsfsdfdfs>sfsfsfsdfsdffds</sdfsDS>Fsd 214

一篇搞定注解在Android的应用

Java注解(Annotation)是JDK5.0引入的元数据机制,用于将元数据与程序元素关联。注解分为标准注解(如@Override、@Deprecated)和元注解(如@Target、@Retention)。@Target指定注解应用范围,@Retention定义注解保留策略(SOURCE、CLASS、RUNTIME)。SOURCE级别注解仅用于源码检查,如@IntDef可用于限定参数取值,提供编译时语法检查而不增加运行时开销。注解通过@interface或annotation class声明,是Jav

ChenYiRan123456的博客 991

UltimateAndroidReference中的Kotlin反射与注解处理器

Kotlin反射(Reflection)与注解处理器(Annotation Processor)是Android开发中实现代码动态化和编译期代码生成的重要技术。在[UltimateAndroidReference](https://link.gitcode.com/i/6acff61db2c3bad384aff50295fb96f9)项目中,这两项技术被广泛应用于简化开发流程、提升代码质量。本文将...

gitblog_00408的博客 317

【Android开发必备技能】:Kotlin反射在实际项目中的9大应用

掌握Kotlin反射高效开发技巧,解决动态调用与属性操作难题。涵盖Android开发中9大典型场景,结合Kotlin反射示例讲解核心API与实用方法,提升代码灵活性与扩展性,值得收藏。

InstrGap的博客 1007

Kotlin注解实战精讲(从入门到高阶应用全解析)

掌握Kotlin注解用法,提升开发效率与代码可读性。本文详解注解在Android开发、依赖注入等场景的应用,涵盖声明、元注解、编译期处理等核心方法,支持AOP与编译优化。从基础到高阶实战全覆盖,值得收藏。

PixelGlow的博客 709

225.反射

由于反射操作在运行时可能会抛出各种异常(如 ClassNotFoundException、NoSuchMethodException、IllegalAccessException 等),必须做好充分的异常捕获和处理,确保在反射失败时能够有合理的降级或者错误日志输出。• Java 反射更成熟,相关文档与示例较多,在跨语言(Java & Kotlin 混合开发)场景下往往会同时使用两种 API,需要掌握二者的相互转换(例如,将 KClass 转为 Class 通过 .java 属性)。

2303_79132118的博客 799
上一篇: Glide缓存机制
下一篇: android面试题相关
coder_yao
博客等级 码龄10年 9粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值