IOC注入技术之运行时注入

IOC技术主要内容:
1.运行时注入, eventBus,springMVC,xUtils
2.源码时注入, android studio插件
3.编译时注入, butterknife,dagger2

需要的基础知识:泛型,反射,注解,动态代理

反射的核心:利用加载到JVM内存中的字节码获取对象的信息,并调用相关反射API进行创建对象,调用方法等操作。

核心思想

  • IOC是一种设计思想
  • 与设计模式要区分开

IOC是原来由程序代码中主动获取资源的方式,转变为由第三方获取资源并使原来的代码被动接收资源的方式,以达到解耦的效果,称为控制反转。

一张图解释下IOC:
在这里插入图片描述

手写实现运行时注入核心原理

布局的注入

@Target(ElementType.TYPE)//ElementType.TYPE表示这个注解是用在类上面的
@Retention(RetentionPolicy.RUNTIME)//三种注解类型:java-->class-->runtime
public @interface ContentView {
    int value();
}

控件的注入

    private static void injectView(Object context) {
        Class<?> clazz = context.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {
                int valueId = viewInject.value();
                //运行到这里,每个按钮的ID已经取到了
                //注入就是反射执行findViewById方法
                try {
                    Method method = clazz.getMethod("findViewById", int.class);
                    //View view = mainActivity.findViewById(valueId);
                    View view = (View) method.invoke(context, valueId);
                    field.setAccessible(true);
                    //mainActivity的field字段的值设置为view
                    field.set(context, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

事件的注入

事件的三要素

1.textView 事件源
2 new View.OnClickListener() 事件
3 onClick() 事件处理
进行订阅事件 setOnClickListener

textView.setOnClickListener(new View.OnClickListener() {
	//onClick() 事件处理
    @Override
    public void onClick(View v) {
        
    }
}); 

注解的多态:
比如定义了注解EventBase :

@Target(ElementType.ANNOTATION_TYPE)//ANNOTATION_TYPE表示这个注解是用在其他注解上面的,比如写在OnClick注解上
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
//    textView.setOnClickListener(new View.OnClickListener() {
//        @Override
//        public void onClick(View v) {
//
//        }
//    });
    //1.订阅关系,如:setOnClickListener()方法
    String listenerSetter();
    //2.订阅关的事件本身, 如:new View.OnClickListener()
    Class<?> listenerType();
    //3.事件处理方法,如:onClick()方法
    String callbackMethod();

}

则可以在OnClick和OnLongClick 注解中的使用:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)//java-->class-->runtime
@EventBase(listenerSetter = "setOnClickListener"
        ,listenerType = View.OnClickListener.class
        ,callbackMethod = "onClick")
public @interface OnClick {
    int[] value() default -1;
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)//java-->class-->runtime
@EventBase(listenerSetter = "setOnLongClickListener"
        ,listenerType = View.OnLongClickListener.class
        ,callbackMethod = "onLongClick")
public @interface OnLongClick {
    int[] value() default -1;
}

事件的注入

private static void injectClick(Object context) {
    //需要一次性处理安卓中23种事件
    Class<?> clazz = context.getClass();
    Method[] methods = clazz.getDeclaredMethods();

    for (Method method : methods) {
        //注意,别把代码写死了 method.getAnnotation(OnClick.class);
        //调用method.getAnnotations()得到方法上所有的注解
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations) {
            //annotation是事件,比如onClick、OnLongClick ,取对应的注解
            Class<?> annotationClass = annotation.annotationType();
            EventBase eventBase = annotationClass.getAnnotation(EventBase.class);
            //如果没有eventBase,则表示当前方法不是一个事件处理的方法
            if (eventBase == null) {
                continue;
            }
            //否则就是一个事件处理的方法
            //开始获取事件处理的相关信息(三要素)
            //用于确定是哪种事件
//                btn.setOnClickListener(new View.OnClickListener() {
//                    @Override
//                    public void onClick(View v) {
//
//                    }
//                });
            //1.订阅关系,如:setOnClickListener()方法
//                String listenerSetter();
            String listenerSetter = eventBase.listenerSetter();
            //2.订阅关的事件本身, 如:new View.OnClickListener()
//                Class<?> listenerType();
            Class<?> listenerType = eventBase.listenerType();
            //3.事件处理方法,如:onClick()方法
//                String callbackMethod();
            String callBackMethod = eventBase.callbackMethod();

            //得到3要素之后,就可以执行代码了
            Method valueMethod = null;
            try {
                //反射调用OnClick、OnLongClick注解的value()方法,得到所有view的id,再根据id得到对应的VIEW(Button)
                valueMethod = annotationClass.getDeclaredMethod("value");
                int[] viewId = (int[]) valueMethod.invoke(annotation);
                for (int id : viewId) {
                    //为了得到Button对象,使用findViewById
                    Method findViewByIdMethod = clazz.getMethod("findViewById", int.class);
                    View view = (View) findViewByIdMethod.invoke(context, id);
                    //运行到这里,view就相当于Activity中我们写的Button
                    if (view == null) {
                        continue;
                    }

                    //activity==context    click===method
                    ListenerInvocationHandler listenerInvocationHandler =
                            new ListenerInvocationHandler(context, method);

                    //做代理   new View.OnClickListener()对象
                    Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader()
                            , new Class[]{listenerType}, listenerInvocationHandler);
                    //执行  让proxy执行的onClick()
                    //参数1  setOnClickListener()
                    //参数2  new View.OnClickListener()对象
                    //view.setOnClickListener(new View.OnClickListener())
                    //获取view对象的setOnClickListener方法,方法的形参是OnClickListener
                    Method setOnClickListeneMethod = view.getClass().getMethod(listenerSetter, listenerType);
                    setOnClickListeneMethod.invoke(view, proxy);//执行view对象的setOnClickListener方法,传递的实参是proxy
                    // 那么当系统调用view设置的监听器的所有方法时(自然包括onClick()、onLongClick()),都会被转为调用proxy的invoke方法()。
                    // 这时候,如果点击按钮,就会去执行代理类proxy中的invoke方法(),最终调用activity中标注了注解的方法

                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

}

Class的getMethod方法:public Method getMethod(String name, Class<?>... parameterTypes)的作用是获得对象所声明的公开方法。该方法的第一个参数name是要方法的名称,第二个参数parameterTypes是该方法的按声明顺序的所有形参类型。

如何让系统执行点击事件的方法时调用Activity中标注了注解的方法?
使用动态代理。即调用view对象的setOnClickListener方法,传递的实参是proxy动态代理实例,那么view对象里面的监听器对象就是一个动态代理实例,当系统在View内部调用监听器对象的所有方式时都会转为调用动态代理实例的invoke()方法,然后在invoke()方法里面调用Activity中标注了注解的方法。
在这里插入图片描述

看一下调用路径:

		longClick()
    java.lang.Exception
        at com.example.javaadvanced.OpenSourceFramework.ioc_runtime.IOCMainActivity.longClick(IOCMainActivity.java:55)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.example.javaadvanced.OpenSourceFramework.ioc_runtime.ListenerInvocationHandler.invoke(ListenerInvocationHandler.java:30)
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
        at $Proxy8.onLongClick(Unknown Source)
        at android.view.View.performLongClickInternal(View.java:7528)
        at android.view.View.performLongClick(View.java:7486)
        at android.widget.TextView.performLongClick(TextView.java:12390)
        at android.view.View.performLongClick(View.java:7504)
        at android.view.View$CheckForLongPress.run(View.java:28254)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

可以看到 $Proxy8 就是jdk生成的动态代理类,后面显示的是(Unknown Source),而其他方法调用都显示java文件信息,因为这个类并没有java文件,是jdk在内存中直接生成字节码的。

总结

1.得到activity上所有的方法
2.再取到每一个方法上所有的注解
3.找到方法上有EventBase注解的,就是事件方法
4.如果是事件处理方法,就要得到这个方法对应的三要素
5.得到三要素后,需要一个动态代理来执行系统的onClick
6.自定义一个动态代理设置到View的OnClickListener对象上
7.让框架执行代理(系统对onClick的调用转为了调用代理中的invoke方法)
8.由代理去执行用户写的abc()点击事件相关方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值