美文网首页
写给自己的IoC

写给自己的IoC

作者: 无良安生 | 来源:发表于2021-04-13 17:33 被阅读0次

IoC是什么呢?
我自己也整不明白,我怎么解释,自己百度去吧

今天干啥,那就弄个注解加载布局加载view 设置监听吧
废话不多说,反正我感觉你也不想听,那就上代码吧

首先看界面

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

    @InjectViews(viewRes = R.id.tv_test)
    TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InitLayout.inject(this);
        textView.setText("来呀打我呀!");
    }

    @OnClick(viewIds = {R.id.tv_demo})
    public void click(View view){
        Toast.makeText(getBaseContext(),"click",Toast.LENGTH_LONG).show();
    }

    @OnLongClick(viewIds = {R.id.tv_test})
    public boolean longClic(View view) {
        Toast.makeText(getBaseContext(),"longClic",Toast.LENGTH_LONG).show();
        return false;
    }
}

布局不给了,就是两个文本,自己写

然后再给你看看注解

注解activity 布局

/*
          TYPE: 用于描述类、接口(包括注解类型) 或enum声明 Class, interface (including annotation type), or enum declaration
          FIELD: 用于描述域 Field declaration (includes enum constants)
          METHOD: 用于描述方法 Method declaration
          PARAMETER: 用于描述参数 Formal parameter declaration
          CONSTRUCTOR: 用于描述构造器 Constructor declaration
          LOCAL_VARIABLE: 用于描述局部变量 Local variable declaration
          ANNOTATION_TYPE: Annotation type declaration
          PACKAGE: 用于描述包 Package declaration
          TYPE_PARAMETER: 用来标注类型参数 Type parameter declaration
          TYPE_USE: 能标注任何类型名称 Use of a type

          RetentionPolicy
          SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
          CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
          RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

 */

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
    int layoutRes();
}

注解初始化view

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectViews {
    int viewRes() default 0;
}

注解设置事件

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ClickBase(listener = "setOnClickListener" ,callback = "onClick",listenerType = View.OnClickListener.class)
public @interface OnClick {
    int[] viewIds();
}

再看这个ClickBase

@Target(ElementType.ANNOTATION_TYPE)//在注解之上
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickBase {

    //设置事件得方法名称
    String listener();
    //事件回调的类行
    Class<?> listenerType();
    //回调的方法名称
    String callback();
}

好了前期准备的差不多了,到核心技术了,不要眨眼

public class InitLayout {

    public static void inject(Activity activity) {
        injectLayout(activity);
        injectViews(activity);
        injectEvents(activity);
    }

