什么是IOC?

IOC框架称为控制控制反转框架也称为依赖注入框架,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。

IOC相关概念

在写一个框架之前,我们需要了解下IOC的相关概念。
简单来说,对象和IOC容器的关系就像电脑和外设之间的关系,其中电脑相当于对象,而IOC容器相当于一个个不同的电脑外设。外设具有不同的功能,并且他们和电脑之间都遵循着某种协议(如USB2.0协议),因此它们在彼此独立的同时却又能相互联系。
输入功能原本属于电脑的,但是外设键盘却能通过电脑提供的相关接口来控制电脑的输入功能,这就是IOC概念里面的控制反转。由于电脑和外设间都遵循着USB协议,键盘便可以实现随意切换,却不影响键盘的输入功能,这就是IOC概念里面的依赖注入。

电脑和外设

好了,了解完IOC协议后,便可以开始绘制相应的架构图了。

IOC框架图

IOC框架图

这是我用StartUml绘制的UML图。

如这个图所示,整个IOC框架可以分为5个部分
1、ModuleListener,IOC的核心接口,相当于电脑的USB协议
2、AbsModule,这就是IOC容器,IOC容器功能都在这个类里面实现
3、IOCProxy,对象的静态代理,相当于电脑的USB接口
4、ModuleFactory,IOC容器的享元工厂,用于创建IOC容器
5、AbsActivity,具体的对象

有了概念,有了图纸,现在终于可以愉快的码代码了

功能实现

ModuleListener

如上文所说的那样,ModuleListener是整个IOC框架的IOC协议,它本质上就是一个接口,定义了几个方法,仅此而已,是的,所谓的IOC协议就是那么简单。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface ModuleListener {
/**
* 无参的回调
*
* @param method 方法名
*/
public void callback(String method);

/**
* 带参数的回调
*
* @param method 方法名
* @param dataClassType 参数类型
* @param data 数据
*/
public void callback(String method, Class<?> dataClassType, Object data);

/**
* 统一接口的回调,回调接口为dataCallback
*
* @param result 返回码
* @param data 回调数据
*/
public void callback(int result, Object data);
}

AbsModule

AbsModule,这就是IOC框架的IOC容器,IOC容器的功能都应该在它的子类里面实现。
AbsModule依赖于ModuleListener接口,它持有ModuleListener的引用。其实说白了它就是一个观察者对象,仅此而已….
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class AbsModule {
public String TAG = "";
private Context mContext;
private ModuleListener mModuleListener;

public AbsModule(Context context) {
mContext = context;
init();
}

/**
* 初始化一些东西
*/
private void init() {
String className = getClass().getName();
String arrays[] = className.split("\\.");
TAG = arrays[arrays.length - 1];
}

public void setModuleListener(ModuleListener moduleListener) {
if (moduleListener == null)
throw new NullPointerException("ModuleListener为空");
this.mModuleListener = moduleListener;
}

public Context getContext() {
return mContext;
}

/**
* 统一的回调
*
* @param result 返回码
* @param data 回调数据
*/
protected void callback(int result, Object data) {
mModuleListener.callback(result, data);
}

/**
* module回调
*
* @param method 回调的方法名
*/
@Deprecated
protected void callback(String method) {
mModuleListener.callback(method);
}

/**
* 带参数的module回调
*
* @param method 回调的方法名
* @param dataClassType 回调数据类型
* @param data 回调数据
*/
@Deprecated
protected void callback(String method, Class<?> dataClassType, Object data) {
mModuleListener.callback(method, dataClassType, data);
}
}

IOCProxy

