在1.4版本之前,Blade有一个很大的弊端,就是注入源和注入目标有耦合。其中注入目标必须有一个@Module
的注解,这个导致同一个注入目标不能被复用。我举一个简单的例子:
![](https://img.haomeiwen.com/i9124992/34b49db11b6e3de7.png)
这两个页面有一部分相同的内容需要加载,所以对于注入目标来说,数据提供者(数据源)都是同一个。但是在1.4版本之前,每个注入目标必须带一个
@Module
注解,会导致在不同的界面,针对相同的一部分内容,会有一部分代码会被重新的书写,这种问题非常不好。而在1.4版本开始,去掉了
@Module
注解,如此就不会死板从一个注入源里面去寻找数据,换句话说,在注入的时候,我们给的是哪个数据源,注入时就从哪个数据源去寻找数据。而1.4版本是怎么实现的呢?接下来我们将简单的看一下整个Blade实现原理。基本使用请参考:Blade - 基本使用
1. Provider接口
从1.4版本开始新增了一个接口--Provider
接口,在编译时,每个Context都会对应生成一个叫Context类名ProviderImpl
名字的类,这个类实现了Provider
接口。然后在这个生成类里面,进行了数据的存储,主要是将对应的Context数据放在这个生成类的一个HashMap成员中。例如,如下的Context
:
public class Context {
@Provides(deepProvides = true, value = "contextString1")
public Demo string1 = new Demo();
@Provides(value = "contextString2")
public String string2;
@Provides(value = "int")
public int code = 2;
}
对应生成的ProviderImpl类是:
public class ContextProviderImpl implements Provider {
private Map<String, Object> pathMap;
public ContextProviderImpl() {
pathMap = new HashMap<>();
}
@Override
public Object find(String id) {
return pathMap.get(id);
}
@Override
public void put(Map<String, Object> map) {
pathMap.putAll(map);
}
public void init(Context source) {
pathMap.put("contextString1", source.string1);;
pathMap.put("contextString2", source.string2);;
pathMap.put("strings", source.string1.string3.strings);;
pathMap.put("demoDemoString", source.string1.string3.demo.demoDemoString);;
pathMap.put("demoString", source.string1.string3);;
pathMap.put("com.example.pby.injectdemo.demo.Demo", source.string1.string3.demo);;
pathMap.put("demo3String", source.string1.string3.string);;
pathMap.put("java.lang.String[]", source.string1.string3.string1);;
pathMap.put("int", source.code);;
}
}
在生成的ProviderImpl
类中,主要实现了Provider
的两个方法--put
方法和find
方法。
其中put
方法表示可以将我们自定义的某些数据存在一个Map
里面,然后在注入的时候可以从这个数据源来寻找数据。这样做的好处就是,一个注入目标没必要再次定义一个Context
类,只需要在基类里面定义一个Context
类就行了,然后子类只需要维护一个Map
数组就OK了。
find
方法在这里就没必要解释了,很明显就是从存储数据的Map去寻找数据,这个方法在生成的Inject
类里面会被调用。
在生成的ProviderImpl
类中,还有一个方法比较重要,那就是init
方法。这个方法主要将Context
里面的数据放在Map
数组里面去,需要注意的是,这里进行了递归,也就是说将子节点的数据也放在了Map
数组里面的。
2. Inject类
在整个框架当中,每个注入目标都会生成一个Inject
类,而对注入目标的数据初始化就是这个Inject
类里面完成的。我们来看看一个例子:
public class MainActivity_Inject {
public void inject(MainActivity target, Provider source) {
target.strings = (java.lang.String[])(source.find("strings"));
target.demoDemoString = (java.lang.String)(source.find("demoDemoString"));
target.pby = (java.lang.String)(source.find("pby"));
target.string1 = (java.lang.String)(source.find("demo3String"));
target.strings1 = (java.lang.String[])(source.find("java.lang.String[]"));
target.code = (int)(source.find("int"));
}
}
在1.4版本中,彻底的重构了Inject
类的结构。在上面的例子当中,我们会发现数据源参数直接是一个Provider
接口,初始化代码调用的是Provider
接口的find
方法。所以从这里,我们可以看出来,在1.4版本中,Context
不再持有数据,进而让Provider
接口的实现类来持有数据。
3. Blade类
Blade
类也进行了重构,其中,重构之后的代码如下:
public class Blade {
public static void inject(Object target, Object source) {
inject(target, source, null);
}
private static void inject(Object target, Map<String, ?> extraMap) {
inject(target, new EmptyProviderImpl(), extraMap);
}
public static void inject(Object target, Object source, Map<String, ?> extraMap) {
try {
Object targetObject = Class.forName(target.getClass().getName() + "_Inject").newInstance();
Provider sourceObject = (Provider) Class.forName(source.getClass().getName() + "ProviderImpl").newInstance();
if (extraMap != null && !extraMap.isEmpty()) {
sourceObject.getClass().getMethod("put", Map.class).invoke(sourceObject, extraMap);
}
sourceObject.getClass().getMethod("init", source.getClass()).invoke(sourceObject, source);
targetObject.getClass().getMethod("inject", target.getClass(), Provider.class).invoke(targetObject, target, sourceObject);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
在Blade
类里面主要做了如下几件事:
- 根据传递进来的注入源找到对应的
ProviderImpl
类, 然后创建它的对象,初始化数据。- 根据传递进来的注入目标找到对应的
Inject
类,然后调用它的inject
方法,同时将ProviderImpl
对象传递进去。
经过如上两步就完成了整个注入过程。
网友评论