SPI 性能优化

项目地址:github.com/johnsonlee/…html

作 Java 开发的同窗应该对 SPI (Service Provider Interface) 不会陌生,不管是 JDK、Gradle 亦或是一些第三方框架,都或多或少的用它来实现可扩展的能力,为何在 Android 平台上却不为人知呢?java

我的认为缘由有 2 点:android

  1. Android 与 Java 应用的分发方式不一样git

    Java 应用采用 JAR 分发,扩展起来很是容易,而 Android APK 一旦打好包,就至关于固化下来了,实现可扩展须要借助于其它非官方推荐的手段github

  2. SPI 在 Android 平台上的性能堪忧编程

因为 Android 设备的性能广泛低于 PC ,加上 Android 平台特有的 ANR 机制,使得 SPI 很容易在 Android 上发生 卡顿甚至 ANRmarkdown

什么是 SPI

Java 官方的定义是:架构

Service

提供访问特定应用功能和特性的一系列编程接口和类oracle

Service Provider Interface (SPI)

由 Service 定义的一系列公共接口和抽象类框架

Service Provider

SPI 的实现

SPI 的性能缺陷

Java 原生的 SPI 是经过 ClassLoaderCLASSPATH 中搜索 META-INF/services/ 下 SPI 配置文件,而后读取配置文件中的内容,因此须要解析 JAR 文件(Android 平台是解析 APK),而这个过程须要遍历整个 JAR/APK 中全部的条目,所以很是耗时(当年就被它坑得很惨)

选择 SPI 的理由

既然 SPI 在 Android 上表现这么差,为何还要用它呢?

在应用开发的过程当中,不免由于应用架构的缘由,须要支持非运行时可扩展能力,好比:接口与实现分离、一个接口对应多个实现须要动态查找、反向依赖等等,相对于 Dagger 来讲,它简单、原生 API 支持、不须要依赖额外的第三方库。

如何优化

既然选择了 SPI,如何解决 SPI 在 Android 平台上的性能瓶颈?——去 I/O

方法其实也很简单,分为以下几个步骤,咱们不妨用逆向思惟来看:

  1. 在构建期间生成一个服务注册表—— ServiceRegistry
  2. 将代码中全部调用 ServiceLoader 的指令替换成调用自定义的 ShadowServiceLoader
  3. ShadowServiceLoaderServiceRegistry 中获取 Service Provider InterfaceService Provider 的映射关系

可是这里有几个问题:

  1. ServiceRegistry 中存储的是 Class<Service>Class<ServiceProvider> 的映射仍是别的映射方式?

能够是 Class 的映射,可是,若是要实例化 Service Provider 就得须要反射

  1. ServiceLoader 经过 ServiceRegistry 加载的时候,可否彻底避免反射?

固然是能够的,这样 ServiceRegistry 中存储的映射关系就须要调整为 Class<Service>Creator<ServiceProvider> 之间的映射关系,内容大体以下:

public class ServiceRegistry {

    static final Map<Class<?>, List<Callable<?>>> registry = new HashMap<>();

}
复制代码

Creator 的内容大体以下:

public class ServiceProviderCreator implements Callable<ServiceProvider> {

    @Override
    public ServiceProvider call() {
        return new ServiceProvider();
    }

}
复制代码
  1. 如何将映射关系注册到 ServiceRegistry 里面呢?——静态代码块,内容大体以下:
public class ServiceRegistry {

    static final Map<Class<?>, List<Callable<?>>> registry = new HashMap<>();

    static {
        register(InterfaceA.class, new InterfaceACreator())
    }

}
复制代码

源代码

该方案已经彻底开源,项目地址:github.com/johnsonlee/… star 和 PR。

相关文章
相关标签/搜索