项目架构之----注解反射

最近一段时间在重新整理项目架构,在整理过程中把一些有用的东西分享一下。那么注解和反射就手写butterknife来解释一下吧,不为别的,就为了方便你复制粘贴。

1.注解的原理:我们可以理解为注解就是通过反射机制,调用注解标记的方法,实现和直接用对象调用方法一样的操作。
2.注解使用 @interface (kotlin 使用 annotation)标记类,用@Target来描述它的作用范围,用@Retention描述它的生命周期。

Target作用范围
@Target(AnnotationTarget.CLASS)
@Target(AnnotationTarget.FIELD)
@Target(AnnotationTarget.FUNCTION)
Retention生命周期
@Retention(AnnotationRetention.RUNTIME)

简单的来说:
Target 中 CLASS——描述类,FIELD——描述变量,METHOD——方法(kt——FUNCTION)
Retention运行期生效–RUNTIME
别的先不用记住了

3.元注解

@Retention(标明注解被保留的阶段)
@Target(标明注解使用的范围)
@Inherited(标明注解可继承)
@Documented(标明是否生成javadoc文档)

4.本次内容主要实现页面的绑定,view绑定和点击事件。那么分别写一下这三种类型的注解。demo因为是kt的,所以注解也使用了kt编写,请大家不要介意~~

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class SetContentView(val layout: Int = -1)
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class BindView(val id: Int = -1)
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class ClickListener(vararg val ids: Int)

注解写完成之后呢,就是我们实现的关键了,通过反射去调用被注解标记过的类、成员变量、方法。实现和对象调用一样的目的。

5.反射字段和方法
getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
getMethods()和getDeclaredMethods()表示获取某个类的方法。

6.findViewById 请看清楚环境,是Activity还是View
isAnnotationPresent方法可以判断该元素是否拥有指定注解类的注解,
getAnnotation(Class var1) 则返回指定注解类的注解

object FindViewHelper {

    fun bind(activity: Activity) {
        setContentView(activity)
        bindView(activity)
        setOnClickListener(activity)
    }

    private fun setOnClickListener(activity: Activity) {
        val clazz = activity.javaClass
        val declaredMethods = clazz.declaredMethods
        for (method in declaredMethods) {
            if (method.isAnnotationPresent(ClickListener::class.java)) {
                val annotation = method.getAnnotation(ClickListener::class.java) as ClickListener
                val ids = annotation.ids
                for (id in ids) {
                    try {
                        if (id == 0) throw IllegalArgumentException("you forget to add id to ClickListener method in ${clazz.name}")
                        val findBy = clazz.getMethod("findViewById", Int::class.java)
                        findBy.isAccessible = true
                        val view = findBy.invoke(activity, id)

                        val click = view.javaClass.getMethod(
                            "setOnClickListener",
                            View.OnClickListener::class.java
                        )
                        click.isAccessible = true
                        click.invoke(view, View.OnClickListener { v ->
                            try {
                                method.isAccessible = true
                                method.invoke(activity, v)
                            } catch (e: java.lang.Exception) {
                                e.printStackTrace()
                            }
                        })
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                }
            }
        }
    }

    private fun bindView(activity: Activity) {
        val clazz = activity.javaClass
        val fields = clazz.fields
        for (f in fields) {
            if (f.isAnnotationPresent(BindView::class.java)) {
                val bindView = f.getAnnotation(BindView::class.java) as BindView
                val viewId = bindView.id
                if (viewId == -1) throw IllegalArgumentException("you forget to add id to BindView in ${clazz.name}")
                try {
                    val method = clazz.getMethod("findViewById", Int::class.java) as Method
                    method.isAccessible = true
                    val view = method.invoke(activity, viewId)
                    f.isAccessible = true
                    f.set(activity, view)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }
    }

    private fun setContentView(activity: Activity) {
        val clazz = activity.javaClass
        if (clazz.isAnnotationPresent(SetContentView::class.java)) {
            val contentView = clazz.getAnnotation(SetContentView::class.java) as SetContentView
            val layoutId = contentView.layout
            if (layoutId == -1) throw IllegalArgumentException("you forget to add id to "
            +"SetContentView method in ${clazz.name}")
            try {
                val method = clazz.getMethod("setContentView", Int::class.java) as Method
                method.isAccessible = true
                method.invoke(activity, layoutId)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
}

之后我们在使用的过程中只需要在类中加入这些注解就可以轻松的实现绑定布局和view的功能。今天比较忙哈,随便写写,下次再修改文章逻辑


@SetContentView(R.layout.activity_second)
class SecondActivity : AppCompatActivity() {

    @BindView(R.id.tv2)
    lateinit var tv2: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
//        AnnoUtilsJava.bind(this)
        FindViewHelper.bind(this)
        tv2.text = "asdasdasdasdasdasdasdasd"
    }

    @ClickListener(R.id.tv2)
    fun onClick(view: View) {
        Toast.makeText(this, "干啥", Toast.LENGTH_SHORT).show()
    }
}

你可能感兴趣的:(注解反射,android,android,kotlin)