IOC注入框架的手寫實現

Ioc注入框架的手寫實現

目的:自己手寫是爲了進一步加深Android種IOC注入實現的理解,例如Android Annotations,ButterKnife,Dagger等開源庫都有體現。

代碼參考:https://github.com/itlgc/Android-IOC-Custom

loC的核心是解耦

在Spring中IoC更多的是依靠xml的配置
而Android上的IoC框架可以不使用xml配置

佈局注入思路

獲得類>佈局註解>註解的值>獲取指定方法>執行方法

Class<? extends Activity> clazz = activity.getClass();
//獲取類之上的註解
ContentView contentView = clazz.getAnnotation(ContentView.class);
if (contentView != null) {
    //獲取註解的值,也就是佈局id
    int layoutId = contentView.value();

    //執行setContentView方法
    Method setContentViewMethod = clazz.getMethod("setContentView", int.class);
    setContentViewMethod.invoke(activity, layoutId);
}

控件注入思路

獲得類>獲得所有屬性>遍歷屬性>每個屬性的註解>每個註解的值>獲得指定方法>執行方法>設置訪問私有>賦值

Class<? extends Activity> clazz = activity.getClass();
//獲取該類所有屬性
Field[] fields = clazz.getDeclaredFields();
//循環 獲取屬性上的註解
for (Field field : fields) {
    InjectView injectView = field.getAnnotation(InjectView.class);
    if (injectView != null) {
        int viewId = injectView.value();

        Method findViewByIdMethod = clazz.getMethod("findViewById", int.class);
        Object view = findViewByIdMethod.invoke(activity, viewId);

        //賦值 屬性的值賦值給控件 在當前的Activity
        field.setAccessible(true);
        field.set(activity, view);
    }
}

事件注入思路

事件有點擊事件、長按事件、觸摸事件等,需要對它們進行兼容處理

        /**
         * 代碼特點:
         * 1、setXXXClickListener
         * 2、xxxClickListener
         * 3、回調方法 onxxx()
         */
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //真正寫代碼的地方
            }
        });

        btn.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                return false;
            }
        });

根據事件代碼特點將其3個重要的成員進行封裝

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {

    //事件3個重要的成員封裝
    // 1、setXXXClickListener
    String listenerSetter();

    // 2、new xxxClickListener()對象
    Class<?> listenerType();

    // 3、回調方法 onxxx()
    String callbackListener();
}

針對點擊事件的註解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(listenerSetter = "setOnClickListener",listenerType = View.OnClickListener.class,
        callbackListener = "onClick")
public @interface OnClick {
    int[] value();
}

具體操作思路見如下注釋

Class<? extends Activity> clazz = activity.getClass();
//獲取類的所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    //循環 獲取方法上的所有註解
    Annotation[] annotations = method.getAnnotations();
    for (Annotation annotation : annotations) {
        //遍歷 獲取其中onClick註解上的註解,即獲取父類EventBase註解上的類型
        // 進而從方法諸多註解中篩選出Onclick註解
        Class<? extends Annotation> annotationType = annotation.annotationType();
        if (annotationType != null) {
            //獲取EventBase註解 和事件3個重要的成員封裝
            EventBase eventBase = annotationType.getAnnotation(EventBase.class);
            if (eventBase != null) {
                String listenerSetter = eventBase.listenerSetter();
                Class<?> listenerType = eventBase.listenerType();
                //這就是需要攔截的方法
                String callbackListener = eventBase.callbackListener();

                //獲取onClick註解中的value方法
                Method valueMethod = annotationType.getDeclaredMethod("value");
                valueMethod.setAccessible(true);
                //獲取註解上的值 (數組)
                int[] ViewsId = (int[]) valueMethod.invoke(annotation);

                //動態代理
                ListenerInvocationHandler invocationHandler =
                        new ListenerInvocationHandler(activity);
                invocationHandler.addMethods(callbackListener, method);

                Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(),
                        new Class[]{listenerType}, invocationHandler);

                for (int viewId : ViewsId) {
                    //獲取當前activity中View控件 並賦值
                    View view = activity.findViewById(viewId);

                    //這樣才能兼容 setxxxClicklister  setxxxLongClickLister等
                    Method setter = view.getClass().getMethod(listenerSetter, listenerType);
                    setter.invoke(view, listener);

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