美文网首页
注解+代理使用

注解+代理使用

作者: 双囍_小赵 | 来源:发表于2021-09-13 10:39 被阅读0次

    代理作用:

    能增加程序灵活度,完美解决解耦问题。
    动态代理可以将调用层和实现层分离,如:retrofit。
    动态代理不需要接口的实现类,如:适用于IPC通信进程。
    动态代理在静态代理的基础上生成了一个class文件,让它去帮我做事。
    

    静态代理:

    举例:我想去买个东西,但是我自己不想去,于是让别人跑腿给我去买,这个就属于代理。
    

    案例中实现如下:

    首先定义一个买东西的动作接口:

    
        //定义一个买东西的接口
       public interface BuySome {
    
                void thing();
    
          }
    
    

    我的目的是要去买东西,所以我自己也要实现这个接口,表明自己的目的:

    //定义一个我的类,实现我想去做的事情
    public class Me implements BuySome{
    
        @Override
    
        public void thing() {
    
                System.out.println("我是被代理类,让代理类帮我去实现这个打印");
    
        }
    
    }
    

    现在我需要张三去帮我做这个动作,那就创建一个人——张三,让他去真正的操作:

    public class Zhangsan implements BuySome{
    
        //初始化被代理的类
        BuySome ioc =new Me();
    
    
        @Override
        public void thing() {
    
            System.out.println("我是代理类,现在我帮Me去实现");
    
            ioc.thing();
    
           System.out.println("我是代理类,我帮Me去实现了");
    
        }
    
        //测试一下
        public static void main(String[] args) {
    
            Zhangsan zhangsan =new Zhangsan();
    
            zhangsan.thing();
    
        }
    
        最后打印结果测试:
        ----:我是代理类,现在我帮Me去实现
    
        ----:我是被代理类,让代理类帮我去实现这个打印
    
        ----:我是代理类,我已经帮Me实现了
    

    以上是静态代理操作案例。

    接下来我们看动态代理是如何实现:

    此案例是演示实现点击和长按代理的操作,使用关键步骤Proxy.newProxyInstance。

    准备工作:
    创建一个点击的注解Click

    /**
     * Created by:zx
     * on 9/8/21
     * 点击的注解
     **/
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)//android运行在模拟器需要运行时
    
    @EventBase(listenerSetter= "setOnClickListener",listenerType = View.OnClickListener.class,callbackMethod = "onClick")
    public @interface Click {
        int[] value() default -1;//点击事件不止一个按钮,所以这里定义数组
    }
    

    接下来定义一个长按事件的注解:

    /**
     * Created by:zx
     * on 9/8/21
     * 长按事件
     **/
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)//android运行在模拟器需要运行时
    
    @EventBase(listenerSetter= "setOnLongClickListener",listenerType = View.OnLongClickListener.class,callbackMethod = "onLongClick")
    
    public @interface OnLongClick {
        int[] value() default -1;
    }
    

    再定义一个可以在以上两个点击注解上使用的注解,注意Target的值,并且在里面定义的三个方法分别对应了原本onClickListener/onLongClickListener事件的三个关键:

    /**
     * Created by:zx
     * on 9/8/21
     * 使用在Click注解之上,为了方便view不用多次调用onClick、onLongClick等这些类似的方法回调
     **/
    @Target(ElementType.ANNOTATION_TYPE)//使用在注解上
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EventBase {
        //事件三要素
        //事件监听的方法setOnClickListener
        String listenerSetter();
    
        //事件监听的类型 OnClickListener事件类型
        Class<?> listenerType();
    
        //事件回调方法
        String callbackMethod();
    
    }
    

    注:

    • listenerSetter 对应的是 setOnLongClickListener
    • listenerType 对应的是 View.OnLongClickListener的接口
    • callbackMethod 对应的是 onLongClick回调方法

    如下代码:setOnLongClickListener和setOnClickListener类似流程,所以可以定义同一个注解表示他们。

     textView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    return false;
                }
            });
     textView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
    
                }
            });
    
    

    定义一个代理类,实现InvocationHandler,接下来需要用到这个类

    /**
     * Created by:zx
     * on 9/8/21
     * 动态代理
     **/
    public class ClickInterface implements InvocationHandler {
        Object act;
        Method method;
    
        public ClickInterface(Object object, Method method) {
            this.act = object;
            this.method = method;
        }
    
        @Override
        public Object invoke(Object proxy, Method me, Object[] args) throws Throwable {
            Log.d("zx","代理前---------------");
            Object o = method.invoke(act,args);//这里参数名不要搞成invoke方法的名字了,否则会报错
            Log.d("zx","代理后---------------");
            return o;
        }
    }
    
    /**
     * Created by:zx
     * on 9/8/21
     * 注解由于是架构,所以里面不能有确切的类型定义  比如Click它是点击事件的具体注解类,后面再加上长按事件就不可以使用了,所以要动态定义以便于长按也可以套用
     * 注解+动态代理
     **/
    public class BindView {
        public static void bind(final Object o) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            //获取当前Activity
            Class<?> cls = o.getClass();
            //获取此class中所有的方法
            Method[] methods = cls.getDeclaredMethods();
    
            for (Method method : methods) {
    
                //获取方法上的注解  使用数组是因为一个方法上可能定义的不止一个注解
                Annotation[] annotations = method.getAnnotations();
    
                //循环这个注解数组
                for (Annotation annotation : annotations) {
                    //获得注解的类型
                    Class<?> annType = annotation.annotationType();
    
                    //从注解里面的类型中找到EventBase类型的注解(因为他是注解上的注解)
                    EventBase eventBase = annType.getAnnotation(EventBase.class);
    
                    //判断这个注解是否为空
                    if (eventBase == null) {
                        continue;
                    }
                    //如果有值说明method是需要事件注入的
                    //获取事件三要素:
                    //获取第一个要素
                    String listenerSetter = eventBase.listenerSetter();
                    //获取事件类型
                    Class<?> listenerType = eventBase.listenerType();
                    //获取回调方法  这个参数不要也行 没用到
                    String callbackMethod = eventBase.callbackMethod();
    
                    //获取点击value  也就是注解上的值  也就是R.id...
                    //注意📢:这个value的值就是你自己注解中定义的那个value  可以不是固定的
                    Method idValue = annType.getDeclaredMethod("value");
                    //这里的invoke反射   把注解的ids都反射出来  我的理解 谁里面有你需要的东西就反射(invoke)谁
                    int[] ids = (int[]) idValue.invoke(annotation);
    
                    //接下来ids都拿到了,就和之前的代码逻辑一样了   通过ids得到view  在使用view做事件处理
                    //findViewbyID找到View
                    for (int id : ids) {
                        Method findViewById = cls.getMethod("findViewById", int.class);
                        View view = (View) findViewById.invoke(o, id);//通过反射拿到了view
                        //拿到View接下来做我们点击事件
                        if (view == null) {
                            continue;
                        }
    
                        //使用动态代理   proxy返回类型取决于第二个参数 new Class[]{View.OnClickListener.class
                        // proxy属性是一个OnClickListener的子类,在内存里生成了一个子类(OnClickListener的实现,帮它做操作)
                        //原本View.OnClickListener.class 改成  listenerType
                        Object proxy = Proxy.newProxyInstance(o.getClass().getClassLoader(),
                                new Class[]{listenerType},
                                new ClickInterface(o, method));
                        //原本拿到View要做点击事件,但是这里就不用写具体的点击事件了,直接改成下面两行
    //                    view.setOnClickListener((View.OnClickListener) proxy);
    
                        Method method1 = view.getClass().getMethod(listenerSetter, listenerType);
                        method1.invoke(view, proxy);
    
                    }
              }
           }
      }
    }
    

    以上就是定义的主要的动态代理的实现类,接下来看一下,如果使用:

    /**
     * 利用反射或者动态代理,直接调用Click注解就可以拿到id实现view的点击
     */
    public class IOCActivity extends AppCompatActivity {
        
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_ioc);
            try {
                //这里要绑定一下,也就是把Activity传到BingView的类里去做解析
                BindView.bind(this);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
    
        }
    
        //一个方法上可能有多个注解,不要忘记定义clickView方法的参数要和真正的点击事件的回调方法一样传一个View v。
        @OnLongClick(R.id.text)
        @Click(R.id.text)
        public void clickView(View v) {
            Toast.makeText(this, "点击了", Toast.LENGTH_LONG).show();
            Log.d("zx", "我是在代理中 也就是ClickInterface中invoke方法中method的实现");
        }
    
    
        @OnLongClick(R.id.text)
        public boolean longClick(View view) {
            Toast.makeText(this, "长按", Toast.LENGTH_LONG).show();
            return true;
        }
    
    }
    

    以上就是动态代理的简单的实际应用案例,可以帮助更好的理解动态代理。

    相关文章

      网友评论

          本文标题:注解+代理使用

          本文链接:https://www.haomeiwen.com/subject/kgaxgltx.html