IOC-控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。这段百度对IOC框架的解释,对于Java开发者来讲最著名的IOC框架莫过于Spring,而在我们的Android开发中,IOC的使用更为常见,比如大家经常使用的XUtil、butterKnife、EventBus、dagger、dagger2、otto等等,这些第三方库几乎都使用了IOC思想,举个例子给大家:
通常我们在Activity中获取一个图片组件采用如下方法:
ImageView img
img = findViewById(R.id.img)
而使用IOC框架给我提供了一种基于注解的实现方式:
@ViewInject(R.id.img)
ImageView img;
可以看出这种方式似乎更加简洁
其实这正是我本篇博文想给大家介绍的,IOC框架可以:
1.让代码更加简洁
2.让模板式的代码更少,减少重复工作
3.把更多的精力放到业务逻辑上
4.解耦合
下面我给大家详细讲解下如何自定义IOC框架,在Android中我们使用IOC框架更多是为了方便注入所有的控件,比如说布局文件。
自定义注解工具库
整体库结构

定义注解工具类
布局注解
/**
*
* 功能:自定义ContentView注解
* 作者:猴子搬来的救兵
* 博客地址:http://blog.csdn.net/mynameishuangshuai
* 日期:2016年10月14日
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CastielContentViewInject {
int value();
}
组件注解
/**
*
* 功能:自定义View注解
* 作者:猴子搬来的救兵
* 博客地址:http://blog.csdn.net/mynameishuangshuai
* 日期:2016年10月14日
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CastielViewInject {
int value();
事件注解
/**
*
* 功能:自定义OnClick注解
* 作者:猴子搬来的救兵
* 博客地址:http://blog.csdn.net/mynameishuangshuai
* 日期:2016年10月14日
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@CastielEventBase(listenerSetter="setOnClickListener",listenerType=View.OnClickListener.class,callbackMethod="onClick")
public @interface CastielOnClickInject {
int[] value();
}
在定义事件注解类时,我们需要在这个注解的基础上再定义一个注解,用于传递事件调用所需的三个重要元素setOnClickListener;传接口类型;回调方法名字
事件注解基类
public @interface CastielEventBase {
String listenerSetter();
Class listenerType();
String callbackMethod();
}
定义注入工具类
/**
* 功能:InjectUtils注入工具类
* 作者:猴子搬来的救兵
* 博客地址:http://blog.csdn.net/mynameishuangshuai
* 日期:2016/10/13
*/
public class InjectUtils {
public static void inject(Activity activity) {
// 注入布局
injectLayout(activity);
// 注入视图
injectViews(activity);
// 注入事件
injectEvents(activity);
}
private static void injectEvents(Activity activity) {
// 获取方法上面的注解
Class myClass = activity.getClass();
Method myMethod[] = myClass.getDeclaredMethods();// 先拿到全部方法
for (Method method : myMethod) {
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
Class annotationType = annotation.annotationType();
CastielEventBase ceb = annotationType.getAnnotation(CastielEventBase.class);// 拿到注解里面的注解
// 得到事件的三要素
String listenerSetter = ceb.listenerSetter();
Class listenerType = ceb.listenerType();
String callbackMethod = ceb.callbackMethod();
// 获取注解事件的控件对象Button
try {
Method valueMethod = annotationType.getDeclaredMethod("value");
try {
int[] viewIds = (int[])valueMethod.invoke(annotation);
for (int viewId : viewIds) {
View view = activity.findViewById(viewId);
// 反射setOnClickListener方法,这里要用到代理
Method setListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);
Map methodMap = new HashMap();
methodMap.put(callbackMethod, method);
InvocationHandler invocationHandler = new ListenerInvocationHandler(activity, methodMap);
Object newProxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, invocationHandler);
setListenerMethod.invoke(view , newProxyInstance);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
}
private static void injectViews(Activity activity) {
// 获取每一个属性上的注解
Class myClass = activity.getClass();
Field[] myFields = myClass.getDeclaredFields();// 先拿到里面所有的成员变量
for (Field field : myFields) {
CastielViewInject myView = field.getAnnotation(CastielViewInject.class);
if (myView != null) {
int value = myView.value();// 拿到属性id
View view = activity.findViewById(value);
// 将view赋值给类里面的属性
try {
field.setAccessible(true);// 为了防止其实私有的的,需要设置允许访问
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
}
private static void injectLayout(Activity activity) {
// 获取我们自定义类CastielContentViewInject上面的注解
Class myClass = activity.getClass();
CastielContentViewInject myContentView = myClass.getAnnotation(CastielContentViewInject.class);
int myLayoutResId = myContentView.value();
activity.setContentView(myLayoutResId);
}
}
最后封装Activity基类
/**
*
* 功能:封装的Activity基类,引入我们自定义的注入工具类
* 作者:猴子搬来的救兵
* 博客地址:http://blog.csdn.net/mynameishuangshuai
* 日期:2016年10月14日
*/
public class BaseActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.inject(this);
}
}
使用自定义的注解工具库
MainActivity.java
import com.castiel.ioc.activity.BaseActivity;
import com.castiel.ioc.annotation.CastielContentViewInject;
import com.castiel.ioc.annotation.CastielOnClickInject;
import com.castiel.ioc.annotation.CastielViewInject;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
@CastielContentViewInject(R.layout.activity_main)
public class MainActivity extends BaseActivity {
@CastielViewInject(R.id.tv)
private TextView tv;
@CastielViewInject(R.id.btn)
private Button btn;
@CastielOnClickInject({R.id.btn})
public void changText(){
tv.setText("猴子搬来的救兵 http://blog.csdn.net/mynameishuangshuai");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
布局文件
当我点击”按钮”,就可以实现TextView文字的切换。

