您的位置:首页 > 其它

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

2017-03-02 16:16 483 查看
本文意于让人简单地使用注解+反射,用注解让控件实现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()方法中调
4000
用一个用于绑定操作的方法

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

}
}


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

本文源码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  依赖注入 注解 反射