使用反射+注解,教你学会最简单的依赖注入

本文意于让人简单地使用注解+反射,用注解让控件实现findViewById()以及setOnClickListener(),不再重复写findViewById这些重复性的代码。

网上已经有很多这种框架,比如xUtils,Butterknife等,浅略地看了这两个框架的源码,前者使用的是注解和反射的方式,后者使用的是编译期生成一些代码,可以看Android Studio中的app/build/generated/source/apt,这里都是编译后,框架自动生成的代码。而这两种均能实现这些功能。区别在于,前者使用的是注解+反射,可能会一定程度影响性能,但是,在这个手机性能过剩的时代,这种影响真的可以忽略了。而后者可能在编译期会消耗一些性能,但是运行的时候不会有影响。

以上,均是个人理解。如有不同意见,可以留言一起探讨一下。

一、首先先看一个炒鸡简陋的界面。


因为只是为了能使用,就没有弄一些比较花哨的界面,只要功能实现了就好了。

打开这个界面的时候,在onStart()生命周期打印了按钮1的文本,证明此时按钮1已经是绑定成功了。


接下来是点击按钮二,只是简单的吐司一下。


这就是要讲的全部了,主要不在于界面,而是怎么去实现这个。


二、自定义注解。

关于Java的注解,网上的教程很多,在这里不再论述了,而且讲得不好倒坏了事了。

首先是控件绑定的注解类(就是代替findViewById()方法的注解)

/**
 * 描述:用于注解成员的注解类
 * 包名:xiedroid.didemo.utils
 * 类名:ViewInject
 *
 * @author XieQingXiong
 */
@Target(ElementType.FIELD)//标识这个注解类只能注解字段
@Retention(RetentionPolicy.RUNTIME) //表示这个注解在运行时期起作用
public @interface ViewInject {
    // 注解里面的值,为 int 类型,用value表示是默认的
    // 比如:使用这个注解的时候
    // @ViewInject(R.id.btn1),R.id.btn1是int值,可以不用 @ViewInject(value=R.id.btn1)的形式。
    // 如果不是  int value()  ,而是 int name() 的话,则使用注解时就要用 @ViewInject(name=R.id.btn1)
    // 所以说 value是一个默认的值
    int value();
}

关于这个注解类的说明,注释我觉得已经讲清楚了。如果还不清楚的话,Google(百度也行)吧。

这个注解的使用是

@ViewInject(R.id.btn1)
private Button btn1;

这样就是绑定一个Button控件了。


接下来就是控件点击事件的注解类(就是setOnClickListener()方法的注解)

/**
 * 描述:点击事件的注解类
 * 包名:xiedroid.didemo.utils
 * 类名:OnClick
 *
 * @author XieQingXiong
 */
@Target(ElementType.METHOD)//只能使用方法上
@Retention(RetentionPolicy.RUNTIME)//运行期起作用作用
public @interface OnClick {
    int value();
}

使用方法

@OnClick(R.id.btn2)
public void btn2Click(View v){
    //处理逻辑

}


三、控件的绑定与事件的绑定

完成注解类之后,接下来就是控件的各种绑定操作了。

首先,需要进行控件绑定的话,需要在Activity的onCreate()方法中调用一个用于绑定操作的方法

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    InjectUtils.inject(this);
}

绑定的操作是在InjectUtils类中执行的,调用的jnject()方法

public static void inject(Activity activity) {
    //绑定控件
    bindView(activity);
    //点击事件的绑定
    bindEvent(activity);
}

在这个方法里调用了两个方法,分别是绑定控件跟绑定事件。

先来看控件的绑定

private static void bindView(Activity activity) {
    //获取class
    Class<? extends Activity> clazz = activity.getClass();
    //获取到这个类的所有的成员字段
    Field[] declaredFields = clazz.getDeclaredFields();
    //遍历所有的字段
    for (int i = 0; i < declaredFields.length; i++) {
        //判断这个成员字段中有没有ViewInject这个注解
        ViewInject viewInject = declaredFields[i].getAnnotation(ViewInject.class);
        //如果没有这个注解,跳过这个成员字段
        if (viewInject == null)
            continue;
        //获取注解的值,在这里就是控件的id
        int resId = viewInject.value();
        //得到这个控件
        View view = activity.findViewById(resId);
        try {
            //暴力反射,如果不设置这个,那么如果成员是private的话,就不能进行绑定
            declaredFields[i].setAccessible(true);
            //将这个控件跟activity绑定
            declaredFields[i].set(activity,view);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

该方法的整体思路是:

1、获取到这个Activity中所有的成员字段(Field) 

2、然后遍历每个字段,看是否有对应的 ViewInject 这个自定义注解存在。

3、如果存在这个注解,获取到这个注解里面的值(控件id),并根据该值找到当前的控件(View)。

4、将这个View跟Activity绑定。

这样就完成了Activity上所有带自定义注解(ViewInject)的控件的绑定。


点击事件的绑定

private static void bindEvent(final Activity activity) {
    Class<? extends Activity> clazz = activity.getClass();
    //获取所有的方法
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (final Method method : declaredMethods) {
        //获取方法上的OnClick注解
        OnClick onClick = method.getAnnotation(OnClick.class);
        if (onClick == null)
            continue;
        //获取控件的id
        int resId = onClick.value();
        final View view = activity.findViewById(resId);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    //暴力反射
                    method.setAccessible(true);
                    method.invoke(activity,view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

思路:

1、找到所有的方法。

2、遍历方法,看是否有自定义注解(OnClick)存在。

3、存在这个注解,获取到注解里面的值,找到对应的控件(View)。

4、调用这个View的setOnClickListener()方法,在这个监听方法里面调用Activity中有@OnClick注解的方法,这样就实现了点击事件的转换。

(注:直接在这里调用setOnClickListener()只是为了可以更清楚的理解,真正要做得更好是不会这么写死的。可能会用动态代理来实现。)


以上就完成了控件的绑定跟点击事件的绑定。

四、最后再瞄一眼ManActivity

public class MainActivity extends AppCompatActivity {

    @ViewInject(R.id.btn1)
    private Button btn1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectUtils.inject(this);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e("xqx", "onStart: 得到按钮的名字 === "+btn1.getText().toString());
    }

    @OnClick(R.id.btn2)
    public void btn2Click(View v){
        Toast.makeText(this, "点击了按钮2", Toast.LENGTH_SHORT).show();

    }
}


这样就完成了一个很简单的依赖注入迷你框架了(请让我装下逼假装说是个框架,勿喷!)。

本文源码


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值