日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

代理模式和IOC架構(gòu)設(shè)計(jì)之事件注入

 怡紅公子0526 2020-02-29

博客主頁(yè)

這篇文章講解了編譯時(shí)注入,但運(yùn)行時(shí)注入框架也值得學(xué)習(xí)。

結(jié)下來(lái)的任務(wù)是分析xUtils3核心模塊IOC注入式的框架設(shè)計(jì),注解解決事件的三要素,靜態(tài)代理和動(dòng)態(tài)代理,運(yùn)行時(shí)注入布局,控件,事件

運(yùn)行時(shí)注入布局

在Activity中加載布局文件一般都是通過在onCreate方法中調(diào)用setContentView方法設(shè)置布局

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

如果在Activity的類上通過注解方式設(shè)置布局,如下代碼。運(yùn)行時(shí)注入布局方式實(shí)現(xiàn),@ContentView替代setContentView

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

然后只需要在onCreate方法中調(diào)用注入方法,就自動(dòng)幫助我們?cè)O(shè)置了布局

ViewInjector.inject(this);

實(shí)現(xiàn)方式就是被一些人所詬病反射技術(shù)實(shí)現(xiàn)。

public static void inject(Object target) {
    Class<?> targetClass = target.getClass();

    // 注入布局文件
    // 獲取Activity上的ContentView注解
    ContentView contentView = targetClass.getAnnotation(ContentView.class);
    if (contentView != null) {
        int layoutResid = contentView.value();
        // 布局資源文件非法
        if (layoutResid <= 0) {
            throw new RuntimeException("注入的布局資源文件非法");
        }

        try {
            Method setContentViewMethod = targetClass.getMethod("setContentView", int.class);
            setContentViewMethod.invoke(target, layoutResid);
        } catch (Exception e) {
            throw new RuntimeException("注入的布局資源文件失敗::" + e.getMessage());
        }
    }
}

通過獲取Activity上的ContentView注解得到布局文件,使用反射調(diào)用setContentView方法。

運(yùn)行時(shí)注入控件

通過在控件上添加@ViewInject,就可以代替findViewById

public class MainActivity extends AppCompatActivity {
    @ViewInject(R.id.text)
    private TextView text;
}

注入控件代碼實(shí)現(xiàn)

public static void inject(Object target) {
   // ...
   injectObject(target, targetClass);
}

