什么是IOC?
百度百科介绍:控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
说白了就是依赖注入,降低类之间的耦合度。
具体到底该怎么办呢,接着往下看:
想要学习IOC首先你要具备JAVA注解的知识
注解的语法比较简单,除了@符号的使用之外,它基本与Java固有语法一致。Java SE5内置了三种标准注解:
@Override,表示当前的方法定义将覆盖超类中的方法。
@Deprecated,使用了注解为它的元素编译器将发出警告,因为注解@Deprecated是不赞成使用的代码,被弃用的代码。
@SuppressWarnings,关闭不当编译器警告信息。
上面这三个注解多少我们都会在写代码的时候遇到。Java还提供了4中注解,专门负责新注解的创建。
@Target | 表示该注解可以用于什么地方,可能的ElementType参数有: CONSTRUCTOR:构造器的声明 FIELD:域声明(包括enum实例) LOCAL_VARIABLE:局部变量声明 METHOD:方法声明 PACKAGE:包声明 PARAMETER:参数声明 TYPE:类、接口(包括注解类型)或enum声明 |
@Retention | 表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括: SOURCE:注解将被编译器丢弃 CLASS:注解在class文件中可用,但会被VM丢弃 RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。 |
@Document | 将注解包含在Javadoc中 |
@Inherited | 允许子类继承父类中的注解 |
定义一个注解的方式:
1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface Test {
4
5 }
定义了注解,必然要去使用注解。
public class PasswordUtils {
@UseCase(id = 47, description = "Passwords must contain at least one numeric")
public boolean validatePassword(String password) {
return (password.matches("\\w*\\d\\w*"));
}
@UseCase(id = 48)
public String encryptPassword(String password) {
return new StringBuilder(password).reverse().toString();
}
}
这就是基本的注解使用方式。
接下来进入正题。
要依赖注入,首先我们要定义注解BindView
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {
int value() default -1;
}
这就是我们平时绑定View的注解,在Activity中我们这样使用
使用了注解然后呢,我们需要取得注解的值,并且要跟activity中的button所绑定,
我们县创建一个工具类,InjectUtil,这个类专门绑定注解的绑定。
我们先完成VIew的绑定,这时候就需要一连串的反射,
1.通过反射获取类中的属性
Class<?> aClass = context.getClass();
Field[] fields = aClass.getDeclaredFields();
2.根据属性获取添加BindView所注解的属性
BindView annotation = field.getAnnotation(BindView.class);
3.获取注解属性的值
int value = annotation.value();
4.获取activity中findViewById方法
Method findViewById = aClass.getMethod("findViewById", int.class);
5.findViewById通过传入的属性值获取到View
View invoke = (View) findViewById.invoke(context, value);
6.把值设置给activity
field.set(context, invoke);
这样就把注解的view找和activity所绑定。
然后在activity中添加
就可以完成绑定。
其他的布局的注入,和事件的注入都是如此,直接上代码。
这是一个用来添加在注解上的注解,凡是添加了此注解的注解都可以进行事件绑定
在工具类中添加事件注入的方法
private static void injectListener(Object context) {
Class<?> aClass = context.getClass();
Method[] methods = aClass.getMethods();
for (Method method : methods) {
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
Class<?> listerClass = annotation.annotationType();
BaseListener baseAnnotation = listerClass.getAnnotation(BaseListener.class);
if (baseAnnotation != null) {
String callBack = baseAnnotation.callBack();
String listenerSeter = baseAnnotation.listenerSeter();
Class<?> listenerType = baseAnnotation.listenerType();
try {
Method value = listerClass.getMethod("value");
int[] ids = (int[]) value.invoke(annotation);
for (int id : ids) {
Method findViewById = aClass.getMethod("findViewById", int.class);
View invoke = (View) findViewById.invoke(context, id);
if(invoke==null)
{
continue;
}
//动态代理,为了调用把自己写的接口
ClickInvocationHandler clickInvocationHandler = new ClickInvocationHandler(method,context);
Object o = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, clickInvocationHandler);
Method method1 = invoke.getClass().getMethod(listenerSeter,listenerType);
method1.invoke(invoke, o);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
这里为什么要加一个BaseListener的注解呢,为了方便事件的扩展,比如OnClickListener,OnLongClickListener
只要在定义注解的时候添加BaseListener可以反射到我们想要的方法。
使用该注解,很简单
直接在方发生添加就好。
当然了使用如此多的反射肯定后影响性能,ioc原理就是用反射获取想要的内容,并完成绑定。
但是为什么butterknife就不会影响性能呢,因为他是在编译阶段就生成我们需要的文件,减少反射的代码,下次我们再来聊一聊butterknife是怎么实现的。