在Java中经常会用到注解,通过注解的方式可以实现很多灵活性的东西。很多优秀的框架都支持注解的方式,如Spring的中对Bean的注解,Hibernate中对POJO类的注解,Mybatis中对Mapper的注解,ButterKnife中对View的注解,Dagger中对各个Component的注解, Retrofit对Api的注解。一言以蔽之,使用注解可以让整个代码风格看起来清爽明了。
传统的代码风格
activity_main.xml
1 | <?xml version="1.0" encoding="utf-8"?> |
很简单的一个界面,只有一个EditText和一个Button,就不过多解释了。
MainActivity.java
1 | package com.puke.annotationdemo; |
我们这里要做到是用户点击提交的时候,Toast弹出EditText输入的内容,比较简单,略过~
传统的风格是这样的,这样看来也许觉得没什么问题,但是实际的开发当中我们一个页面当中包含的View,以及对应的View的一些事件回调要远远比这个繁琐。我相信一个Activity中要处理十几二十几个View也不算是什么稀奇的事情,那这样会造成什么结果呢。。。
你的属性声明会是这样的
1 | private Button mButton1; |
你的findView会是这样的
1 | mButton1 = (Button) findViewById(R.id.button1); |
你的事件监听会是这样的
1 | mButton1.setOnClickListener(new View.OnClickListener() { |
例子举得不怎么恰当,但是足以说明随着业务代码的不断扩大,这些看上去的”无脑操作“也会让我们广大coder变得愈加的不耐烦,而且会使得我们的类变得庞大而臃肿。
那么,接下来我们就通过注解的方式来搞一发~
注解的编码风格
我们打算使用注解的方式实现
- xml的配置
- View的注入
- 点击事件的绑定
接下来就是具体实现逻辑
- 首先我们先定义一个Bind注解
1 | package com.puke.annotationdemo; |
- 然后写Bind注解对应的注解处理器
1 | package com.puke.annotationdemo; |
好了,到了这一步,我们的注解工作算是结束了,代码相对有点多,但这个是一劳永逸的。
- 接下来就是对注解的使用了
1 | package com.puke.annotationdemo; |
Run一下,完美运行~
我们这里可以看到,使用注解之后
setContentView方法没了
findViewById方法没了
setOnClickListener方法没了
MainActivity整个类减肥了
所有的所有,都让注解处理器一手承包了。而我们要做的是什么,要做的是真正应该由coder做的事情,在对应的地方加上对应的注解配置就ok了。
然后我们可以回过头看一下Bind这个注解,细心的同学可能发现注解声明value()的时候理论上来讲不应该有一个default为0的默认值。原因很简单啊,因为就目前的使用场景来看,无论注入一个layout还是一个id都不会为0,那这里干嘛还要再写一个default 0呢,直接不要default可以限制业务方的使用,强约束业务方一旦使用注解就必须要在注解里面set一个值进来。这里我要说明一下,我们写注解就是为了方便使用,快速开发,既然要懒,我们就懒到家,干脆就让我们的注解处理器能在业务方没有在Bind中注入值的时候也能生效。
就是要实现下面这种效果:
1 | package com.puke.annotationdemo; |
这样一来,只需要几个全裸的注解一顿狂注之后,就完事了。后面这种的实现方式我这里就不写了,大致说一下思路,注解处理器要制定类名—layout,属性名—id,方法名—id,这样一套转换标准出来,然后注解处理器的处理逻辑是先看业务方有没有手动注入,没有手动注入的情况(也就是前面提到的default 0)下,注解处理器再按照这套标准利用反射来取出对应的R类的对应资源值,只要找到对应的资源值,就和手动注入处理结果的完全一样。
一些问题
每当一种事物出现时,只要不是太极端,总会有人拥护,也有人异议。单单站在coder的角度,这种注解的方式给我带来的好处是显而易见的,算是治愈代码密集恐惧症的偏方了。
但是值得深思的是,这里大量使用了反射,在Java中反射的性能问题总是尴尬的不要不要的。虽然jdk每次升级时基本上都在对反射进行优化,但是毕竟是反射,纯理论上讲,它确实没有直接的方法调用高效。
当然针对这个问题,我也有见过这样一种说法,假如我们对所谓”高效“的时间容忍度是1000t(t为一个时间粒度单位),直接方法调用耗时是1t,反射是50 - 200t。也就是说,反射是耗性能,是不效率,但是这个也只是相对与直接方法调用而言的,而还远远没达到我们对性能指标的容忍值。
我曾经也在高效开发和反射性能消耗之间纠结很久,在这里就不去过多评价,仁者见仁,智者见智了~