手动实现IOC框架,与findViewById说拜拜
转载请标明出处:
http://blog.csdn.net/lisdye2/article/details/64233563
本文出自:【Alex_MaHao的博客】
项目中的源码已经共享到github,有需要者请移步【Alex_MaHao的github】
自序
在开发Android
中,总要写许多的findViewById
方法,这无疑是一件非常痛苦的事情。直到接触了Xutils
框架,发现竟然可以使用注解的方式,优雅的干掉了findViewById
方法,当时真是惊为天人。后来遇到了Butterknife
之后,发现不仅能够通过实现findViewById
方法,甚至连setOnClickListener
,getString()
,getResource()
方法,都能通过一行注解的方式快速的实现。
当时在使用过程中,感觉这种方式大大的提高了开发的效率,以及编码的舒畅度,自己很有必要实现以下。于是便有了这篇博客,该篇博客主要实现了findViewById
方法,虽然广度不是很大,但他们的原理都是相同的。
如何使用
使用方式和Butterknife
相似,通过注解@BindView
标识控件,通过ViewFinder.inject(this)
实现代码的注入。
public class MainActivity extends AppCompatActivity {
// 通过注解绑定控件
@BindView(R.id.text)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//代码注入
ViewFinder.inject(this);
textView.setText("123");
}
}
实现原理
实现方式有两种
通过反射实现:如果对于反射有深入了解,则应该清楚,我们可以通过反射获取到该注解,并且获取到该注解的值等等一系列的必须量,通过反射我们实现对控件的注入。该方法虽然可以实现,但对效率有着一定的影响。毕竟反射很影响效率。
通过编译器生成代码实现:在我们运行java代码的时候,通常先通过javac
将java文件编译成.class
文件,然后运行class
文件,那么我们能不能够在编译时期,根据我们的注解生成findViewById
等方法,这样我们就能够在运行期查找控件。结果当然是可以的,本例就是使用这种方式。因为虽然其使用的是注解,但在运行期其实质仍是通过findViewById
方法查找控件,相比于反射来说,大大的提高了性能。
通过编译器生成代码的大致流程如下:
- 编写
Modul
:ioc-annotation
,该工程主要定义注解@BindView
用以修饰变量。 - 编写
Modul
:ioc-compiler
,该工程为最终会打成jar包,主要是在javac
编译时期根据注解生成注入代码的相关类 - 编写
Modul
:ioc-api
,该工程主要提供注入的调用方法ViewFidder.inject()
,调用代码注入的方法。 - 编写
Modul
:app
,测试工程。
代码实现
根据上面的流程,开始实现框架
编写ioc-annotation
模块
该模块比较简单,就是定义一个注解。如下:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
该注解主要有两个功能:
- 在
Activity
中修饰变量,用以标识需要findViewById
的相关控件。 - 在
ioc-compiler
模块中,用以检索和获取需要findViewById
的控件。
编写ioc-compiler
模块
在之前我们提到过,该框架的原理是在编译时期根据我们的要求生成注入的辅助代码,那么如何生成,以何种规则生成,肯定是由我们来定义的。
javac
命令中,可以在其编译的指定目录放入一个.jar
文件,当然这个.jar
文件有特殊的要求(后面再说),这样运行javac
命令之前,javac
会调用jar
,通过这种特性订制一些我们想实现的功能。
那么看一下该模块的关键类IocProcessor
@AutoService(Processor.class)
public class IocProcessor extends AbstractProcessor {