xUtils -- view模塊分析

簡介

xUtils是Android的工具庫,其中包含有View、Bitmap、Db、Http四個模塊。

本篇主要爭對xUtils的2.6.14版本和xUtils3版本的View模塊做個分析。

View模塊主要提供功能有View佈局的綁定,事件的綁定。而在xUtils2.6.14中還提供資源綁定等功能。

使用

@ContentView(R.layout.activity_main)
public class MainActivity extends Activity {

    @ViewInject(R.id.btn)
    Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        x.view().inject(this);

    }


    @Event(value = R.id.btn, type = View.OnLongClickListener.class)
    private boolean onBtnClick(View view) {
        Toast.makeText(MainActivity.this, "onBtnClick", Toast.LENGTH_SHORT).show();
        return true;
    }
}

簡要分析

主要通過註解,反射,動態代理的技術。

在定義View時添加註解,確定所要注入的id等信息,並在Activity啓動的onCreate方法執行注入x.view().inject(this);,而對於事件的綁定則還需要通過動態代理來實現。

流程圖

圖片很好概括了這個過程,圖片來自於xUtils 源碼解析

關於註解的文章:
Java註解實踐
公共技術點之 Java 註解 Annotation

關於反射的文章:
公共技術點之 Java反射 Reflection

關於動態代理的文章:
公共技術點之 Java 動態代理
Java 動態代理機制分析及擴展

xUtils2.6.14和xUtils3分析

兩者都支持註解ContentView、View、Event。

但是2.6.14版本支持註解資源,還有preference,這些功能在xUtils3被移除掉。

xUtils2.6.14和xUtils3的View部分變化主要在Event部分,對比兩個版本View模塊註解,可以發現2.6.14版本的事件註解類型多,而且擴展性不高,

先看下xUtils2.6.14事件註解

// xUtils2.6.14
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
    Class<?> listenerType();

    String listenerSetter();

    String methodName();
}

// onClick方法註解 其他註解類似
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(
        listenerType = View.OnClickListener.class,
        listenerSetter = "setOnClickListener",
        methodName = "onClick")
public @interface OnClick {
    int[] value();

    int[] parentId() default 0;
}

再看下xUtils3的事件註解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Event {

    /**
     * 控件的id集合, id小於1時不執行ui事件綁定.
     *
     * @return
     */
    int[] value();

    /**
     * 控件的parent控件的id集合, 組合爲(value[i], parentId[i] or 0).
     *
     * @return
     */
    int[] parentId() default 0;

    /**
     * 事件的listener, 默認爲點擊事件.
     *
     * @return
     */
    Class<?> type() default View.OnClickListener.class;

    /**
     * 事件的setter方法名, 默認爲set+type#simpleName.
     *
     * @return
     */
    String setter() default "";

    /**
     * 如果type的接口類型提供多個方法, 需要使用此參數指定方法名.
     *
     * @return
     */
    String method() default "";
}

xUtils3通過使用一個註解表示事件。

看下主要函數:

private static void injectObject(Object handler, ViewFinder finder) {

        Class<?> handlerType = handler.getClass();

        // inject ContentView
        ContentView contentView = handlerType.getAnnotation(ContentView.class);
        // ...
        Method setContentViewMethod = handlerType.getMethod("setContentView", int.class);
        setContentViewMethod.invoke(handler, contentView.value());
        // ...  

        // inject view
        Field[] fields = handlerType.getDeclaredFields();
        if (fields != null && fields.length > 0) {
            for (Field field : fields) {
                ViewInject viewInject = field.getAnnotation(ViewInject.class);
                if (viewInject != null) {
                    try {
                        View view = finder.findViewById(viewInject.value(), viewInject.parentId());
                        if (view != null) {
                            field.setAccessible(true);
                            field.set(handler, view);
                        }
                    } catch (Throwable e) {
                        LogUtils.e(e.getMessage(), e);
                    }
                } else {

                    // inject resourse
                    ResInject resInject = field.getAnnotation(ResInject.class);
                    if (resInject != null) {
                        try {
                            Object res = ResLoader.loadRes(
                                    resInject.type(), finder.getContext(), resInject.id());
                            if (res != null) {
                                field.setAccessible(true);
                                field.set(handler, res);
                            }
                        } catch (Throwable e) {
                            LogUtils.e(e.getMessage(), e);
                        }
                    } else {

                        // inject preference
                        // ...
                        field.set(handler, preference);
                        // ...
                    }
                }
            }
        }

        // inject event
        Method[] methods = handlerType.getDeclaredMethods();
        if (methods != null && methods.length > 0) {
            for (Method method : methods) {
                Annotation[] annotations = method.getDeclaredAnnotations();
                if (annotations != null && annotations.length > 0) {
                    for (Annotation annotation : annotations) {
                        Class<?> annType = annotation.annotationType();
                        if (annType.getAnnotation(EventBase.class) != null) {
                            method.setAccessible(true);
                            try {
                                // ...
                                for (int i = 0; i < len; i++) {
                                    ViewInjectInfo info = new ViewInjectInfo();
                                    info.value = Array.get(values, i);
                                    info.parentId = parentIdsLen > i ? (Integer) Array.get(parentIds, i) : 0;
                  // 動態代理處理                  EventListenerManager.addEventMethod(finder, info, annotation, handler, method);
                                }
                            } catch (Throwable e) {
                                LogUtils.e(e.getMessage(), e);
                            }
                        }
                    }
                }
            }
        }
    }

而對於這一個過程分析及實現的有
打造IOC框架 上
打造IOC框架 下

總結

本篇博客簡單分析xUtils的view注入過程,和對xUtils版本改進後的理解。

View的注入使用了註解和反射等技術,在開發過程中難免會造成資源浪費以及效率低下,但是這一過程涉及到Java的註解和反射以及動態代理等技術,還是非常值得去學習,而不是隻會使用。

最後,如哪裏不足或者分析錯誤,非常歡迎指正,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章