你們好,我係蒼王。
這個系列已經出到了第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源碼的特別之處。架構
//調用靜態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);
}
複製代碼
能夠看到使用的是懶加載的迭代器,只有迭代器被使用的時候,纔會真正初始化每個繼承接口的實體類。app
//判斷是否有下一個對象
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;
}
複製代碼
經過反射完成接口類的初始化框架
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腳本動態更新。