Java--Annotation之旅 ( Runtime反射篇)

相信經(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ì)照上半部分。

特別說明:

  1. 由于每一次都會(huì)對(duì)所有的field ,或 method 進(jìn)行遍歷,這樣會(huì)比較影響效率,這也是反射存在的一個(gè)問題
  2. 反射在注解中使用,那么注解必須是Runtime保留級(jí)別,筆者粗心使用上一篇中的SOURCE級(jí)別注解,未修改,則一直無法成功
  3. 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í)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容