private static void injectObject(Object target, Class<?> targetClass) {
    if (targetClass == null) return;

    // 注入控件
    Field[] fields = targetClass.getDeclaredFields();
    if (fields.length > 0) {
        for (Field field : fields) {
            Class<?> fieldType = field.getType();
            if (/*不注入基本類型字段*/ fieldType.isPrimitive() ||
                    /*不注入數(shù)組類型字段*/ fieldType.isArray() ||
                    /*不注入靜態(tài)字段*/ Modifier.isStatic(field.getModifiers()) ||
                    /*不注入final字段*/ Modifier.isFinal(field.getModifiers())) {
                continue;
            }

            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {
                int viewResid = viewInject.value();
                if (viewResid <= 0) continue;

                try {
                    Method findViewByIdMethod = targetClass.getMethod("findViewById", int.class);
                    Object view = findViewByIdMethod.invoke(target, viewResid);

                    if (view != null) {
                        field.setAccessible(true);
                        field.set(target, view);
                    } else {
                        throw new RuntimeException("Invalid @ViewInject for "
                                + targetClass.getSimpleName() + "." + field.getName());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

通過反射拿到注入類中所有的字段,排除不需要注入的字段有:基本類型、數(shù)據(jù)類型、靜態(tài)修飾、final修飾。在獲取字段上的@ViewInject注解,使用反射調(diào)用findViewById找到view并設(shè)置給該字段。

運(yùn)行時(shí)注入事件

在實(shí)現(xiàn)運(yùn)行時(shí)注入事件之前,先了解下動(dòng)態(tài)代理。

在動(dòng)態(tài)代理中,代理類并不是在java代碼中實(shí)現(xiàn),而是在運(yùn)行期生成,相比靜態(tài)代理,動(dòng)態(tài)代理可以很方便的對(duì)委托類的方法進(jìn)行統(tǒng)一處理。

事件的三要素:訂閱、事件源、事件

  1. 訂閱:事件的setter方法名,默認(rèn)為set+type,如setOnClickListener

  2. 事件源:事件的listener,默認(rèn)為點(diǎn)擊事件,如View.OnClickListener

  3. 事件:事件源中提供的方法,如onClick

/**
 *
 * 事件注解
 * 被注解的方法必須:
 * 1. private修飾
 * 2. 返回值類型沒有要求
 * 3. 參數(shù)簽名和type的接口要求的參數(shù)簽名一致
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Event {
    // 控件的id集合,當(dāng)id小于1時(shí)不執(zhí)行事件綁定
    int[] value();

    // 事件三要素:訂閱、事件源、事件

    // 訂閱,事件的setter方法名,默認(rèn)為set+type
    String setter() default "";

    // 事件源,事件的listener,默認(rèn)為點(diǎn)擊事件
    Class<?> type() default View.OnClickListener.class;

    // 事件,如果type的接口類型提供多個(gè)方法,需要使用此參數(shù)指定方法名
    String method() default "";
}

只需要在方法上添加@Event注解就可以代替View.setOnClickListener(OnClickListener l)

@Event(R.id.text)
private void gotoOut(View view) {
    Log.d("todo_xutils", "onCreate: 注入的方式點(diǎn)擊事件");
}

注入事件代碼實(shí)現(xiàn)

private static void injectObject(Object target, Class<?> targetClass) {
    // 注入事件
    Method[] methods = targetClass.getDeclaredMethods();
    if (methods.length > 0) {
        for (Method method : methods) {
            if (/*不注入靜態(tài)的方法*/ Modifier.isStatic(method.getModifiers()) ||
                    /*注入的方法必須是private的*/  !Modifier.isPrivate(method.getModifiers())) {
                continue;
            }

            Event event = method.getAnnotation(Event.class);
            if (event != null) {
                int[] ids = event.value();

                for (int i = 0; i < ids.length; i++) {
                    int id = ids[i];
                    if (id > 0) {
                        method.setAccessible(true);
                        EventListenerManager.addEventMethod(target, targetClass, id, event, method);
                    }
                }
            }
        }
    }
}

通過反射拿到注入類中所有的方法,注入的方法需要必須:private修飾、返回值類型沒有要求、參數(shù)簽名和type的接口要求的參數(shù)簽名一致。

class EventListenerManager {
    static void addEventMethod(
            Object target, /*注入的類*/
            Class<?> targetClass, /*注入的類Class*/
            int id,  /*注入的控件的id*/
            Event event, /*Event注解*/
            Method method  /*注入的方法*/
    ) {
        try {
            Method findViewByIdMethod = targetClass.getMethod("findViewById", int.class);

            Object view = findViewByIdMethod.invoke(target, id);
            if (view == null) {
                throw new RuntimeException("No Found @Event for "
                        + targetClass.getSimpleName() + "." + method.getName());
            }
//            view.setOnClickListener(new View.OnClickListener() {
//                @Override
//                public void onClick(View v) {
//                }
//            });

            // 注解中定義的接口,如Event注解默認(rèn)的接口為View.OnClickListener
            Class<?> listenerType = event.type();
            // 默認(rèn)為空,事件的setter方法名,如:setOnClickListener
            String listenerSetter = event.setter();
            if (TextUtils.isEmpty(listenerSetter)) {
                listenerSetter = "set" + listenerType.getSimpleName();
            }

            Object proxyListener = Proxy.newProxyInstance(
                    listenerType.getClassLoader(),
                    new Class<?>[]{listenerType},
                    new EventInvocationHandler(target, method)
            );

            //  view.setOnClickListener(@Nullable OnClickListener l)
            Method setEventListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);
            setEventListenerMethod.invoke(view, proxyListener);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

獲取Method上的@Event注解后,獲取事件三要素。通過反射調(diào)用訂閱方法,方法的參數(shù)設(shè)置為代理類

private static class EventInvocationHandler implements InvocationHandler {
    // 存放代碼對(duì)象,如MainActivity
    private WeakReference<Object> targetRef;
    private Method targetMethod;

    EventInvocationHandler(Object target, Method method) {
        targetRef = new WeakReference<>(target);
        targetMethod = method;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object target = targetRef.get();
        if (target != null) {
            return targetMethod.invoke(target, args);
        }
        return null;
    }
}

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多