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。

相關文章
相關標籤/搜索