項目地址:github.com/johnsonlee/…html
作 Java 開發的同窗應該對 SPI (Service Provider Interface) 不會陌生,不管是 JDK、Gradle 亦或是一些第三方框架,都或多或少的用它來實現可擴展的能力,爲何在 Android 平臺上卻不爲人知呢?java
我的認爲緣由有 2 點:android
Android 與 Java 應用的分發方式不一樣git
Java 應用採用 JAR 分發,擴展起來很是容易,而 Android APK 一旦打好包,就至關於固化下來了,實現可擴展須要藉助於其它非官方推薦的手段github
SPI 在 Android 平臺上的性能堪憂編程
因爲 Android 設備的性能廣泛低於 PC ,加上 Android 平臺特有的 ANR 機制,使得 SPI 很容易在 Android 上發生 卡頓甚至 ANRmarkdown
Java 官方的定義是:架構
提供訪問特定應用功能和特性的一系列編程接口和類oracle
由 Service 定義的一系列公共接口和抽象類框架
SPI 的實現
Java 原生的 SPI 是經過 ClassLoader 在 CLASSPATH 中搜索 META-INF/services/ 下 SPI 配置文件,而後讀取配置文件中的內容,因此須要解析 JAR 文件(Android 平臺是解析 APK),而這個過程須要遍歷整個 JAR/APK 中全部的條目,所以很是耗時(當年就被它坑得很慘)
既然 SPI 在 Android 上表現這麼差,爲何還要用它呢?
在應用開發的過程當中,不免由於應用架構的緣由,須要支持非運行時可擴展能力,好比:接口與實現分離、一個接口對應多個實現須要動態查找、反向依賴等等,相對於 Dagger 來講,它簡單、原生 API 支持、不須要依賴額外的第三方庫。
既然選擇了 SPI,如何解決 SPI 在 Android 平臺上的性能瓶頸?——去 I/O
方法其實也很簡單,分爲以下幾個步驟,咱們不妨用逆向思惟來看:
可是這裏有幾個問題:
能夠是 Class 的映射,可是,若是要實例化 Service Provider 就得須要反射
固然是能夠的,這樣 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(); } } 複製代碼
public class ServiceRegistry { static final Map<Class<?>, List<Callable<?>>> registry = new HashMap<>(); static { register(InterfaceA.class, new InterfaceACreator()) } } 複製代碼
該方案已經徹底開源,項目地址:github.com/johnsonlee/… star 和 PR。