美文网首页
IOC实现一个简单的注解处理器

IOC实现一个简单的注解处理器

作者: 码了个寂寞 | 来源:发表于2021-06-17 11:41 被阅读0次

    首先看一下用法和效果图:
    点击按钮 然后处理注入的事件

    image.png
    具体使用方法类似ButterKnife:
    @ContentView(R.layout.activity_main)
    public class MainActivity extends AppCompatActivity {
    
        @ViewInject(R.id.testTv)
        private TextView testTv;
    
        @ViewInject(R.id.btn)
        private Button btn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 注入
            InjectUtils.inject(this);
            testTv.setText("这是一段很有意思的文字~~~");
            btn.setText("点击有惊喜");
        }
        
        @OnClick({R.id.btn})
        private void btnClickListener(View v){
            Toast.makeText(this, "哎呀,差一点就拿到100w奖金了,再试一次吧~", Toast.LENGTH_SHORT).show();
        }
    }
    

    具体实现

    1、首先先定义几个想要注入事件的注解文件

    • 定义一个ContentView注解
    @Target(ElementType.TYPE) // (Target表示注解用在什么地方) ElementType.TYPE 表示是用在类上的注解
    @Retention(RetentionPolicy.RUNTIME) // 存在于运行期
    public @interface ContentView  {
        int value();
    }
    
    • 定义一个view的初始化注解
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ViewInject {
        int value();
    }
    
    • 然后准备定义一个onClick事件的注解,但是不能像上面那样写了,因为可能还有别的行为事件,如果按上面写法,那么在处理注解的时候就需要手动判断然后再对应处理是点击事件还是长按事件等等了,耦合度较高,所以这里需要定义一个行为事件的父级注解,仔细看会发现一般行为事件分为三部分:
    简单行为事件的结构

    所以父级注解需要有三个字段,以方便后续处理注解的事件:

    @Target(ElementType.ANNOTATION_TYPE) // 用于注解上的注解
    @Retention(RetentionPolicy.RUNTIME) // 存在于运行期
    public @interface EventBase {
    
        /**
         * 设置监听的方法
         * @return
         */
        String listenerSetter();
    
        /**
         * 设置监听的类型
         * @return
         */
        Class<?> listenerType();
    
        /**
         * 事件触发之后的回调方法
         * @return
         */
        String callbackMethod();
    
    }
    
    • 然后再定义自己想要处理的行为事件,这里定义一个onClick事件
    @Target(ElementType.METHOD) // 用于方法上的注解
    @Retention(RetentionPolicy.RUNTIME) // 存在于运行期
    @EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, callbackMethod = "onClick")
    public @interface OnClick {
        int[] value();
    }
    

    2、接下来实现一个InvocationHandler接口
    为什么处理注入事件不直接用InvocationHandler呢,这里做一个优化:

    public class EventInvocationHandler implements InvocationHandler {
    
        private Activity activity;
        private final Map<String ,Method> methodMap;
    
        public EventInvocationHandler(Activity activity,Map<String ,Method> methodMap){
            this.activity=activity;
            this.methodMap=methodMap;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
            Method mtd = methodMap.get(method.getName());
            if(mtd!=null){
                // 如果这个方法不是被public修饰的 就强制改成可被使用的(忽略检查)
                if (!Modifier.isPublic(mtd.getModifiers())) {
                    AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
                        mtd.setAccessible(true);
                        return null;
                    });
                }
                return mtd.invoke(activity,objects);
            }
            return method.invoke(proxy,objects);
        }
    
    }
    

    3、最后就是核心实现了,处理注解的类、变量、函数

    public class InjectUtils {
    
        public static void inject(Activity activity) {
            // 注入布局
            injectLayout(activity);
            // 注入视图
            injectViews(activity);
            // 注入事件
            injectEvents(activity);
        }
    
        /**
         * 注入布局
         *
         * @param activity
         */
        private static void injectLayout(Activity activity) {
            Class<? extends Activity> clazz = activity.getClass();
            ContentView content = clazz.getAnnotation(ContentView.class);
            activity.setContentView(content.value());
        }
    
        /**
         * 注入布局
         *
         * @param activity
         */
        private static void injectViews(Activity activity) {
            Class<? extends Activity> clazz = activity.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field item : fields) {
                ViewInject viewInject = item.getAnnotation(ViewInject.class);
                if (viewInject == null) {
                    continue;
                }
                int id = viewInject.value();
                View view = activity.findViewById(id);
                item.setAccessible(true); // 设置访问权限
                try {
                    item.set(activity, view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static void injectEvents(Activity activity) {
            Class<? extends Activity> clazz = activity.getClass();
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                // 获取方法上的所有注解
                Annotation[] annotation = method.getAnnotations();
                for (Annotation ann : annotation) {
                    Class<? extends Annotation> annotationType = ann.annotationType();
                    EventBase eventBase = annotationType.getAnnotation(EventBase.class);
                    if (eventBase == null) {
                        continue;
                    }
                    // 事件要素
                    Class<?> listenerType = eventBase.listenerType();
                    String listenerSetter = eventBase.listenerSetter();
                    String callbackMethod = eventBase.callbackMethod();
    
                    // 方法拦截的对应关系
                    Map<String,Method> methodMap = new HashMap<>();
                    methodMap.put(callbackMethod,method);
    
                    // 事件源
                    // 通过方法拿到事件源
                    try {
                        Method valueMethod = annotationType.getDeclaredMethod("value");
                        int[] viewIds = (int[]) valueMethod.invoke(ann);
                        if (viewIds == null) {
                            continue;
                        }
                        for (int id : viewIds) {
                            View view = activity.findViewById(id);
                            if (view == null) {
                                continue;
                            }
                            Method setListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);
                            // 如何得到View.OnClickListener监听器的代理对象
                            EventInvocationHandler invocationHandler = new EventInvocationHandler(activity,methodMap);
                            Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class<?>[]{listenerType}, invocationHandler);
                            setListenerMethod.invoke(view,proxy);
                        }
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    
    }
    

    相关文章

      网友评论

          本文标题:IOC实现一个简单的注解处理器

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