SPI原理解析

前言

SPI全称Service Provider Interface,是Java提供的一种动态服务发现机制。通过SPI机制,我们可以直接跨模块查找到想要的接口实现类,从而避免不必要的模块间依赖,降低模块之间的耦合性。这对Android组件间通信非常重要的意义。试想一下如果有一个组件能提供这样一种能力,我们的组件通信是否会变得很容易: 通过一个接口,我可以很容易的查找到这个接口在任意模块下的任意一个或多个实现类的对象.

使用

创建

首先创建一个ISPIService接口:

1
2
3
public interface ISPIService {
void execute();
}

接着实现接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SpiImpl1 implements ISPIService {
@Override
public void execute() {
Log.e("ISPIService----------","SpiImpl1");
}
}

public class SpiImpl2 implements ISPIService {
@Override
public void execute() {
Log.e("ISPIService----------","SpiImpl2");
}
}

目录结构:

ServiceLoader加载

1
2
3
4
5
6
7
8
ServiceLoader<ISPIService> serviceLoader = ServiceLoader.load(ISPIService.class);
Iterator<ISPIService> iterator = serviceLoader.iterator();
while (iterator.hasNext()){
Log.e("ISPIService-------",iterator.toString());
ISPIService service = iterator.next();
Log.e("ISPIService-------",service.getClass().getName());
service.execute();
}

打印结果:

1
2
3
4
5
6
2022-06-07 14:53:33.960 7422-7422/com.myapplication E/ISPIService-------: java.util.ServiceLoader$1@6ab81e8
2022-06-07 14:53:33.960 7422-7422/com.myapplication E/ISPIService-------: com.myapplication.spi.SpiImpl1
2022-06-07 14:53:33.960 7422-7422/com.myapplication E/ISPIService----------: SpiImpl1
2022-06-07 14:53:33.960 7422-7422/com.myapplication E/ISPIService-------: java.util.ServiceLoader$1@6ab81e8
2022-06-07 14:53:33.960 7422-7422/com.myapplication E/ISPIService-------: com.myapplication.spi.SpiImpl2
2022-06-07 14:53:33.960 7422-7422/com.myapplication E/ISPIService----------: SpiImpl2

所以,我们只需要把具体实现的接口类,添加到文件内,就可以通过ServiceLoader加载。

再看下apk内目录结构:

注册

通过上面的图,我们可以知道,我们的service是注册再resources文件夹下的,并且接口的全路径名是文件的名字。这是其中一种注册方式,还有另外一种注册方式,Google的AutoService的注解方式注册。

1
2
3
4
5
6
7
@AutoService(ISPIService.class)
public class SpiImpl1 implements ISPIService {
@Override
public void execute() {
Log.e("ISPIService----------","SpiImpl1");
}
}

效果图如下:

ServiceLoader源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//这里能看出,META-INF/services/是两个文件夹,我在使用过程中,建成了一个,导致一直失败
private static final String PREFIX = "META-INF/services/";
private class LazyIterator implements Iterator<S>
{
private boolean hasNextService() {
if (nextName != null) {
return true;
}
// configs = URL = jar:file:/data/app/com.myapplication-1/base.apk!/META-INF/services/com.myapplication.spi.ISPIService
if (configs == null) {
try {
//service的文件全路径名
String fullName = PREFIX + service.getName();
//loader == null就是系统SystemClassLoader.loader,否则自己的loader
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
}
}
// pending 就是fullName文件中的实现类
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
//解析service文件
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) {
}

try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
x);
}
}

public boolean hasNext() {
// Android-changed: do not use legacy security code
/* if (acc == null) { */
return hasNextService();
/*
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
*/
}
// 取实现对象
public S next() {
// Android-changed: do not use legacy security code
/* if (acc == null) { */
return nextService();
/*
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
*/
}

public void remove() {
throw new UnsupportedOperationException();
}

}
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
//读文件啊,解析文件内容,按行截取,放到list里,首行排到第一位0,顺序依次
private Iterator<String> parse(Class<?> service, URL u) throws ServiceConfigurationError
{
InputStream in = null;
BufferedReader r = null;
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
return names.iterator();
}