    private static void injectViews(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        //获取到该方法下的变量(全部,不论访问权限)
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //获取到注解 InjectViews
            InjectViews injectViews = field.getAnnotation(InjectViews.class);
            if (injectViews != null) {
                //获取到注解中的布局ID
                int viewid = injectViews.viewRes();

//               View view = activity.findViewById(viewid)  //这个是方式之一,但是不够档次
                try {
                    //获取指定类 (getMethod 第一个是方法名 第二个是参数的集合 可以用Object[])
                    Method method = clazz.getMethod("findViewById", int.class);
                    //执行getMethod获取到的方法
                    Object view = method.invoke(activity, viewid);
                    //给属性field赋值
                    field.set(activity, view);
                    //设置访问权限
                    field.setAccessible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }

    /**
     * 设置点击事件
     *
     * @param activity
     */
    private static void injectEvents(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        //获取到所有public的方法,包括父类
        Method[] methods = clazz.getMethods();

        //遍历方法,拿到有自己注解的方法
        for (Method method : methods) {
            //一个方法有多个注解
            Annotation[] annotations = method.getAnnotations();
            /*
                遍历注解,拿到自己定义的注解
                其实也可以这样拿到 某个注解 -》ContentView contentView = clazz.getAnnotation(ContentView.class);
                但是这样会写死,只支持方法上一种注解
                遍历所有注解,然后拿到每个注解上带有ClickBase注解的 注解 真是妙哉妙哉妙哉
                后面只要写各种注解上使用ClickBase注解就可以
             */
            for (Annotation annotation : annotations) {
                //获取到注解上面的注解
                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (annotationType != null) {
                    try {
                        //拿到方法注解上的注解
                        ClickBase clickBase = annotationType.getAnnotation(ClickBase.class);

                        if (clickBase != null) {
                            //拿到ClickBase的三个重要的值
                            String callbackStr = clickBase.callback();
                            String listenerStr = clickBase.listener();
                            Class<?> listenerType = clickBase.listenerType();
                            //Aop
                            ClickListenerHandler handler = new ClickListenerHandler(activity);
                            //把方法都添加到ClickListenerHandler中
                            handler.addMethod(callbackStr, method);
                            //实现代理
                            Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler);

                            /*
                                  获取ids的也是可以这样写
                                  OnClick onClick = (OnClick) annotation;
                                  int[] ids = onClick.viewIds();
                                  但是这样又写死了
                             */

//                            //获取控件id 的方法
                            Method valueMethod = annotationType.getDeclaredMethod("viewIds");
//                            //执行获取id 的方法,拿到id列表
                            int[] ids = (int[]) valueMethod.invoke(annotation);


                            //遍历控件id列表
                            for (int id : ids) {
                                View view = activity.findViewById(id);
                                //获取到设置监听的方法
                                Method clickListener = view.getClass().getMethod(listenerStr, listenerType);

                                clickListener.invoke(view, listener);
                            }
                        }

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

        }


    }

    private static void injectLayout(Activity activity) {
        //拿到activity的反射
        Class<? extends Activity> clazz = activity.getClass();
        //拿到反射类的注解
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if (contentView != null) {
            //拿到注解的值
            int layout = contentView.layoutRes();
            try {
                //获取指定类 (getMethod 第一个是方法名 第二个是参数的集合 可以用Object[])
                Method method = clazz.getMethod("setContentView", int.class);
                //执行getMethod获取到的方法
                method.invoke(activity, layout);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}

好了,这个就是核心代码了

发现了没,我们要设置事件的时候,就要引入一个东西 Aop
用注解的方法,去替掉回调的方法,nice 看懂了么,不懂没关系,我是不会多说的,耗子尾汁

先看看InvocationHandler实现类

public class ClickListenerHandler implements InvocationHandler {

    //执行方法的对象
    private Object target;

    //替换掉回调的方法
    private HashMap<String, Method> methodHashMap = new HashMap();

    public ClickListenerHandler(Object target) {
        this.target = target;
    }

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

    public void addMethod(String methodName,Method method){
        methodHashMap.put(methodName,method);
    }
}

行了,代码上了,然后扩展吧

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ClickBase(listener = "setOnLongClickListener" ,callback = "onLongClick",listenerType = View.OnLongClickListener.class)
public @interface OnLongClick {

    int[] viewIds();
}

剩下的自己去鸟补吧

相关文章

  • 写给自己的IoC

    IoC是什么呢?我自己也整不明白,我怎么解释,自己百度去吧 今天干啥,那就弄个注解加载布局加载view 设置监听吧...

  • 理解Spring IOC

    TODO:未来按自己的思路和理解再整理 IOC概念 阅读:Spring的IOC原理 Spring中IOC实现 阅读...

  • Spring学习

    IOC (接口) 1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂; 2、Spring提供的IOC容器...

  • 向您图文并茂生动讲解Spring AOP 源码(2)

    前言 往期文章: Spring IoC - Spring IoC 的设计 Spring IoC - IoC 容器初...

  • 向您图文并茂生动讲解Spring AOP 源码(2)

    前言 往期文章: Spring IoC - Spring IoC 的设计 Spring IoC - IoC 容器初...

  • 向您生动地讲解Spring AOP 源码(3)

    前言 往期文章: Spring IoC - Spring IoC 的设计 Spring IoC - IoC 容器初...

  • SpringIOC AOP

    IOC:通过IOC容器来管理所有对象的生命周期,将对象的声明周期交由IOC容器进行管理,不需要自己手动new对象,...

  • Java 面试问题系列三(Spring)

    1、Spring 主要思想是什么,回答 IOC 和AOP,怎么自己实现 AOP ? IOC 的好处 使用基于反射的...

  • Spring小结

    1、IOC 容器 1、概念 IOC为Inversion of control,控制反转,意思是现在需要对象,自己不...

  • spring整理

    ioc 什么是ioc? 1.IOC是Inversion of Control的缩写,翻译为控制反转。ioc是容器,...

网友评论

      本文标题:写给自己的IoC

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