android spi读写不通,Android-SPI学习笔记

概述

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.APrinter

public 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.BPrinter

public class BPrinter implements IPrinter {

@Override

public void print() {

Log.d("LLL", "BPrinter");

}

}

// src/main/resources/META-INF/services/com.hearing.common.IPrinter

com.hearing.moduleb.BPrinter

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

// implementation project(':module_common')

ServiceLoader printers = ServiceLoader.load(IPrinter.class);

for (IPrinter printer : printers) {

printer.print();

}

ServiceLoader.load

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

public static ServiceLoader load(Class service) {

// 获取当前线程的类加载器

ClassLoader cl = Thread.currentThread().getContextClassLoader();

return ServiceLoader.load(service, cl);

}

public static ServiceLoader load(Class service, ClassLoader loader) {

// 创建 ServiceLoader 实例

return new ServiceLoader<>(service, loader);

}

ServiceLoader实例创建

private LinkedHashMap providers = new LinkedHashMap<>();

private ServiceLoader(Class 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 iterator() {

return new Iterator() {

Iterator> 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 {

Class service;

ClassLoader loader;

Enumeration configs = null;

Iterator pending = null;

String nextName = null;

private LazyIterator(Class 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的资料请关注脚本之家其它相关文章!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值