private int parseLine(Class<?> service, URL u, BufferedReader r, int lc, List<String> names) throws IOException, ServiceConfigurationError
{
String ln = r.readLine();
if (ln == null) {
return -1;
}
int ci = ln.indexOf('#');
if (ci >= 0) ln = ln.substring(0, ci);
ln = ln.trim();
int n = ln.length();
if (n != 0) {
if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
fail(service, u, lc, "Illegal configuration-file syntax");
int cp = ln.codePointAt(0);
if (!Character.isJavaIdentifierStart(cp))
fail(service, u, lc, "Illegal provider-class name: " + ln);
for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
cp = ln.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
fail(service, u, lc, "Illegal provider-class name: " + ln);
}
if (!providers.containsKey(ln) && !names.contains(ln))
names.add(ln);
}
return lc + 1;
}

AutoService源码

首先AutoService就是个注解:其主要的实现逻辑在AutoServiceProcessor中,他也是一个注解处理器。

1
2
3
4
5
6
7
@Documented
@Retention(CLASS)
@Target(TYPE)
public @interface AutoService {
/** Returns the interfaces implemented by this service provider. */
Class<?>[] value();
}

我们主要看一下AutoServiceProcessor.process->processImpl方法:
如果上一次循环中注解处理器已经处理完了,就调用generateConfigFiles生成MEATA_INF配置文件;如果上一轮没有处理就调用processAnnotations处理注解。返回true就代表改变或者生成语法树中的内容;返回false就是没有修改或者生成,通知编译器这个Round中的代码未发生变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class AutoServiceProcessor extends AbstractProcessor {

private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//判断roundEnv,是否完成了。
if (roundEnv.processingOver()) {
generateConfigFiles();
} else {
processAnnotations(annotations, roundEnv);
}
return true;
}
}


//注解类处理,通过RoundEnvironment的getElementsAnnotatedWith(AutoService.class)拿到所有的标注了AutoService注解的元素
private void processAnnotations(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {

Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);

log(annotations.toString());
log(elements.toString());

for (Element e : elements) {
// TODO(gak): check for error trees?
TypeElement providerImplementer = (TypeElement) e;
AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class).get();
Set<DeclaredType> providerInterfaces = getValueFieldOfClasses(annotationMirror);
if (providerInterfaces.isEmpty()) {
error(MISSING_SERVICES_ERROR, e, annotationMirror);
continue;
}
for (DeclaredType providerInterface : providerInterfaces) {
TypeElement providerType = MoreTypes.asTypeElement(providerInterface);

log("provider interface: " + providerType.getQualifiedName());
log("provider implementer: " + providerImplementer.getQualifiedName());

if (checkImplementer(providerImplementer, providerType)) {
providers.put(getBinaryName(providerType), getBinaryName(providerImplementer));
} else {
String message = "ServiceProviders must implement their service provider interface. "
+ providerImplementer.getQualifiedName() + " does not implement "
+ providerType.getQualifiedName();
error(message, e, annotationMirror);
}
}
}
}
// 生成MEATA_INF配置文件
private void generateConfigFiles() {
Filer filer = processingEnv.getFiler();
//从providers提取接口的实现类,是个集合kes
for (String providerInterface : providers.keySet()) {
//根据keys里接口信息,拼接文件名,一个接口对应一个文件
String resourceFile = "META-INF/services/" + providerInterface;
log("Working on resource file: " + resourceFile);
try {
SortedSet<String> allServices = Sets.newTreeSet();
try {

FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "",
resourceFile);
//如果之前文件已经存在,就读取文件数据
Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
log("Existing service entries: " + oldServices);
allServices.addAll(oldServices);
} catch (IOException e) {

}
//读取provides中指定的接口对应的子类集合
Set<String> newServices = new HashSet<String>(providers.get(providerInterface));
if (allServices.containsAll(newServices)) {
log("No new service entries being added.");
return;
}
// 将老的数据和从providers中读取的数据合并
allServices.addAll(newServices);

FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "",
resourceFile);
OutputStream out = fileObject.openOutputStream();
// 将数据重新写入文件
ServicesFiles.writeServiceFile(allServices, out);
out.close();
} catch (IOException e) {
return;
}
}
}

如果实现类再不同的模块下,那每个模块都会生成自己的META-INF/services/….文件,但最终打包完成时会合并所有的META-INF文件目录

参考资料

组件化之AutoService使用与源码解析