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

本文意于让人简单地使用注解+反射,用注解让控件实现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();

    }
}


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

本文源码


### 回答1: Spring框架通过使用依赖注入(DI)来实现解耦。DI允许外部实体在构造函数,字段或者集合属性注入相应的依赖,从而使得类的实例化更加简单,代码更加清晰,维护更加容易。Spring框架实现DI的关键有三个:控制反转(IoC)、面向切面编程(AOP)和依赖查找(DL)。 ### 回答2: Spring框架是一个开源的Java应用开发框架,它使用依赖注入的原理来管理对象之间的依赖关系。所谓依赖注入,就是让程序员不再需要手动创建和管理对象之间的关系,而是由框架来自动完成。 在Spring框架中,我们首先需要定义好我们的Java类,声明它们之间的依赖关系。我们可以使用注解的方式,在需要依赖的属性或者构造方法上加上注解,告诉框架这个属性或者参数需要注入一个对象。 当我们启动程序的时候,Spring框架会根据我们的配置信息,遍历所有的Java类,解析其中的注解信息。然后会根据这些信息创建一个对象的实例,并且将需要注入的属性或者参数自动赋值。这个过程是通过Java的反射机制来实现的。 具体来说,Spring框架会根据注解上的信息,找到合适的对象实例,然后通过调用对象的构造方法或者设值方法,将实例注入到被依赖的属性或者参数中。这样,我们就完成了对象之间的依赖关系的建立,可以方便地使用它们进行开发和业务处理。 借助依赖注入,我们不再需要手动创建和管理对象之间的依赖关系,大大简化了对象之间的耦合度。我们只需要关注对象的功能实现,而不需要过多关心它的依赖关系。这样可以提高开发效率,同时也方便了程序的维护和修改。 ### 回答3: Spring框架是一个用于简化Java开发的框架,其中的依赖注入是其中的一个核心特性。 依赖注入是将对象之间的依赖关系交由框架来管理,而不是由开发人员手动创建和管理。在Spring中,依赖注入是通过配置文件或注解的方式来实现的。 首先,需要将要注入的类所对应的bean配置为一个Spring的bean,这样框架就能够管理这个对象的生命周期。配置文件通常是一个XML文件,其中包含了对Bean的定义和属性的设置。 接下来,需要在需要注入的类中声明需要注入的属性,并为这些属性提供setter方法。Spring框架在启动时会扫描配置文件,找到需要注入的类,并创建对应的对象。 当需要使用某个对象时,Spring会自动将需要注入的属性通过反射的方式注入到对象中,而不需要开发人员手动创建和设置依赖关系。 通过注入,对象之间的依赖关系被解耦,每个对象只需要关注自己的业务逻辑,而不需要关心如何获取依赖的对象。这样可以提高代码的可维护性和可测试性,并且减少了对象之间的紧耦合。 总的来说,Spring框架的依赖注入是通过配置文件或注解的方式来管理对象之间的依赖关系,框架会自动将需要注入的属性注入到对象中。这样可以简化开发过程,提高代码的可维护性和可测试性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值