类如其名,IOCProxy本身就是一个静态代理,代理本身其实很简单,说白了,代理就是我们平时所说的“黑中介”,在这里,我们的代理仅仅只实现了两个功能:
1、给IOC对象设置观察者,这个观察者是继承于AbsModule的子类。
2、中转AbsModule回调的数据,将数据再次回调到AbsActivity的相应接口。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class IOCProxy implements ModuleListener {
private static final String TAG = "IOCProxy";
private static final String mMethod = "dataCallback";

private Object mObj;

/**
* 初始化静态代理
*/
public static IOCProxy newInstance(Object obj, AbsModule module) {
return new IOCProxy(obj, module);
}

/**
* 被代理对象
*/
private IOCProxy(Object obj, AbsModule module) {
this.mObj = obj;
if (module != null) {
module.setModuleListener(this);
}
}

/**
* 动态切换不同的观察者
* @param module
*/
public void changeModule(AbsModule module) {
module.setModuleListener(this);
}

/**
* 统一的数据回调
*
* @param result 返回码
* @param data 回调数据
*/
@Override
public void callback(int result, Object data) {
synchronized (this) {
try {
Method m = mObj.getClass().getDeclaredMethod(mMethod, int.class, Object.class);
m.setAccessible(true);
m.invoke(mObj, result, data);
} catch (NoSuchMethodException e) {
Log.e(TAG, "无法找到" + mMethod + "方法");
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

/**
* 数据回调
*
* @param method 方法名
*/
@Override
@Deprecated
public void callback(String method) {
synchronized (this) {
try {
Method m = mObj.getClass().getDeclaredMethod(method);
m.setAccessible(true);
m.invoke(mObj);
} catch (NoSuchMethodException e) {
Log.e(TAG, "无法找到" + method + "方法");
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

/**
* 带参数的回调
*
* @param method 方法名
* @param dataClassType 参数类型,如 int.class
* @param data 数据
*/
@Override
@Deprecated
public void callback(String method, Class<?> dataClassType, Object data) {
synchronized (this) {
try {
Method m = mObj.getClass().getDeclaredMethod(method, dataClassType);
m.setAccessible(true);
m.invoke(mObj, data);
} catch (NoSuchMethodException e) {
Log.e(TAG, "无法找到" + method + "方法");
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}

在这个类里,博主根据以前的需求写了几种不同的代理回调。
但是,我强烈建议你使用带有返回码的统一回调接口,该方法规定了AbsActivity的回调函数,实现了数据流的统一,一致性总归是好的,便于我们后期维护。

ModuleFactory

ModuleFactory是一个享元工厂,IOC容器的构建都是由它来完成了,享元实现不同对象的容器之间的共享。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class ModuleFactory {

private static final String TAG = "ModuleFactory";

private static Map<String, AbsModule> mModules = new HashMap<>();

/**
* 获取Module
*/
protected static <T extends AbsModule> T getModule(Context context, Class<T> clazz) {
T module = (T) mModules.get(String.valueOf(clazz.hashCode()));
if (module == null) {
return newInstanceModule(context, clazz);
}
return module;
}

/**
* 构造一个新的Module
*/
private static <T extends AbsModule> T newInstanceModule(Context context, Class<T> clazz) {
Class[] paramTypes = {Context.class};
Object[] params = {context};
try {
Constructor<T> con = clazz.getConstructor(paramTypes);
T module = con.newInstance(params);
mModules.put(String.valueOf(clazz.hashCode()), module);
return module;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}

AbsActivity

写了这么久,终于快到尾声了…
AbsActivity是IOC框架的对象,Activity也没啥好说的,大家都懂的,直接放代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public abstract class AbsActivity extends AppCompatActivity {
private static IOCProxy mProxy;
private IOCProxy mNewProxy;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNewProxy = IOCProxy.newInstance(this, null);
}

/**
* 利用反射来对代理进行重指向
*/
private void setProxy() {
mProxy = mNewProxy;
}

/**
* 获取Module
*/
protected static <T extends AbsModule> T getModule(AbsActivity activity, Class<T> clazz) {
Method m = ReflectionUtil.getMethod(activity.getClass(), "setProxy", new Class[]{});
try {
m.invoke(activity);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
T module = ModuleFactory.getModule(activity, clazz);
mProxy.changeModule(module);
return module;
}

/**
* 统一的回调接口
*
* @param result 返回码,用来判断是哪个接口进行回调
* @param data 回调数据
*/
protected abstract void dataCallback(int result, Object data);
}

总结

到现在为止,框架核心部分已经完成了,现在让我们整理下整个框架到底是怎样运作的!
老规矩,还是用图来说明,没什么比图更让人易懂的。
下图是整个框架的流程图:
框架流程图
图不难懂,我就不废话了,我直接给出例子就行了,大家都是码农,我相信没几个码农喜欢看文字的,都喜欢看代码直接干的。。

例子

Module1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import android.content.Context;

import com.example.yuyu.blogframedemo.frame.AbsModule;

/**
* Created by yuyu on 2015/9/6.
*/
public class Module1 extends AbsModule{
public Module1(Context context) {
super(context);
}

public void module1Test(){
callback(100, "我是Module1111111");
}

public void cusomCallback(){
callback("myCallback", String.class, "我是Module11的自定义回调......");
}
}

Module2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import android.content.Context;

import com.example.yuyu.blogframedemo.frame.AbsModule;

/**
* Created by yuyu on 2015/9/6.
*/
public class Module2 extends AbsModule{
public Module2(Context context) {
super(context);
}

public void module2Test(){
callback(101, "我是Module22222222");
}
}

MainActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.example.yuyu.blogframedemo.R;
import com.example.yuyu.blogframedemo.frame.AbsActivity;

/**
* Created by yuyu on 2015/9/6.
*/
public class MainActivity extends AbsActivity{

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

/**
* 回调到自定义的方法
* @param data
*/
private void myCallback(String data){
show(data);
}

/**
* 数据回调,当然你也可以回调到指定的方法里面
* @param result 返回码,用来判断是哪个接口进行回调
* @param data 回调数据
*/
@Override
protected void dataCallback(int result, Object data) {
show(String.valueOf(data));
}

public void onClick(View view){
switch (view.getId()){
case R.id.module1:
getModule(this, Module1.class).module1Test();
break;
case R.id.module2:
getModule(this, Module2.class).module2Test();
break;
case R.id.custom_module:
getModule(this, Module1.class).cusomCallback();
break;
}
}

private void show(String msg){
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}

效果图


DEMO

IOC升级版,MVVM框架(实现了6.0权限封装、不需要xml的填充布局、module注入)