SPI机制

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)
            }
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • SPI机制是Dubbo框架的基础知识,学习Dubbo框架之前有必要深入理解SPI机制。下面对SPI的概念,作用,使...
    邵小星要努力阅读 533评论 0 0
  • 概述   在某些时候我们可以通过在软件上游提供服务接口,无需在意接口的实现逻辑,全部交由扩展程序进行实现,上游只需...
    ruoshy阅读 710评论 0 6
  • 从今天开始,将会逐步介绍关于DUbbo的有关知识。首先先简单介绍一下DUbbo的整体概述。 概述 Dubbo是SO...
    九点半的马拉阅读 1,419评论 0 0
  • 本篇其实更应该在Dubbo系列的最早去写,只不过因为当初在读源码的时候没有开始更新博客,所以漏了重要的一个章节,那...
    此鱼不得水阅读 19,727评论 1 7
  • 背景 一位前辈在一次技术分享中指出我们目前的包管理不规范,模块间职责有重叠,理解成本高不易维护,提出在开发过程中应...
    XHLeee阅读 387评论 0 0

友情链接更多精彩内容