一些大型项目每每会有多个module
,随着module
愈来愈复杂,module
间的依赖关系会变得难以维护,一不当心就可能形成循环依赖
,致使项目编译不过。java
有一个Module-A
,里面有一个class A
,在Module B
有一个class B
,若是Module A
须要用到class B
,同时Module-B
又须要用到class A
时,就必须得改变代码结构了。若是直接在gradle里写android
// module A build.gradle
implementation project(":Module-B")
// module B build.gradle
implementation project(":Module-A")
复制代码
在构建时就会看到循环依赖
的错误输出。git
为何不能循环依赖? 在构建前
Gradle
会计算依赖图,至关于对任务进行拓扑排序
,而拓扑排序
是不容许图中有环的,上面的A
和B
显然就构成了一个图中的环,构建没法继续。github
从Module-A
和Module-B
中抽出一个Module-C
,用来装class A
和class B
。造成了这样的依赖关系。api
这看起来是一个好的办法,对于比较简单的好比工具类的处理是不错的。可是若是class A
自己又依赖于不少Module-A
中的文件,是否是也得把以来的文件也一块儿抽出来呢?另外一个问题是,这样作显然违反了分Module的初衷,把本该各自属于Module-A
和Module-B
的功能合到了同一个Module。bash
把class A
和class B
须要的对外提供的功能抽象出接口interface A
和interface B
放在Module-C
中,其实现放在原来module中,经过依赖注入建立A和B的实例。任何module要使用A、B的功能,只须要依赖Module-C
便可。造成以下依赖关系:app
其中,API Factory
就集中管理实例的建立和查询,经过他获取到接口的实例。框架
项目地址:github.com/SirLYC/AppI…ide
在根目录的build.gradle
添加工具
buildscript {
repositories {
//...
jcenter()
}
dependencies {
// ...
classpath "com.lyc:app-inject-plugin:latest.release"
}
}
复制代码
在输出apk文件的module中添加gradle插件:
apply plugin: 'com.android.application'
// 必须在application插件apply后引入
apply plugin: "com.lyc.app-inject-plugin"
复制代码
最后在须要获取接口实例的module添加上这个依赖便可:
dependencies {
// provide Annotations and AppInject API
implementation "com.lyc:app-inject:latest.release"
}
复制代码
固然,一个一个添加嫌麻烦的话,能够直接在一个基础module使用api
引入:
dependencies {
// provide Annotations and AppInject API
api "com.lyc:app-inject:latest.release"
}
复制代码
使用@InjectApi
标记须要依赖注入的接口。
@InjectApi
public interface ISingleApi {
String logMsg();
}
复制代码
oneToMany
参数表示这个接口是否能够有多个实现,oneToMany
不一样,获取接口实例的方式不一样。
@InjectApi(oneToMany = true)
public interface IOneToManyApi {
String logMsg();
}
复制代码
实现使用@InjectApiImpl
标记,同时须要指出实现的父接口是哪个,默认状况是调用这个类的空构造方法构造实例(一个接口只会构建一次实例)。
@InjectApiImpl(api = ISingleApi.class)
public class SingleApiImpl implements ISingleApi {
@Override
public String logMsg() {
return "I'm SingleApiImpl!";
}
}
复制代码
对于oneToMany=true
的接口,容许有多个实现
// 第一个实现
@InjectApiImpl(api = IOneToManyApi.class)
public class OneToManyApiImpl1 implements IOneToManyApi {
@Override
public String logMsg() {
return "I'm OneToManyApiImpl1!";
}
}
复制代码
// 第二个实现
@InjectApiImpl(api = IOneToManyApi.class)
public class OneToManyApiImpl2 implements IOneToManyApi {
@Override
public String logMsg() {
return "I'm OneToManyApiImpl2!";
}
}
复制代码
这个是用kotlin
实现的例子,其中createMethod是实例的建立方法,这个改为了GET_INSTANCE,会调用类的静态方法getInstance
构建,因此在用kotlin
实现时要添加@JvmStatic
注解,不然会找不到方法没法建立这个实例。
// 这是用kotlin实现的例子
// class直接传::classs
@InjectApiImpl(api = IOneToManyApi::class, createMethod = CreateMethod.GET_INSTANCE)
class OneToManyApiImplKt private constructor() : IOneToManyApi {
companion object {
private val instance = OneToManyApiImplKt()
// important!
@JvmStatic
fun getInstance(): IOneToManyApi {
return instance
}
}
override fun logMsg(): String {
return "I'm OneToManyApiImplKt, created by getInstance()!"
}
}
复制代码
下面这个是使用Java
实现的建立方法为getInstance
的实例:
@InjectApiImpl(api = IGetInstanceApi.class, createMethod = CreateMethod.GET_INSTANCE)
public class GetInstanceApiImpl implements IGetInstanceApi {
private static GetInstanceApiImpl instance = new GetInstanceApiImpl();
private GetInstanceApiImpl() {
}
` // 会用这个方法去获取实例
public static IGetInstanceApi getInstance() {
return instance;
}
@Override
public String logMsg() {
return "I'm GetInstanceApiImpl, created by getInstance()!";
}
}
复制代码
在有com.lyc:app-inject
这个依赖的任意module,经过如下方法就能够获取到实例:
// oneToMany = false
ISingleApi singleApi = AppInject.getInstance().getSingleApi(ISingleApi.class);
Log.d(TAG, singleApi.logMsg());
// oneToMany = true
for (IOneToManyApi oneToManyApi : AppInject.getInstance().getOneToManyApiList(IOneToManyApi.class)) {
Log.d(TAG, oneToManyApi.logMsg());
}
复制代码
能够看到,调用哪一个方法获取实例,由前文提到的oneToMany
相关,取决因而否容许接口有多个实现。
kotlin
进一步封装利用kotlin
的泛型特化,能够进一步作封装(在sample
中有):
inline fun <reified T> getSingleApi(): T? {
return AppInject.getInstance().getSingleApi(T::class.java)
}
inline fun <reified T> getOneToManyApiList(): List<T> {
return AppInject.getInstance().getOneToManyApiList(T::class.java)
}
复制代码
使用就变得更加简洁:
val testApi = getSingleApi<ITestApi>()
// or
val testApi:ITestApi? = getSingleApi()
复制代码
框架的实现有一些背景知识,这里就不展开说:
了解了上面的背景知识后,框架的结构就很简单了。首先有两个插桩方法:
// 保存单实现接口的信息
private Map<Class<?>, Implementation> singleApiClassMap = new HashMap<>();
// 保存多实现接口的信息
private Map<Class<?>, List<Implementation>> oneToManyApiClassMap = new HashMap<>();
// 插桩方法
private void initSingleApiMap() {
}
// 插桩方法
private void initOneToManyApiMap() {
List<Implementation> list;
}
复制代码
在构建的transform
流程里,扫描全部类(必须在输出apk的module中引入插件才能够扫描到app中用到的全部类),收集接口和实现的信息,再把把这些信息插入到上述代码两个map
对应的字节码插到两个对应的插桩方法便可。因此在了解了上述背景知识后实现起来并非太难,只是有可能遇到一些坑...
这个框架的灵感实际上是来源于我以前实习的团队。最近本身在写毕设的时候才发现模块多了后,模块之间的依赖很差处理,因而就想起了实习团队中用到的相似的模块解耦的方式,当时还没怎么注意这个框架,本身摸索着去实现这样一个框架才发现须要这么多的背景知识...还得继续加油啊!
再次贴一下项目地址:github.com/SirLYC/AppI…
里面有sample能够clone下来,按照README的指引来运行。若是有什么问题欢迎留言或者邮件联系!