免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
代理模式和IOC架構設計之事件注入

博客主頁

這篇文章講解了編譯時注入,但運行時注入框架也值得學習。

結下來的任務是分析xUtils3核心模塊IOC注入式的框架設計,注解解決事件的三要素,靜態(tài)代理和動態(tài)代理,運行時注入布局,控件,事件

運行時注入布局

在Activity中加載布局文件一般都是通過在onCreate方法中調用setContentView方法設置布局

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

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

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

然后只需要在onCreate方法中調用注入方法,就自動幫助我們設置了布局

ViewInjector.inject(this);

實現(xiàn)方式就是被一些人所詬病反射技術實現(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注解得到布局文件,使用反射調用setContentView方法。

運行時注入控件

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

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

注入控件代碼實現(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注解,使用反射調用findViewById找到view并設置給該字段。

運行時注入事件

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

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

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

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

  2. 事件源:事件的listener,默認為點擊事件,如View.OnClickListener

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

/** * * 事件注解 * 被注解的方法必須: * 1. private修飾 * 2. 返回值類型沒有要求 * 3. 參數(shù)簽名和type的接口要求的參數(shù)簽名一致 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Event {    // 控件的id集合,當id小于1時不執(zhí)行事件綁定    int[] value();    // 事件三要素:訂閱、事件源、事件    // 訂閱,事件的setter方法名,默認為set+type    String setter() default "";    // 事件源,事件的listener,默認為點擊事件    Class<?> type() default View.OnClickListener.class;    // 事件,如果type的接口類型提供多個方法,需要使用此參數(shù)指定方法名    String method() default "";}

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

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

注入事件代碼實現(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注解默認的接口為View.OnClickListener            Class<?> listenerType = event.type();            // 默認為空,事件的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注解后,獲取事件三要素。通過反射調用訂閱方法,方法的參數(shù)設置為代理類

private static class EventInvocationHandler implements InvocationHandler {    // 存放代碼對象,如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;    }}
本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Butterknife源碼
Butterknife用法
面試官:@Transactional 注解是如何實現(xiàn)的?面試必問!
Spring-Schedule框架的@Scheduled注解繼承問題
@Transactional注解不起作用解決辦法及原理分析
spring aop 自定義注解實現(xiàn)本地緩存
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服