相信經(jīng)過前面兩篇文章的學(xué)習(xí),我們對(duì)Java Annotation 有了一定的掌握了。仔細(xì)的同學(xué)注意到了,我們自定義的注解的保留級(jí)別是source ,那么APT技術(shù)就是注解source級(jí)別的重要應(yīng)用場(chǎng)景之一,其次該級(jí)別的應(yīng)用場(chǎng)景是IDE可以根據(jù)注解來進(jìn)行語(yǔ)法檢查,例如:@IdRes ,@IntDef 等注解,詳細(xì)用法可以去找度娘。
我們今天要學(xué)習(xí)的是注解另外一個(gè)應(yīng)用場(chǎng)景(即Runtime保留級(jí)別)--反射的使用。
關(guān)于反射后面會(huì)有詳細(xì)的文章來介紹,這里就不展開介紹了。
接下來我們就直接開擼:
我們還是接著使用上兩篇中的自定義的注解,也可以到github上下載學(xué)習(xí)Demo
public class InjectUtils {
/*
* TODO 反射綁定view
*/
public static void injectAnnotation(final Activity activity) {
Class<? extends Activity> aClass = activity.getClass();
/*
* TODO 獲取所有的自己的fields,不包括父類
*/
Field[] declaredFields = aClass.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
//遍歷所有的field
Field field = declaredFields[i];
if (field.isAnnotationPresent(BindView.class)) {
//找到使用BindView注解的屬性,并取出對(duì)應(yīng)的注解
BindView annotation = field.getAnnotation(BindView.class);
if (annotation != null) {
//獲取到注解的參數(shù)值
int viewId = annotation.viewId();
//找到Id對(duì)應(yīng)的視圖
View view = activity.findViewById(viewId);
//設(shè)置field是可以訪問的,否則則不能操作
field.setAccessible(true);
try {
//將找到的視圖賦值給field
field.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/*
* TODO 獲取該類所有的方法,不包括父類
*/
Method[] declaredMethods = aClass.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
final Method declaredMethod = declaredMethods[i];
if (declaredMethod.isAnnotationPresent(ViewOnClick.class)) {
ViewOnClick annotation = declaredMethod.getAnnotation(ViewOnClick.class);
if (annotation != null) {
final int[] viewIds = annotation.viewId();
for (int i1 = 0; i1 < viewIds.length; i1++) {
final int finalI = i1;
activity.findViewById(viewIds[i1]).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//反射方法的調(diào)用使用invoke
declaredMethod.invoke(activity, viewIds[finalI]);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
}
}
}
}
}
上述代碼幾乎對(duì)每一句都進(jìn)行了注釋,大家可以看到,method 和Field的處理邏輯基礎(chǔ)一致,所以下面處理method上的注解的注釋可以對(duì)照上半部分。
特別說明:
- 由于每一次都會(huì)對(duì)所有的field ,或 method 進(jìn)行遍歷,這樣會(huì)比較影響效率,這也是反射存在的一個(gè)問題
- 反射在注解中使用,那么注解必須是Runtime保留級(jí)別,筆者粗心使用上一篇中的SOURCE級(jí)別注解,未修改,則一直無法成功
- getDeclaredFields() 和getFields()兩個(gè)方法的區(qū)別,前者是返回當(dāng)前類的所有的成員變量,不包括父類;后者則返回當(dāng)前類及其父類的(public)所有的成員變量(含static),那么如何區(qū)分父類的成員變量呢,可以調(diào)用superClass()的getDeclaredFields()方法獲取。
同理:getDeclaredMethods()和getMethods()是一個(gè)道理
簡(jiǎn)單使用:
@BindView(viewId = R.id.inject_btn)
Button mInjectBtn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inject);
InjectUtils.injectAnnotation(this);
}
@ViewOnClick(viewId = R.id.inject_btn)
public void onClick(@IdRes int viewId) {
if (viewId == R.id.inject_btn) {
Toast.makeText(this, mInjectBtn.getText().toString(), Toast.LENGTH_SHORT).show();
}
}
其實(shí)和注解處理器的使用是一樣的,只是InjectUtils.injectAnnotation(this);換成反射工具的調(diào)用了。
怎么樣,反射來處理注解是不是很簡(jiǎn)單?以上是注解應(yīng)用場(chǎng)景--運(yùn)行時(shí)反射解析。那么注解保留級(jí)別是CLASS時(shí)的應(yīng)用場(chǎng)景又是什么呢,我們下一篇來學(xué)習(xí)。