IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 注解与反射学习,文末附kotlin版本的注解反射案例替代(findViewById、OnclickListener、intent自动传参) -> 正文阅读

[移动开发]注解与反射学习,文末附kotlin版本的注解反射案例替代(findViewById、OnclickListener、intent自动传参)

注解

元注解有这些:

  • @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{

}

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

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Myclass {
    String value();
}
//使用
@Myclass("test")
Class Test{

}

创建注解处理器

  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)

注解处理

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()
                    }
                }
            }
        }
    }
}

使用

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"
    }
}

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)

注解处理器

  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<View>(viewId)
                            //获取View中setOnClickListener方法
                            val setOnListener = findViewById.javaClass.getMethod(listenerSetter, listenerType)
                            //运行View中setOnClickListener方法
                            setOnListener.invoke(findViewById, newProxyInstance)
                        }
                    }
                }
            }
        }

使用

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 -> {
            }
        }
    }
}

kotlin版本的替代自动传参

注解

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

注解处理器

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<Any> = obj as Array<Any>
                    var objects = Arrays.copyOf(objs, objs.size, method.type as Class<out Array<Any>>?)
                    obj = objects
                }
                method.isAccessible = true
                try {
                    //反射赋值
                    method.set(activity, obj)
                } catch (e: IllegalAccessException) {
                    e.printStackTrace()
                }
            }
        }
    }
}

使用

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 -> {
            }
        }
    }
}

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

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 19:17:25  更:2022-08-19 19:18:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 4:24:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码