在1.4版本之前,Blade有一个很大的弊端,就是注入源和注入目标有耦合。其中注入目标必须有一个@Module
的注解,这个导致同一个注入目标不能被复用。我举一个简单的例子:

这两个页面有一部分相同的内容需要加载,所以对于注入目标来说,数据提供者(数据源)都是同一个。但是在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
对象传递进去。
经过如上两步就完成了整个注入过程。
原著是一个有趣的人,若有侵权,请通知删除
还没有人抢沙发呢~