經過對《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 按照這個流程也能夠很方便的實現。