经过对《Java Annotation简介》的简单介绍,咱们应该了解到了Java 中自定义注解的使用方式。实际上现有市场上已经出现了不少利用注解来完成依赖注入工做的库,例如ButterKnife。java
ButterKnife 简单来说就是经过注解的方式,简化代码中View变量与XML资源绑定的流程的工具。
前面提到过,元注解@Retention有三种取值:ide
ButterKnife使用的是RetentionPoicy.CLASS级别的注解,为了简单直观,咱们这里使用RUNTIME注解来模仿,固然由于须要经过反射得到元素对象,会损失运行时性能。工具
首先咱们来看一个简单的使用场景:性能
... @BindView(R.id.title_text) TextView mTitleTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); ButterKnife.inject(this); mTitleTextView.setText("test"); } ...
在onCreate方法中使用如下方法后,咱们已经得到了该控件的引用,能够实现控件方法的各类调用,前提只须要在声明控件变量时添加BindView注解,并设置对应的资源变量。this
ButterKnife.inject(this)
咱们模仿这样一个流程,首先定义一个名为BindView的注解:spa
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @Documented public @interface BindView { // 惟一的value是要指定的R.id变量 int value(); }
在这里咱们定义注解保留在运行时环境中,而且这个注解是应用在类中的FIELD上。.net
下面咱们就能在Activity中使用这个注解了(暂时不起做用):code
public class MainActivity extends AppCompatActivity { @BindView(R.id.textview_a) TextView textViewA; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
如今咱们来实现注解的功能。起一个名为PoorKnife的类,声明一个static方法inject,而且接收一个Activity类型的参数:对象
public class PoorKnife { public static void inject(Activity activity) { try { // 获取类变量 Class contextClass = Class.forName(activity.getClass().getCanonicalName()); // 遍历类中全部Field for (Field field : contextClass.getDeclaredFields()) { // 若是包含注解 if (field.isAnnotationPresent(BindView.class)) { Log.d(TAG, field.getName() + " has annotation"); // 获得注解值 int rId = field.getAnnotation(BindView.class).value(); String type = field.getType().toString(); if (type.endsWith("TextView")) { // 设置field可访问,并将经过set方法赋值view field.setAccessible(true); field.set(activity, activity.findViewById(rId)); } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
inject方法中的操做主要用到反射机制: blog
接下来咱们再次重写onCreate方法,此时才完成了控件对象的初始化工做:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PoorKnife.inject(this); textViewA.setText("PoorKnife succeed!!!"); }
因为是运行时经过反射调用,所以效率相对较低,同理注解 onClick 按照这个流程也能够很方便的实现。