• 注解与反射学习


    注解

    元注解有这些:

    • @Retention 注解是用于指定被修饰的注解可以保留多长时间,即制定JVM策略在哪个时间点上删除当前注解。保留策略值有以下三个
    策略值功能描述
    SOURCE注解只在源文件中保留,在编译期间删除。源码级别用于APT技术
    CLASS注解只在编译期间存在于.calss文件中,运行时JVM不可获取注解信息。该策略值也是默认值。注意,在android中,编译后的dex文件获取不到注解信息。字节码级别用于字节码增加技术
    RUNTIME运行时JVM可以获取注解信息,是最长注解持续期。运行时级别用于反射技术
    • @Target注解用来限制注解的使用范围,即指定被修饰的注解能用于哪些程序单元
    枚举值功能描述
    Type可以修饰类,接口,注解或枚举类型
    FIELD可以修饰属性(成员变量),包括枚举常量
    METHOD可以修饰方法
    PAPAMETER可以修饰参数
    CONSTRUCTOR可以修饰构造方法
    LOCAL_VARIABLE可以修饰局部变量
    ANNOTATION_TYPE可以修饰注解类
    PACKAGE可以修饰包
    • @IntDef注解,用于语法检查,编写注解时建议经常使用此注解来进行语法检查

    以下三种我们不经常用到,知道大概使用场景即可

    • @Document注解用于将注解包含在javadoc中

    • @Inherited注解用于指明父类注解会被子类继承得到

    • @Repeatable注解用于声明标记的注解为可重复类型注解,可以在同一个地方多次使用

    声明注解主要有用到两个元注解,java跟kotlin声明注解的方式有区别。下边看下示例:

    Kotlin版本,注意需要在class前边添加annotation,注解默认值需要使用kotlin版本下的类型(AnnotationTarget.TYPE,AnnotationRetention.RUNTIME)

    @Target(AnnotationTarget.TYPE)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class MyClass(val value: String)
    //使用
    //使用
    @Myclass("test")
    Class Test{
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Java版本,注意需要使用@interface,注解默认值需要使用java版本下的类型(ElementType.TYPE,RetentionPolicy.RUNTIME)

    @Target({ ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Myclass {
        String value();
    }
    //使用
    @Myclass("test")
    Class Test{
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    创建注解处理器

    1. 创建一个java项目,在java项目中编写我们的注解处理器
    2. 创建的class类继承系统的AbstractProcessor
    3. 根目录创建resources资源文件夹
    4. resources目录下创建META-INF文件夹
    5. META-INF目录创建services文件夹
    6. services创建javax.annotation.processing.Processor文件,注意此文件没有后缀名
    7. 在javax.annotation.processing.Processor文件中声明我们的注解处理,

    反射

    反射就是可以使用JDK提供的反射API进行反射调用。可以在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意方法和属性,并且能改变它的属性。是java被视为动态语言的关键

    java反射机制主要提供了一下功能

    1. 在运行时构造任意一个类的对象
    2. 在运行时获取或者修改任意一个类所具有的成员变量和方法
    3. 在运行时调用任意一个对象的方法(属性)

    在这里插入图片描述
    根据方法名来进行反射操作

    kotlin版本的替代findview

    注解

    /**
     * 注解代替findViewById
     */
    @Target(AnnotationTarget.FIELD)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class InjectView(@IdRes val value: Int)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注解处理

    class InjectUtils {
        companion object {
    
            fun injectView(activity: Activity) {
                val javaClass = activity.javaClass
                val declaredFields = javaClass.declaredFields
    
                for (field in declaredFields) {
                    //判断属性是否被InjectView注解声明
                    if (field.isAnnotationPresent(InjectView::class.java)) {
                        val injectView = field.getAnnotation(InjectView::class.java)
                        //获得了注解中设置的id
                        val id = injectView.value
                        val view: View = activity.findViewById(id)
                        //反射设置属性的至
                        field.isAccessible = true//设置访问权限,允许操作private的属性
                        try {
                            //反射赋值
                            field.set(activity, view)
                        } catch (e: IllegalAccessException) {
                            e.printStackTrace()
                        }
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    使用

    class MainActivity : AppCompatActivity() {
        @InjectView(R.id.tv)
        val tv: TextView? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            InjectUtils.injectView(this)
            tv?.text = "llllllllll"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    kotlin版本的替代Onclick

    注解

    //定义注解区分长按还是点击
    @Target(AnnotationTarget.CLASS)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class EventType(val listenerSetter: String, val listenerType: KClass<*>)
    /**
     * 注解代替setOnClickListener
     */
    @Target(AnnotationTarget.FUNCTION)
    @Retention(AnnotationRetention.RUNTIME)
    @EventType(listenerType = View.OnClickListener::class, listenerSetter = "setOnClickListener")
    annotation class InjectOnClick(@IdRes vararg val value: Int)
    
    /**
     * 注解代替setOnLongClickListener
     */
    @Target(AnnotationTarget.FUNCTION)
    @Retention(AnnotationRetention.RUNTIME)
    @EventType(listenerType = View.OnLongClickListener::class, listenerSetter = "setOnLongClickListener")
    annotation class InjectOnLongClick(@IdRes vararg val value: Int)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注解处理器

      fun injectOnclick(activity: Activity) {
                //遍历activity中的全部方法
                activity.javaClass.declaredMethods.forEach { method ->
                    //遍历方法的所有注解,filter集合筛选函数,返回一个新的集合
                    method.annotations.filter { annotation ->
                        //筛选注解中含有EventType注解
                        annotation.annotationClass.java.isAnnotationPresent(EventType::class.java)
                    }.forEach { annotation ->//遍历注解中含有EventType注解
                        //获得EventType注解,从java获取的数据不确定不为空,所以单独提取出来好控制
                        val eventTypeAnnotation = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                            //版本控制
                            annotation.annotationClass.java.getDeclaredAnnotation(EventType::class.java)
                        } else {
                            null
                        }
                        if (eventTypeAnnotation != null) {
                            //EventType注解中listenerSetter listenerType值setOnclickListener
                            val listenerSetter = eventTypeAnnotation.listenerSetter
                            //加入listenerType.java是吧KClass转换成Class。
                            // newProxyInstance(classLoader,Interface Array,InvokeHandler)
                            Java 中第二参数是Class[] 所以必须装换
                            val listenerType = eventTypeAnnotation.listenerType.java
                            //添加访问权限,如果是私有方法,必须设置为true
                            method.isAccessible = true
                            val newProxyInstance =
                                Proxy.newProxyInstance(
                                    listenerType.classLoader, arrayOf(listenerType)
                                ) { proxy, _, args ->
                                    //运行Activity中加OnClick或者OnLongClick注解的方法
                                    // 此处判断长按监听事件,长按事件需要返回boolean值
                                    if (listenerSetter == "setOnLongClickListener") {
                                        method!!.invoke(activity, *(args ?: emptyArray()))
                                        true
                                    } else {
                                        method!!.invoke(activity, *(args ?: emptyArray()))
                                    }
                                }
                            //获取所有activity中加OnClick或者OnLongClick注解的value方法
                            val valueMethod = annotation.annotationClass.java.getDeclaredMethod("value")
                            //获取所有OnClick或者OnLongClick注解的value值
                            val viewIds = valueMethod.invoke(annotation) as IntArray
                            viewIds.forEach { viewId ->
                                val findViewById = activity.findViewById(viewId)
                                //获取View中setOnClickListener方法
                                val setOnListener = findViewById.javaClass.getMethod(listenerSetter, listenerType)
                                //运行View中setOnClickListener方法
                                setOnListener.invoke(findViewById, newProxyInstance)
                            }
                        }
                    }
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    使用

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            InjectUtils.injectOnclick(this)
        }
    
        @InjectOnClick(R.id.tv, R.id.tv1)
        fun onClick(view: View) {
            when (view.id) {
                R.id.tv -> {
                    val intent = Intent(this, MainActivity2::class.java)
                    intent.putExtra("ceshi", "22222222")
                    startActivity(intent)
                }
                R.id.tv1 -> Log.d("MainActivity", "tv1")
                else -> {
                }
            }
        }
    
        @InjectOnLongClick(R.id.tv, R.id.tv1)
        fun onLongClick(view: View) {
            when (view.id) {
                R.id.tv -> Log.d("MainActivity", "长按tv")
                R.id.tv1 -> Log.d("MainActivity", "长按tv1")
                else -> {
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    kotlin版本的替代自动传参

    注解

    /**
     * 注解代替跳转页面传参
     */
    @Target(AnnotationTarget.FIELD)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class InjectAutoWired(val value: String = "")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注解处理器

    fun injectAutoWried(activity: Activity) {
        val javaClass = activity.javaClass
        val intent = activity.intent
        val extras = intent.extras ?: return
        //获取文件中所有的属性
        val declaredFields = javaClass.declaredFields
        for (method in declaredFields) {
            if (method.isAnnotationPresent(InjectAutoWired::class.java)) {
                val autoWired = method.getAnnotation(InjectAutoWired::class.java)
                val key = if (autoWired.value.isEmpty()) {
                    method.name
                } else {
                    autoWired.value
                }
                if (extras.containsKey(key)) {
                    var obj = extras.get(key)
                    val componentType = method.type.componentType
                    if (method.type.isArray && Parcelable::class.java.isAssignableFrom(componentType)) {
                        var objs: Array = obj as Array
                        var objects = Arrays.copyOf(objs, objs.size, method.type as Class>?)
                        obj = objects
                    }
                    method.isAccessible = true
                    try {
                        //反射赋值
                        method.set(activity, obj)
                    } catch (e: IllegalAccessException) {
                        e.printStackTrace()
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    使用

    class MainActivity2 : AppCompatActivity() {
        @InjectView(R.id.tv)
        val tv: TextView? = null
    
        @InjectAutoWired("ceshi")
        val ceshi: String? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            InjectUtils.injectView(this)
            InjectUtils.injectOnclick(this)
            InjectUtils.injectAutoWried(this)
            tv?.text = ceshi
        }
    
        @InjectOnClick(R.id.tv, R.id.tv1)
        fun onClick(view: View) {
            when (view.id) {
                R.id.tv -> {
                    val intent = Intent(this, MainActivity2::class.java)
                    intent.putExtra("ceshi", "22222222")
                    startActivity(intent)
                }
                R.id.tv1 -> Log.d("MainActivity", "tv1")
                else -> {
                }
            }
        }
    
        @InjectOnLongClick(R.id.tv, R.id.tv1)
        fun onLongClick(view: View) {
            when (view.id) {
                R.id.tv -> Log.d("MainActivity", "长按tv")
                R.id.tv1 -> Log.d("MainActivity", "长按tv1")
                else -> {
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    仅用于学习注解,反射相关知识

  • 相关阅读:
    PyTorch - 高效快速配置 Conda + PyTorch 环境 (解决 segment fault )
    ceph块存储在线扩容
    PPT系统化学习 - 第1天
    私募基金CRM客户关系管理系统软件开发应该具备的功能清单
    应用软件漏洞排名
    Placement Rules 使用文档
    eclipse启动一个Springboot项目
    【Spring面试】八、事务相关
    Spring Cloud Alibaba Sentinel流量防卫兵
    HTTP协议格式、URL格式及URL encode
  • 原文地址:https://blog.csdn.net/qq_31309689/article/details/126279375