Android-SPI学习笔记

时间:2021-05-21

概述

SPI(Service Provider Interface, 服务提供方接口),服务通常是指一个接口或者一个抽象类,服务提供方是对这个接口或者抽象类的具体实现,由第三方来实现接口提供具体的服务。通过解耦服务与其具体实现类,使得程序的可扩展性大大增强,甚至可插拔。基于服务的注册与发现机制,服务提供者向系统注册服务,服务使用者通过查找发现服务,可以达到服务的提供与使用的分离。

可以将 SPI 应用到 Android 组件化中,很少直接使用 SPI,不过可基于它来扩展其功能,简化使用步骤。

基本使用

1. 在低层 module_common 中声明服务

public interface IPrinter { void print();}

2. 在上层 module 中实现服务

// module_a -- implementation project(':module_common')// com.hearing.modulea.APrinterpublic class APrinter implements IPrinter { @Override public void print() { Log.d("LLL", "APrinter"); }}// src/main/resources/META-INF/services/com.hearing.common.IPrinter// 可以配置多个实现类com.hearing.modulea.APrinter// ----------------------------------------------------------------//// module_b -- implementation project(':module_common')// com.hearing.moduleb.BPrinterpublic class BPrinter implements IPrinter { @Override public void print() { Log.d("LLL", "BPrinter"); }}// src/main/resources/META-INF/services/com.hearing.common.IPrintercom.hearing.moduleb.BPrinter

3. 在其它上层 module 中使用服务

// implementation project(':module_common')ServiceLoader<IPrinter> printers = ServiceLoader.load(IPrinter.class);for (IPrinter printer : printers) { printer.print();}

ServiceLoader.load

ServiceLoader 的原理解析从 load 方法开始:

public static <S> ServiceLoader<S> load(Class<S> service) { // 获取当前线程的类加载器 ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl);}public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) { // 创建 ServiceLoader 实例 return new ServiceLoader<>(service, loader);}

ServiceLoader实例创建

private LinkedHashMap<String,S> providers = new LinkedHashMap<>();private ServiceLoader(Class<S> svc, ClassLoader cl) { service = Objects.requireNonNull(svc, "Service interface cannot be null"); loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; reload();}// Clear this loader's provider cache so that all providers will be reloaded.public void reload() { providers.clear(); // 创建了一个懒迭代器 lookupIterator = new LazyIterator(service, loader);}

LazyIterator

ServiceLoader 实现了 Iterable 接口,可以使用 iterator/forEach 方法来迭代元素,其 iterator 方法实现如下:

public Iterator<S> iterator() { return new Iterator<S>() { Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator(); public boolean hasNext() { if (knownProviders.hasNext()) return true; return lookupIterator.hasNext(); } public S next() { // 如果 knownProviders 缓存中已经存在,则直接返回,否则加载 if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); } public void remove() { throw new UnsupportedOperationException(); } };}

上面使用了懒加载的方式,不至于一开始便去加载所有服务实现,否则反射影响性能。LazyIterator 类如下:

private static final String PREFIX = "META-INF/services/";private class LazyIterator implements Iterator<S> { Class<S> service; ClassLoader loader; Enumeration<URL> configs = null; Iterator<String> pending = null; String nextName = null; private LazyIterator(Class<S> service, ClassLoader loader) { this.service = service; this.loader = loader; } private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { // 获取服务配置文件 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; } // 解析服务配置 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) { // throw Exception } if (!service.isAssignableFrom(c)) { // throw Exception } try { S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { // throw Exception } throw new Error(); // This cannot happen } public boolean hasNext() { return hasNextService(); } public S next() { return nextService(); } public void remove() { throw new UnsupportedOperationException(); }}

总结

ServiceLoader 的原理比较简单,其实就是使用一个懒迭代器,用时加载的方式可以减少性能损耗,在加载新服务的时候通过解析服务配置文件获取配置的服务,然后通过类加载器去加载配置的服务实现类,最后将其实例返回。

SPI的优点

  • 只提供服务接口,具体服务由其他组件实现,接口和具体实现分离。

SPI的缺点

  • 配置过于繁琐
  • 具体服务的实例化由ServiceLoader反射完成,生命周期不可控
  • 当存在多个实现类对象时,ServiceLoader只提供了一个Iterator,无法精确拿到具体的实现类对象
  • 需要读取解析配置文件,性能损耗

以上就是Android-SPI学习笔记的详细内容,更多关于Android-SPI的资料请关注其它相关文章!

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章