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

2021-06-17  本文已影响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、首先先定义几个想要注入事件的注解文件

@Target(ElementType.TYPE) // (Target表示注解用在什么地方) ElementType.TYPE 表示是用在类上的注解
@Retention(RetentionPolicy.RUNTIME) // 存在于运行期
public @interface ContentView  {
    int value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    int value();
}
简单行为事件的结构

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

@Target(ElementType.ANNOTATION_TYPE) // 用于注解上的注解
@Retention(RetentionPolicy.RUNTIME) // 存在于运行期
public @interface EventBase {

    /**
     * 设置监听的方法
     * @return
     */
    String listenerSetter();

    /**
     * 设置监听的类型
     * @return
     */
    Class<?> listenerType();

    /**
     * 事件触发之后的回调方法
     * @return
     */
    String callbackMethod();

}
@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();
                }
            }
        }
    }


}
上一篇下一篇

猜你喜欢

热点阅读