Java 中有個 SPI 的機制,可以用來很好的做功能的擴展和模塊之間的接口通信,這方面不太了解的可以參考我之前的文章 Android 模塊開發(fā)之 SPI, 今天接著分析 WMRouter 中的 ServiceLoader 功能和實現(xiàn)。
1.ServiceLoader 功能
這方面其實官網(wǎng)WMRouter說的很清楚了,我們直接拿過來看下:
- 使用注解自動配置
- 支持獲取接口的所有實現(xiàn),或根據(jù) Key 獲取特定實現(xiàn)
- 支持獲取 Class 或獲取實例
- 支持無參構造、Context 構造,或自定義 Factory、Provider 構造
- 支持單例管理
- 支持方法調(diào)用
看一個官方例子,先看下服務的注解定義, 官方注釋的很清晰,可以通過 interface 接口和 key 來加載實現(xiàn)類。還可以聲明服務是否是單例的。
// RouterService.java
/**
* 聲明一個Service,通過interface和key加載實現(xiàn)類。此注解可以用在任意靜態(tài)類上。
*
* Created by jzj on 2018/3/29.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface RouterService {
/**
* 實現(xiàn)的接口(或繼承的父類)
*/
Class[] interfaces();
/**
* 同一個接口的多個實現(xiàn)類之間,可以通過唯一的key區(qū)分。
*/
String[] key() default {};
/**
* 是否為單例。如果是單例,則使用ServiceLoader.getService不會重復創(chuàng)建實例。
*/
boolean singleton() default false;
/**
* 是否設置為默認實現(xiàn)類。如果是默認實現(xiàn)類,則在獲取該實現(xiàn)類實例時可以不指定key
* @return
*/
boolean defaultImpl() default false;
}
再接著往下看獲取接口的所有實現(xiàn)的 class
工程里有兩個 module libmodule1 和 libmodule2,兩個的對外服務都通過接口名標識
public abstract class LibraryModule {
public abstract String getModuleName();
@Override
public String toString() {
return "Module: " + getModuleName();
}
}
// libmodule1
@RouterService(interfaces = LibraryModule.class)
public class LibraryModule1 extends LibraryModule {
@Override
public String getModuleName() {
return "lib1";
}
}
// libmodule2
@RouterService(interfaces = LibraryModule.class)
public class LibraryModule2 extends LibraryModule {
@Override
public String getModuleName() {
return "lib2";
}
}
然后在主工程中寫如下代碼, 可以拿到全部兩個的實現(xiàn)類 class
// 通過接口(或父類)加載所有實現(xiàn)類
// ServiceLoaderActivity.java
List<Class<LibraryModule>> classList = Router.getAllServiceClasses(LibraryModule.class);
那拿到所有的類實現(xiàn)就簡單了,簡單根據(jù)反射就行, 框架提供了接口
// 通過接口(或父類)創(chuàng)建所有實現(xiàn)類的實例
// ServiceLoaderActivity.java
List<LibraryModule> list = Router.getAllServices(LibraryModule.class);
通過 key 來獲取特定的實現(xiàn)類就簡單增加一個 key 就行
// TestFragment.java
@RouterService(interfaces = Fragment.class, key = DemoConstant.TEST_FRAGMENT)
public class TestFragment extends Fragment {
}
// ServiceLoaderActivity.java
Class<Fragment> fragmentClass = Router.getServiceClass(Fragment.class,DemoConstant.TEST_FRAGMENT);
再接著往下單例的使用,
有一個賬戶服務,功能是可以打開登錄頁,記錄登錄狀態(tài)等。通過注解@RouterService 聲明接口是 IAccountService,key 是 DemoConstant.SINGLETON("/singleton"),并且是單例
// FakeAccountService.java
@RouterService(interfaces = IAccountService.class, key = DemoConstant.SINGLETON, singleton = true)
public class FakeAccountService implements IAccountService {
@RouterProvider
public static FakeAccountService getInstance() {
return new FakeAccountService(Router.getService(Context.class, "/application"));
}
private boolean mIsLogin = false;
private final List<Observer> mObservers = new ArrayList<>();
private FakeAccountService(Context context) {
// ...
}
@Override
public void startLogin(Context context) {
Router.startUri(context, DemoConstant.LOGIN);
}
@Override
public boolean isLogin() {
return mIsLogin;
}
@Override
public void registerObserver(Observer observer) {
if (observer != null && !mObservers.contains(observer)) {
mObservers.add(observer);
}
}
@Override
public void unregisterObserver(Observer observer) {
if (observer != null) {
mObservers.remove(observer);
}
}
@Override
public void notifyLoginSuccess() {
mIsLogin = true;
Observer[] observers = getObservers();
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onLoginSuccess();
}
}
@Override
public void notifyLoginCancel() {
Observer[] observers = getObservers();
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onLoginCancel();
}
}
@Override
public void notifyLoginFailure() {
Observer[] observers = getObservers();
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onLoginFailure();
}
}
@Override
public void notifyLogout() {
mIsLogin = false;
Observer[] observers = getObservers();
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onLogout();
}
}
@NonNull
private Observer[] getObservers() {
return mObservers.toArray(new Observer[mObservers.size()]);
}
}
在外面使用方式, 通過接口 class 和 key 就可以拿到賬號服務
// ServiceLoaderActivity.java
IAccountService accountService1 = Router.getService(IAccountService.class,DemoConstant.SINGLETON);
再看下構造的使用,可以有無參構造,context 構造,自定義構造和 RouterProvider 構造四種方式:
// IFactoryService.java
public interface IFactoryService {
String name();
}
// FactoryServiceImpl.java
@RouterService(interfaces = IFactoryService.class, key = "/factory")
public class FactoryServiceImpl implements IFactoryService {
private final String mName;
@RouterProvider
public static FactoryServiceImpl provideService() {
return new FactoryServiceImpl("CreateByProvider");
}
public FactoryServiceImpl() {
mName = "CreateWithEmptyArgs";
}
public FactoryServiceImpl(Context context) {
mName = "CreateWithContext";
}
public FactoryServiceImpl(String name) {
mName = name;
}
@Override
public String name() {
return mName;
}
}
通過下面代碼調(diào)用,其中無參構造其實就是反射調(diào)用無參構造,四種調(diào)用的順序是單例,factory,如果沒有傳入 factory 默認找 Provider,找不到通過默認無參構造
// ServiceLoaderActivity.java
// EmptyArgsFactory
IFactoryService service1 = Router.getService(IFactoryService.class, "/factory", EmptyArgsFactory.INSTANCE);
// Provider
IFactoryService service2 = Router.getService(IFactoryService.class, "/factory");
// ContextFactory
IFactoryService service3 = Router.getService(IFactoryService.class, "/factory", this);
// CustomFactory
IFactoryService service4 = Router.getService(IFactoryService.class, "/factory", new IFactory() {
@NonNull
@Override
public <T> T create(@NonNull Class<T> clazz) throws Exception {
return clazz.getConstructor(String.class).newInstance("CreateByCustomFactory");
}
});
public class EmptyArgsFactory implements IFactory {
public static final EmptyArgsFactory INSTANCE = new EmptyArgsFactory();
private EmptyArgsFactory() {
}
@NonNull
@Override
public <T> T create(@NonNull Class<T> clazz) throws Exception {
return clazz.newInstance();
}
}
再看一下最后一個功能,方法調(diào)用,方法其實也是封裝成了類,該類還是通過注解 RouterService,并且實現(xiàn)特定的 FunX 接口
@RouterService(interfaces = Func2.class, key = DemoConstant.ADD_METHOD, singleton = true)
public class AddMethod implements Func2<Integer, Integer, Integer> {
@Override
public Integer call(Integer a, Integer b) {
return a + b;
}
}
在核心庫下面定義了下面這些函數(shù)調(diào)用接口,定義了 Func0 到 Func9 和 FuncN
// Func0.java
public interface Func0<R> extends Function {
R call();
}
// Func1.java
public interface Func1<T, R> extends Function {
R call(T t);
}
// Func2.java
public interface Func2<T1, T2, R> extends Function {
R call(T1 t1, T2 t2);
}
// FuncN.java
public interface FuncN<R> extends Function {
R call(Object... args);
}
// Function.java
public interface Function {
}
然后在主工程中調(diào)用:
// ServiceLoaderActivity.java
Integer result = Router.callMethod(DemoConstant.ADD_METHOD, 1, 2);
其實是根據(jù)參數(shù)個數(shù)匹配接口:
/**
* 調(diào)用方法。方法應該實現(xiàn) {@link Func0} ~ {@link FuncN} 接口,根據(jù)參數(shù)個數(shù)匹配接口。
*/
@SuppressWarnings("unchecked")
public static <T> T callMethod(String key, Object... args) {
switch (args.length) {
case 0:
return (T) getService(Func0.class, key).call();
case 1:
return (T) getService(Func1.class, key).call(args[0]);
case 2:
return (T) getService(Func2.class, key).call(args[0], args[1]);
case 3:
return (T) getService(Func3.class, key).call(args[0], args[1], args[2]);
case 4:
return (T) getService(Func4.class, key).call(
args[0], args[1], args[2], args[3]);
case 5:
return (T) getService(Func5.class, key).call(
args[0], args[1], args[2], args[3], args[4]);
case 6:
return (T) getService(Func6.class, key).call(
args[0], args[1], args[2], args[3], args[4], args[5]);
case 7:
return (T) getService(Func7.class, key).call(
args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
case 8:
return (T) getService(Func8.class, key).call(
args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7]);
case 9:
return (T) getService(Func9.class, key).call(
args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7], args[8]);
default:
return (T) getService(FuncN.class, key).call(args);
}
}
最后也支持 kotlin 的服務調(diào)用:
// KotlinService.java
@RouterService(interfaces = [Object::class], key = [DemoConstant.KOTLIN_SERVICE])
class KotlinService {
}
// ServiceLoaderActivity.java
Object service = Router.getService(Object.class, DemoConstant.KOTLIN_SERVICE);
上面看了 ServiceLoader 的使用,下面看下具體的源碼分析
2. ServiceLoader源碼分析
先從初始化入口著手,一般建議我們在 Application 中進行異步初始化,當然這樣也行,因為會在每次跳轉之前判斷ServiceLoaderInit.class是否加載了:
// DemoApplication.java
// 懶加載后臺初始化(可選)
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
Router.lazyInit();
return null;
}
}.execute();
// Router.java
/**
* 此初始化方法的調(diào)用不是必須的。
* 使用時會按需初始化;但也可以提前調(diào)用并初始化,使用時會等待初始化完成。
* 本方法線程安全。
*/
public static void lazyInit() {
ServiceLoader.lazyInit();
getRootHandler().lazyInit();
}
再看到 ServiceLoader.lazyInit, 其中的
Const.SERVICE_LOADER_INIT = com.sankuai.waimai.router.generated.ServiceLoaderInit
// ServiceLoader.java
public static void lazyInit() {
sInitHelper.lazyInit();
}
private static final LazyInitHelper sInitHelper = new LazyInitHelper("ServiceLoader") {
@Override
protected void doInit() {
try {
// 反射調(diào)用Init類,避免引用的類過多,導致main dex capacity exceeded問題
Class.forName(Const.SERVICE_LOADER_INIT)
.getMethod(Const.INIT_METHOD)
.invoke(null);
Debugger.i("[ServiceLoader] init class invoked");
} catch (Exception e) {
Debugger.fatal(e);
}
}
};
上面反射調(diào)用的類是編譯時生成的,這個可以參考上一篇博客 WMRouter 源碼解析之 Transform
看下這個 com.sankuai.waimai.router.generated.ServiceLoaderInit.class 的內(nèi)容,其實就是調(diào)用工程所有動態(tài)生成的 ServiceInit_xxx 的初始化方法,
public class ServiceLoaderInit {
public static void init() {
ServiceInit_aea7f96d0419b507d9b0ef471913b2f5.init();
ServiceInit_f3649d9f5ff15a62b844e64ca8434259.init();
ServiceInit_eb71854fbd69455ef4e0aa026c2e9881.init();
ServiceInit_b57118238b4f9112ddd862e55789c834.init();
ServiceInit_f1e07218f6691f962a9f674eb5b4b8bd.init();
ServiceInit_4268a3e74040533ba48f2e1679155468.init();
ServiceInit_e694d982fb5d7a3a8c6b7085829e74a6.init();
ServiceInit_ee5f6404731417fe1433da40fd3c9708.init();
ServiceInit_9482ef47a8cf887ff1dc4bf705d5fc0a.init();
ServiceInit_36ed390bf4b81a8381d45028b37cc645.init();
}
}
看下主工程的一些 ServiceInit_xxx, 看到了我們前面舉例的 FakeAccountService 和 Func2
// ServiceInit_b57118238b4f9112ddd862e55789c834.java
public class ServiceInit_b57118238b4f9112ddd862e55789c834 {
public static void init() {
ServiceLoader.put(Context.class, "/application", DemoApplication.class, true);
ServiceLoader.put(ILocationService.class, "/singleton", FakeLocationService.class, true);
ServiceLoader.put(Func0.class, "/method/get_version_code", GetVersionCodeMethod.class, true);
ServiceLoader.put(IAccountService.class, "/singleton", FakeAccountService.class, true);
ServiceLoader.put(Fragment.class, "/fragment/test", TestFragment.class, false);
ServiceLoader.put(TestServiceAnnotation.IService.class, "com.sankuai.waimai.router.demo.testannotation.TestServiceAnnotation.ServiceImpl2", TestServiceAnnotation.ServiceImpl2.class, false);
ServiceLoader.put(TestServiceAnnotation.IService.class, "com.sankuai.waimai.router.demo.testannotation.TestServiceAnnotation.ServiceImpl1", TestServiceAnnotation.ServiceImpl1.class, false);
ServiceLoader.put(Func2.class, "/method/add", AddMethod.class, true);
ServiceLoader.put(Object.class, "/service/test_annotation_2", TestServiceAnnotation.TestPathService2.class, false);
ServiceLoader.put(Object.class, "/service/test_annotation_1", TestServiceAnnotation.TestPathService1.class, false);
}
}
// ServiceInit_eb71854fbd69455ef4e0aa026c2e9881.java
public class ServiceInit_eb71854fbd69455ef4e0aa026c2e9881 {
public static void init() {
ServiceLoader.put(IUriAnnotationInit.class, "com.sankuai.waimai.router.generated.UriAnnotationInit_72565413b8384a4bebb02d352762d60d", com.sankuai.waimai.router.generated.UriAnnotationInit_72565413b8384a4bebb02d352762d60d.class, false);
}
}
接著看ServiceLoader.java的源碼,
內(nèi)部有個接口類型 -> ServiceLoader 的靜態(tài)映射容器
// ServiceLoader.java
private static final Map<Class, ServiceLoader> SERVICES = new HashMap<>();
public static void put(Class interfaceClass, String key, Class implementClass, boolean singleton) {
ServiceLoader loader = SERVICES.get(interfaceClass);
if (loader == null) {
loader = new ServiceLoader(interfaceClass);
SERVICES.put(interfaceClass, loader);
}
loader.putImpl(key, implementClass, singleton);
}
前面的putImpl就是往另外一個私有容器里面根據(jù)key對應構造一個ServiceImpl放入這個私有容器中。
/**
* key --> class name
*/
private HashMap<String, ServiceImpl> mMap = new HashMap<>();
private final String mInterfaceName;
private ServiceLoader(Class interfaceClass) {
if (interfaceClass == null) {
mInterfaceName = "";
} else {
mInterfaceName = interfaceClass.getName();
}
}
private void putImpl(String key, Class implementClass, boolean singleton) {
if (key != null && implementClass != null) {
mMap.put(key, new ServiceImpl(key, implementClass, singleton));
}
}
ServiceImpl只是幾個屬性的簡單包裝,就不貼代碼了
再分別看下get 類型和實例的實現(xiàn)代碼,就是簡單的get
public <T extends I> T get(String key, Context context) {
return createInstance(mMap.get(key), new ContextFactory(context));
}
public <T extends I> Class<T> getClass(String key) {
return (Class<T>) mMap.get(key).getImplementationClazz();
}
再看下createInstance:
- 如果是單例,會通過SingletonPool進行get
- 如果沒有傳入IFactory,會默認構造一個,里面會通過provider獲取實例或者默認構造
@Nullable
private <T extends I> T createInstance(@Nullable ServiceImpl impl, @Nullable IFactory factory) {
if (impl == null) {
return null;
}
Class<T> clazz = (Class<T>) impl.getImplementationClazz();
if (impl.isSingleton()) {
try {
return SingletonPool.get(clazz, factory);
} catch (Exception e) {
Debugger.fatal(e);
}
} else {
try {
if (factory == null) {
factory = RouterComponents.getDefaultFactory();
}
T t = factory.create(clazz);
Debugger.i("[ServiceLoader] create instance: %s, result = %s", clazz, t);
return t;
} catch (Exception e) {
Debugger.fatal(e);
}
}
return null;
}
先看下RouterComponents.getDefaultFactory:
// RouterComponents.java
@NonNull
public static IFactory getDefaultFactory() {
return sDefaultFactory;
}
@NonNull
private static IFactory sDefaultFactory = DefaultFactory.INSTANCE;
// DefaultFactory.java
public class DefaultFactory implements IFactory {
public static final DefaultFactory INSTANCE = new DefaultFactory();
private DefaultFactory() {
}
@NonNull
@Override
public <T> T create(@NonNull Class<T> clazz) throws Exception {
T t = ProviderPool.create(clazz);
if (t != null) {
return t;
} else {
return clazz.newInstance();
}
}
}
上面就是ServiceLoader的大部分實現(xiàn),其實大體上就是一個接口對應一個ServiceLoader,然后每個ServiceLoader中有個key -> ServiceLoaderImpl的映射容器。
最后就是剩下兩個pool,一個是單例SingletonPool,另外一個就是ProviderPool,先看下SingletonPool, 邏輯比較簡單,通過靜態(tài)的緩存,如果沒有緩存就通過傳進來的factory進行構造。
// SingletonPool.java
public class SingletonPool {
private static final Map<Class, Object> CACHE = new HashMap<>();
@SuppressWarnings("unchecked")
public static <I, T extends I> T get(Class<I> clazz, IFactory factory) throws Exception {
if (clazz == null) {
return null;
}
if (factory == null) {
factory = RouterComponents.getDefaultFactory();
}
Object instance = getInstance(clazz, factory);
Debugger.i("[SingletonPool] get instance of class = %s, result = %s", clazz, instance);
return (T) instance;
}
@NonNull
private static Object getInstance(@NonNull Class clazz, @NonNull IFactory factory) throws Exception {
Object t = CACHE.get(clazz);
if (t != null) {
return t;
} else {
synchronized (CACHE) {
t = CACHE.get(clazz);
if (t == null) {
Debugger.i("[SingletonPool] >>> create instance: %s", clazz);
t = factory.create(clazz);
//noinspection ConstantConditions
if (t != null) {
CACHE.put(clazz, t);
}
}
}
return t;
}
}
}
再接著看下ProviderPool,是用來緩存通過注解RouterProvider的靜態(tài)方法
/**
* 指定一個靜態(tài)方法,用于構造Service
* Created by jzj on 2018/6/7.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RouterProvider {
}
通過getProivider找到注解的方法,如果沒有找到就報錯,找到就通過反射調(diào)用返回
private static final HashMap<Class, Method> CACHE = new HashMap<>();
private static final Method NOT_FOUND = ProviderPool.class.getDeclaredMethods()[0];
@SuppressWarnings("unchecked")
public static <T> T create(Class<T> clazz) {
if (clazz == null) {
return null;
}
Method provider = getProvider(clazz);
if (provider == NOT_FOUND) {
Debugger.i("[ProviderPool] provider not found: %s", clazz);
return null;
} else {
Debugger.i("[ProviderPool] provider found: %s", provider);
try {
return (T) provider.invoke(null);
} catch (Exception e) {
Debugger.fatal(e);
}
}
return null;
}
再看下上面的getProvider方法, 通過double check的方式防止多線程環(huán)境出錯。
findProvider通過拿到RouterProvider注解的方法,同時滿足是靜態(tài)方法,返回類型是類構造傳進來的實現(xiàn)類型,另外沒有參數(shù)的方法
@NonNull
private static <T> Method getProvider(@NonNull Class<T> clazz) {
Method provider = CACHE.get(clazz);
if (provider == null) {
synchronized (CACHE) {
provider = CACHE.get(clazz);
if (provider == null) {
provider = findProvider(clazz);
CACHE.put(clazz, provider);
}
}
}
return provider;
}
@NonNull
private static Method findProvider(@NonNull Class clazz) {
Debugger.i("[ProviderPool] >>> find provider with reflection: %s", clazz);
for (Method method : clazz.getDeclaredMethods()) {
if (method.getAnnotation(RouterProvider.class) != null) {
if (Modifier.isStatic(method.getModifiers()) &&
method.getReturnType() == clazz &&
RouterUtils.isEmpty(method.getParameterTypes())) {
return method;
} else {
Debugger.fatal("[ProviderPool] RouterProvider注解的應該是靜態(tài)無參數(shù)方法,且返回值類型為當前Class");
return NOT_FOUND;
}
}
}
return NOT_FOUND;
}
最后看一張ServiceLoader的運行時圖,有個總體的了解

3.總結
學習優(yōu)秀的代碼可以促進自己的進步,傳統(tǒng)的SPI機制會有運行時IO和反射的缺點,而WMRouter中就可以通過后臺線程進行懶加載,做了緩存用空間換取時間,并且提供了獲取實現(xiàn)類和實例的接口,也支持單例和方法的調(diào)用。