Android手动编写ButterKnife编译时注解框架

我们在项目中经常使用ButterKnife注解等框架,那里面的实现原理是什么呢?其实内部原理比较简单,今天就跟大家一起分享一下。

先上效果:

这里写图片描述

这就是我用自己写的编译时注解框架实现的效果。

MainActivity代码:

    @InjectView(R.id.btn1)
    Button btn1;
    @InjectView(R.id.btn2)
    Button btn2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectViewUtils.inject(this);

        btn1.setText("点击我111啊!!!");
        btn2.setText("点击我222啊!!!");
    }

    @OnClick({R.id.btn1, R.id.btn2})
    public void click(View view) {
        switch (view.getId()) {
            case R.id.btn1:
                Toast.makeText(this,"我是按钮11111",Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn2:
                Toast.makeText(this,"我是按钮22222",Toast.LENGTH_SHORT).show();
                break;
        }
    }

可以看到用法都是和ButterKnife一样的。那我们就从上往下看,先看InjectView里面的代码:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
    int value();
}

这里面的代码很简单,主要就是一个接口和注解。解释一下注解。

@Target: @Target 用于指定该注解可以声明在哪些成员上,常见的值有FIELD和METHOD,如果不设置值得话,默认可以添加到任何元素上,但是一般不推荐这样使用。
这里的话我们设置的值是FIELD。

@Retention: 用于声明该注解生效的生命周期,有三个值可选
* 1.RetentionPolicy.SOURCE:注解之保留在源码上,编译成class的时候自动被编译器抹除
* 2.RetentionPolicy.CLASS:注解只留到字节码上,VM加载字节码时自动抹除
* 3.RetentionPolicy.RUNTIME:注解永久保留,可以被VM加载时加载到内存中
这里我们是想VM在运行时对Field上的注解进行反射,所以设置为第三个。

@interface:是声明注解类的组合关键字

然后是OnClick里面的代码:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    int[] value();
}

这里和刚才的都是差不多的,主要是设置@Target的值为METHOD,意思应该都是懂得。

最后是我们的重点InjectViewUtils类,代码如下:

public static void inject(final Activity activity) {
        Class clazz = activity.getClass();
        //通过字节码获取field的时候一定要用getDeclaredField(),只有该方法才能获取到任何权限修饰符的Field
        Field[] field = clazz.getDeclaredFields();

        for (int i = 0; i < field.length; i++) {
            Field f = field[i];
            //设置为可访问,暴力反射,私有也能访问
            f.setAccessible(true);
            //获取到字段的注解对象
            InjectView inject = f.getAnnotation(InjectView.class);
            if (inject == null) {
                continue;//如果该方法上没有注解,循环下一个
            }
            int id = inject.value();//获取注解中的值

            View v = activity.findViewById(id);//获取控件

            try {
                f.set(activity,v);//将控件设置给field对象
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        Method[] method = clazz.getDeclaredMethods();
        for (int i = 0; i < method.length; i++) {
            final Method m = method[i];

            OnClick click = m.getAnnotation(OnClick.class);
            if (click == null) {
                continue;
            }

            int[] value = click.value();
            for (int j = 0; j < value.length; j++) {
                int id = value[j];

                final View v = activity.findViewById(id);

                v.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        try {
                            m.invoke(activity,v);//反射调用用户设定的方法
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }

    }

里面主要就是一个方法,通过反射获取字段的id,然后获取控件,然后做相应的设定,不了解反射的话,可以看下反射的知识,希望对大家有所帮助。

点击下载源码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值