Java 中有个 SPI 的机制,可以用来很好的做功能的扩展和模块之间的接口通信,这方面不太了解的可以参考我之前的文章 Android 模块开发之 SPI, 今天接着分析 WMRouter 中的 ServiceLoader 功能和实现。
这方面其实官网WMRouter说的很清楚了,我们直接拿过来看下:
- 使用注解自动配置
- 支持获取接口的所有实现,或根据 Key 获取特定实现
- 支持获取 Class 或获取实例
- 支持无参构造、Context 构造,或自定义 Factory、Provider 构造
- 支持单例管理
- 支持方法调用
看一个官方例子,先看下服务的注解定义, 官方注释的很清晰,可以通过 interface 接口和 key 来加载实现类。还可以声明服务是否是单例的。
// RouterService.java /** * 声明一个Service,通过interface和key加载实现类。此注解可以用在任意静态类上。 * * Created by jzj on 2018/3/29. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface RouterService { /** * 实现的接口(或继承的父类) */ Class[] interfaces(); /** * 同一个接口的多个实现类之间,可以通过唯一的key区分。 */ String[] key() default {}; /** * 是否为单例。如果是单例,则使用ServiceLoader.getService不会重复创建实例。 */ boolean singleton() default false; /** * 是否设置为默认实现类。如果是默认实现类,则在获取该实现类实例时可以不指定key * @return */ boolean defaultImpl() default false; } 复制代码
再接着往下看获取接口的所有实现的 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"; } } 复制代码
然后在主工程中写如下代码, 可以拿到全部两个的实现类 class
// 通过接口(或父类)加载所有实现类 // ServiceLoaderActivity.java List<Class<LibraryModule>> classList = Router.getAllServiceClasses(LibraryModule.class); 复制代码
那拿到所有的类实现就简单了,简单根据反射就行, 框架提供了接口
// 通过接口(或父类)创建所有实现类的实例 // ServiceLoaderActivity.java List<LibraryModule> list = Router.getAllServices(LibraryModule.class); 复制代码
通过 key 来获取特定的实现类就简单增加一个 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); 复制代码
再接着往下单例的使用, 有一个账户服务,功能是可以打开登录页,记录登录状态等。通过注解@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; } } 复制代码
通过下面代码调用,其中无参构造其实就是反射调用无参构造,四种调用的顺序是单例,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(); } } 复制代码
再看一下最后一个功能,方法调用,方法其实也是封装成了类,该类还是通过注解 RouterService,并且实现特定的 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; } } 复制代码
在核心库下面定义了下面这些函数调用接口,定义了 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 { } 复制代码
然后在主工程中调用:
// ServiceLoaderActivity.java Integer result = Router.callMethod(DemoConstant.ADD_METHOD, 1, 2); 复制代码
其实是根据参数个数匹配接口:
/** * 调用方法。方法应该实现 {@link Func0} ~ {@link FuncN} 接口,根据参数个数匹配接口。 */ @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 的服务调用:
// KotlinService.java @RouterService(interfaces = [Object::class], key = [DemoConstant.KOTLIN_SERVICE]) class KotlinService { } // ServiceLoaderActivity.java Object service = Router.getService(Object.class, DemoConstant.KOTLIN_SERVICE); 复制代码
上面看了 ServiceLoader 的使用,下面看下具体的源码分析
先从初始化入口着手,一般建议我们在 Application 中进行异步初始化,当然这样也行,因为会在每次跳转之前判断ServiceLoaderInit.class
是否加载了:
// DemoApplication.java // 懒加载后台初始化(可选) new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... voids) { Router.lazyInit(); return null; } }.execute(); // Router.java /** * 此初始化方法的调用不是必须的。 * 使用时会按需初始化;但也可以提前调用并初始化,使用时会等待初始化完成。 * 本方法线程安全。 */ 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 { // 反射调用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); } } }; 复制代码
上面反射调用的类是编译时生成的,这个可以参考上一篇博客 WMRouter 源码解析之 Transform
看下这个 com.sankuai.waimai.router.generated.ServiceLoaderInit.class 的内容,其实就是调用工程所有动态生成的 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
的源码,
内部有个接口类型 -> ServiceLoader 的静态映射容器
// 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就是往另外一个私有容器里面根据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 类型和实例的实现代码,就是简单的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
:
@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
的大部分实现,其实大体上就是一个接口对应一个ServiceLoader,然后每个ServiceLoader中有个key -> ServiceLoaderImpl的映射容器。
最后就是剩下两个pool,一个是单例SingletonPool,另外一个就是ProviderPool,先看下SingletonPool, 逻辑比较简单,通过静态的缓存,如果没有缓存就通过传进来的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的静态方法
/** * 指定一个静态方法,用于构造Service * Created by jzj on 2018/6/7. */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RouterProvider { } 复制代码
通过getProivider找到注解的方法,如果没有找到就报错,找到就通过反射调用返回
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的方式防止多线程环境出错。
findProvider通过拿到RouterProvider注解的方法,同时满足是静态方法,返回类型是类构造传进来的实现类型,另外没有参数的方法
@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注解的应该是静态无参数方法,且返回值类型为当前Class"); return NOT_FOUND; } } } return NOT_FOUND; } 复制代码
最后看一张ServiceLoader
的运行时图,有个总体的了解
学习优秀的代码可以促进自己的进步,传统的SPI机制会有运行时IO和反射的缺点,而WMRouter中就可以通过后台线程进行懒加载,做了缓存用空间换取时间,并且提供了获取实现类和实例的接口,也支持单例和方法的调用。