你們好,我係蒼王。
這個系列已經出到了第30章節了,已經開通了已經有一年半的時間了。
在一年半里,創建了千人的QQ大羣,很多編輯也找過我編輯圖書,也有同行找過我合做出公衆號。可是我的的時間是有限的,並不可能所有願望都實現。
那麼上一年就選了一件對這輩子很是有意義的事情,和電子工業出版社出版一本關於組件化技術的書。很是感謝陳曉猛編輯找到了我一同出書,也感謝在技術羣中不斷深討組件化技術的羣友們。
書中重點介紹了使用組件化思想去搭建一個Android項目,介紹了組件化的思想,組件化的編程技術,多人管理組件化,組件化的編譯優化,以及對項目演進的思想感悟。
此書並非只是介紹技術,也包含了我對一些生活的理解,技術思惟的理解。
京東、淘寶和噹噹都可以購買,有興趣能夠點擊連接就能夠跳轉了。php
如下是我這個系列的相關文章,有興趣能夠參考一下,能夠給個喜歡或者關注個人文章。html
[Android]如何作一個崩潰率少於千分之三噶應用app--章節列表java
關於spi,其全名是Service Provider Interfaces。ServiceLoder用於動態加載接口實現類的加載器。
1.其能夠動態加載一些繼承某個接口的實體類
2.須要將實體類名聲明到resources/META-INF/services目錄,能夠取巧使用@AutoService
3.其加載的並非單例,並且構造方法不帶任何參數,由於ServiceLoader底層是使用了反射的機制來加載。
4.加載文件順序應該是按照resources/META-INF/services目錄中順序加載,因此若是使用@AutoService是不可控的。
5.ServiceLoader繼承iterator接口,能夠像List同樣遍歷實體類。
6.其實際也是經過反射來實現初始化操做,使用接口的方式使模塊,ServiceLoader裝載器、啓動器之間更加解耦。
7.比較適合於組件化中,模塊入口初始化的統一加載場景。android
如下借用一個Modular框架中的加載爲例
1.聲明接口編程
public interface IModule { /** * 模塊初始化,只有組建時才調用,用於開啓子線程輪訓消息 */ void init(); /** * 模塊ID * * @return 模塊ID */ int getModuleId(); /** * 模塊註冊並鏈接成功後,能夠作如下事情: * <p> * 一、註冊監聽事件 * 二、發送事件 * 三、註冊服務 * 四、調用服務 */ void afterConnected(); } 複製代碼
2.使用@AutoService,將全路徑名寫到resources/META-INF/services目錄json
@AutoService(IModule.class) public class Module extends BaseModule { @Override public void afterConnected() { } @Override public int getModuleId() { return Constants.MODULE_B; } } 複製代碼
3.使用ServiceLoder加載模塊服務器
@Override//只有當是組建單獨運行時,才當Application運行,纔會走onCreate,最終打包時根本沒有這個類 public void onCreate() { super.onCreate(); …… //自動註冊服務器(若是是獨立模塊內聲明只有一個IModule) ServiceLoader<IModule> modules = ServiceLoader.load(IModule.class); mBaseModule = (BaseModule) modules.iterator().next(); //模塊初始化 mBaseModule.init(); …… } 複製代碼
public void onCreate() { super.onCreate(); …… //SPI自動註冊服務(主module裝載的時候,已經將所有META_INF文件合併) ServiceLoader<IModule> modules = ServiceLoader.load(IModule.class); for (IModule module : modules) module.afterConnected(); } 複製代碼
使用看起來很是簡單,咱們研究一下ServiceLoader源碼的特別之處。markdown
//調用靜態load方法來初始化XXXInterface接口信息。 public static <S> ServiceLoader<S> load(Class<S> service) { //獲取當前線程ClassLoader ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); } //構建ServiceLoader對象 public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) { return new ServiceLoader<>(service, loader); } private ServiceLoader(Class<S> svc, ClassLoader cl) { //檢測接口是否否存在 service = Objects.requireNonNull(svc, "Service interface cannot be null"); //檢測classloader是否爲空,爲空使用系統classloader加載器 loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; // Android-changed: Do not use legacy security code. // On Android, System.getSecurityManager() is always null. // acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; reload(); } public void reload() { //清理provides配置加載器 providers.clear(); //初始化懶加載迭代器 lookupIterator = new LazyIterator(service, loader); } 複製代碼
能夠看到使用的是懶加載的迭代器,只有迭代器被使用的時候,纔會真正初始化每個繼承接口的實體類。架構
//判斷是否有下一個對象 private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { //PREFIX = "META-INF/services/" //加載配置地址 String fullName = PREFIX + service.getName(); if (loader == null) //加載配置 configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } //解析配置文件,只要找到一個須要解析的接口就跳出 while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } //解析config的節點 pending = parse(service, configs.nextElement()); } nextName = pending.next(); return true; } 複製代碼
經過反射完成接口類的初始化app
private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<?> c = null; try { //經過類路徑名,加載類信息 c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, // Android-changed: Let the ServiceConfigurationError have a cause. "Provider " + cn + " not found", x); // "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { // Android-changed: Let the ServiceConfigurationError have a cause. ClassCastException cce = new ClassCastException( service.getCanonicalName() + " is not assignable from " + c.getCanonicalName()); fail(service, "Provider " + cn + " not a subtype", cce); // fail(service, // "Provider " + cn + " not a subtype"); } try { //反射初始化類,並轉化成接口 S p = service.cast(c.newInstance()); //記錄映射關係 providers.put(cn, p); //返回接口實體 return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen } 複製代碼
ServiceLoader實際仍是經過路徑名反射來完成,只是其經過配置到META_INF中的目錄文件來完成解耦。
ServiceLoader使用場景是用於不須要區分module加載順序的狀況,若是有加載順序,還須要從新排序後再初始化方法,這裏最後仍是使用優先級機制。
在開編的時候已經介紹了SPI的優勢和侷限性,跳出SPI,依然能作一個更靈活更可控的加載機制,例如json腳本,xml腳本動態更新。