初谈Android-Annotations(二)

一、前言

先给大家推荐一款非常强大的 Android-注解库 提高效率的神器,在 github 上的火热程度也值得你拥有。它是基于反射来实现的,有一句话说得好:反射,反射,程序员的快乐,那么反射是怎么让程序员快乐的呢?

kotlin rxJava 的火爆让我意识到牛逼的程序员每天都在思考怎么样用 最简洁的代码实现最繁琐的功能 以此来提高工作效率,在此强烈的推荐 :

androidannotations+kotlin+RxJava

二、事件注入

本篇是基于运行时的注入(RUNTIME),比较简单的实现;而 androidannotations 基于编译时的实现(CLASS)的注入。后期提供编译时的实现方式。

先来看看效果:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ClickUtils.injectClick(this);//注入点击事件
    }
    @Click
    void btn() {
        Toast.makeText(this, "hello word", Toast.LENGTH_LONG).show();
    }
}

inject

怎么这么熟悉呢,这不正是 ButterKnife 框架里的点击事件吗? ButterKnife 也是基于编译时的注入方式。

注入实现

(1)、事件注解类(Click)

@Target(ElementType.METHOD)//针对方法
@Retention(RetentionPolicy.RUNTIME)//运行时
public @interface Click {
    int[] value() default {-1};
}

默认值值为 -1 ,为 -1 时自动匹配(R.id.+方法名)的控件ID,如

    @Click
    void btn()

匹配 R.id.btn 的控件ID

(2)、注入实现类(ClickUtils)

扫描二维码关注公众号,回复: 1552351 查看本文章
        Class<?> cls = activity.getClass();

        Method[] methods = cls.getDeclaredMethods();

首先是获取当前 Class 所申明的方法,包含了(私有的,保护的,共有的)方法。注意与

        cls.getMethods(); //自身及父类的共有方法

的区别。

接着是获取每个方法的注解并获取注解值。

            Click clickInject = method.getAnnotation(Click.class);

            if (clickInject != null) {

               int[] clickIds = clickInject.value();

               }

然后获取 ActivityfindViewById 方法:

    Method findViewMethod = cls.getMethod("findViewById", int.class);

然后使用代理类,来实现 View.OnClickListener.class 来实现点击的接口:

Object proxy = Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{View.OnClickListener.class}, new DynaHandler(activity, method));


public static class DynaHandler implements InvocationHandler {

        Object target = null;

        Method mMethod = null;

        public DynaHandler(Object target, Method method) {
            this.target = target;
            mMethod = method;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            this.mMethod.setAccessible(true);
            return this.mMethod.invoke(target); 
        }
    }

this.mMethod.invoke(target); 相当于:

setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //调用注入的方法
            }
        });

如果你对代理类不了解,请参考:

java的动态代理机制详解

接着是处理默认值 -1 的情况:

    for (int id : clickIds) {
        if (id == -1) {  //默认查找 R.id+方法名称 的Id 是否存在
            Class idClass = R.id.class; //获取到 ID 类
            Field field = idClass.getField(method.getName());
            if (field == null) {
                throw new NoSuchFieldException(method.getName() + " id Non-existent");
            }
            id = field.getInt(idClass); //获取ID 值
        }

最后是调用 setOnClickListener 方法:

        Object view = findViewMethod.invoke(activity, id);
        Method clickMethod = view.getClass().getMethod("setOnClickListener", View.OnClickListener
                .class);
        clickMethod.invoke(view, proxy);

下面是 ClickUtils 类的全部代码:

public class ClickUtils {

    public static void injectClick(Activity activity) {

        Class<?> cls = activity.getClass();

        Method[] methods = cls.getDeclaredMethods();


        for (Method method : methods) {

            Click clickInject = method.getAnnotation(Click.class);

            if (clickInject != null) {

                int[] clickIds = clickInject.value();

                try {
                    Method findViewMethod = cls.getMethod("findViewById", int.class);

                    Object proxy = Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{View.OnClickListener.class}, new DynaHandler(activity, method));

                    for (int id : clickIds) {

                        if (id == -1) {  //默认查找 R.id+方法名称 的Id 是否存在

                            Class idClass = R.id.class;

                            Field field = idClass.getField(method.getName());

                            if (field == null) {
                                throw new NoSuchFieldException(method.getName() + " id Non-existent");
                            }

                            id = field.getInt(idClass);

                        }

                        Object view = findViewMethod.invoke(activity, id);

                        Method clickMethod = view.getClass().getMethod("setOnClickListener", View.OnClickListener
                                .class);

                        clickMethod.invoke(view, proxy);
                    }

                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public static class DynaHandler implements InvocationHandler {

        Object target = null;

        Method mMethod = null;

        public DynaHandler(Object target, Method method) {
            this.target = target;
            mMethod = method;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            this.mMethod.setAccessible(true);
            return this.mMethod.invoke(target);
        }
    }
}

基于运行时的事件注入就完成了,那么怎么去实现编译时的注入呢?

由于编译时的实现篇幅太长,接着会分几篇来讲解。

三、结语

一路有你们的陪伴

期待你们的期待

一同成长

猜你喜欢

转载自blog.csdn.net/u012551350/article/details/73799751