SPI:由于业务模块进行了组件拆分,开发了基于SPI组件通信方式,用在模块间降低耦合,解决业务模块的通信问题。可以在业务aar中使用。是Arouter框架服务间调用仅使用于java源文件的功能拓展。
A模块,B模块都依赖于Common基础服务模块,A模块需要调用B模块提供的数据,B模块也需要调用A模块提供的数据,Common模块与A模块,B模块都依赖于SPI插件。
实现原理:
SPI(Service Provider Interface)
本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载具体实现类。这样可以在运行时,动态为接口替换实现类。用在模块间降低耦合,适合在业务aar中使用,解决了业务模块间的通信问题。
1.自定义gradle插件:ServiceProviderInterfacePlugin implements Plugin<Project>
2.自定义generateServiceRegistry task,它的执行时机在javaCompile编译之后,根据传入的编译后的字节码路径,使用javassit扫描工程中含有@ServiceProvider注解的*.class字节码文件并收集接口实现类信息写入指定目录文件,然后根据指定目录下的文件使用javapoet生成服务注册类ServiceRegistry,建立接口类和实现类的映射关系。就可以调用ServiceLoader.load(interface.Class)获取具体接口实现类。
3.最后调用自定义compileGeneratedTask编译javapoet生成的注册类ServiceRegistry,接下来就可以在代码中使用了。
// ServiceProvider注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceProvider {
Class<?> [] value();
int priority() default 0;
String alias() default "";
}
具体接口实现类:
@ServiceProvider(ITest.class)
public class Test implements ITest{
public static String hello="hello";
@Override
public void printMessage(String msg) {
Log.e("stormzsl",msg);
}
}
调用方式:
for (ITest test : ServiceLoader.load(ITest.class)) {
test.printMessage("测试");
}
自定义gradle插件:
ServiceProviderInterfacePlugin implements Plugin<Project>{
@Override
void apply(final Project project) {
project.dependencies {
api 'com.fasten.component.spi:loader:1.0.0'
api 'com.fasten.component.spi:annotations:1.0.0'
}
project.afterEvaluate {
try {
if (!project.plugins.hasPlugin(Class.forName('com.android.build.gradle.AppPlugin'))) {
return
}
} catch (final ClassNotFoundException e) {
throw new GradleException("Android gradle plugin is required", e)
}
project.android.applicationVariants.all { variant ->
def spiSourceDir = project.file("${project.buildDir}/intermediates/spi/${variant.dirName}/src")
//variant.dirName=debug/release,下面以debug为构建类型的输出
println(">>>>>>> variant.dirName=${variant.dirName}")
// spiSourceDir=/Users/didi/develop/practiceProject/androidSPI/app/build/intermediates/spi/debug/src
println(">>>>>>> spiSourceDir=${spiSourceDir}")
// spiServicesDir=/Users/didi/develop/practiceProject/androidSPI/app/build/intermediates/spi/debug/services
def spiServicesDir = project.file("${project.buildDir}/intermediates/spi/${variant.dirName}/services")
println(">>>>>>> spiServicesDir=${spiServicesDir}")
def spiClasspath = project.files(project.android.bootClasspath, variant.javaCompile.classpath, variant.javaCompile.destinationDir)
println(">>>>>>> spiClasspath=${spiClasspath}")
// project.android.bootClasspath=[/Users/didi/Library/Android/sdk/platforms/android-28/android.jar]
println(">>>>>>> project.android.bootClasspath=${project.android.bootClasspath}")
// variant.javaCompile.classpath=file collection
println(">>>>>>> variant.javaCompile.classpath=${variant.javaCompile.classpath}")
//variant.javaCompile.destinationDir=/Users/didi/develop/practiceProject/androidSPI/app/build/intermediates/javac/debug/classes
println(">>>>>>> variant.javaCompile.destinationDir=${variant.javaCompile.destinationDir}")
println(">>>>>> variant.name=${variant.name} variant.name.capitalize()=${variant.name.capitalize()}")
def generateTask = project.task("generateServiceRegistry${variant.name.capitalize()}", type: ServiceRegistryGenerationTask) {
description = "Generate ServiceRegistry for ${variant.name.capitalize()}"
classpath += spiClasspath
sourceDir = spiSourceDir
servicesDir = spiServicesDir
outputs.upToDateWhen { false }
}
def compileGeneratedTask = project.task("compileGenerated${variant.name.capitalize()}", type: JavaCompile) {
description = "Compile ServiceRegistry for ${variant.name.capitalize()}"
source = spiSourceDir
include '**/*.java'
classpath = spiClasspath
destinationDir = variant.javaCompile.destinationDir
sourceCompatibility = '1.5'
targetCompatibility = '1.5'
}
generateTask.mustRunAfter(variant.javaCompile)
compileGeneratedTask.mustRunAfter(generateTask)
variant.assemble.dependsOn(generateTask, compileGeneratedTask)
}
}
}
网友评论