你们好,我系苍王。
这个系列已经出到了第30章节了,已经开通了已经有一年半的时间了。
在一年半里,创建了千人的QQ大群,很多编辑也找过我编辑图书,也有同行找过我合做出公众号。可是我的的时间是有限的,并不可能所有愿望都实现。
那么上一年就选了一件对这辈子很是有意义的事情,和电子工业出版社出版一本关于组件化技术的书。很是感谢陈晓猛编辑找到了我一同出书,也感谢在技术群中不断深讨组件化技术的群友们。
书中重点介绍了使用组件化思想去搭建一个Android项目,介绍了组件化的思想,组件化的编程技术,多人管理组件化,组件化的编译优化,以及对项目演进的思想感悟。
此书并非只是介绍技术,也包含了我对一些生活的理解,技术思惟的理解。
京东、淘宝和当当都可以购买,有兴趣能够点击连接就能够跳转了。php
如下是我这个系列的相关文章,有兴趣能够参考一下,能够给个喜欢或者关注个人文章。html
[Android]如何作一个崩溃率少于千分之三噶应用app--章节列表java
关于spi,其全名是Service Provider Interfaces。ServiceLoder用于动态加载接口实现类的加载器。
1.其能够动态加载一些继承某个接口的实体类
2.须要将实体类名声明到resources/META-INF/services目录,能够取巧使用@AutoService
3.其加载的并非单例,并且构造方法不带任何参数,由于ServiceLoader底层是使用了反射的机制来加载。
4.加载文件顺序应该是按照resources/META-INF/services目录中顺序加载,因此若是使用@AutoService是不可控的。
5.ServiceLoader继承iterator接口,能够像List同样遍历实体类。
6.其实际也是经过反射来实现初始化操做,使用接口的方式使模块,ServiceLoader装载器、启动器之间更加解耦。
7.比较适合于组件化中,模块入口初始化的统一加载场景。android
如下借用一个Modular框架中的加载为例
1.声明接口编程
public interface IModule {
/** * 模块初始化,只有组建时才调用,用于开启子线程轮训消息 */
void init();
/** * 模块ID * * @return 模块ID */
int getModuleId();
/** * 模块注册并链接成功后,能够作如下事情: * <p> * 一、注册监听事件 * 二、发送事件 * 三、注册服务 * 四、调用服务 */
void afterConnected();
}
复制代码
2.使用@AutoService,将全路径名写到resources/META-INF/services目录json
@AutoService(IModule.class)
public class Module extends BaseModule {
@Override
public void afterConnected() {
}
@Override
public int getModuleId() {
return Constants.MODULE_B;
}
}
复制代码
3.使用ServiceLoder加载模块服务器
@Override//只有当是组建单独运行时,才当Application运行,才会走onCreate,最终打包时根本没有这个类
public void onCreate() {
super.onCreate();
……
//自动注册服务器(若是是独立模块内声明只有一个IModule)
ServiceLoader<IModule> modules = ServiceLoader.load(IModule.class);
mBaseModule = (BaseModule) modules.iterator().next();
//模块初始化
mBaseModule.init();
……
}
复制代码
public void onCreate() {
super.onCreate();
……
//SPI自动注册服务(主module装载的时候,已经将所有META_INF文件合并)
ServiceLoader<IModule> modules = ServiceLoader.load(IModule.class);
for (IModule module : modules) module.afterConnected();
}
复制代码
使用看起来很是简单,咱们研究一下ServiceLoader源码的特别之处。架构
//调用静态load方法来初始化XXXInterface接口信息。
public static <S> ServiceLoader<S> load(Class<S> service) {
//获取当前线程ClassLoader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
//构建ServiceLoader对象
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
return new ServiceLoader<>(service, loader);
}
private ServiceLoader(Class<S> svc, ClassLoader cl) {
//检测接口是否否存在
service = Objects.requireNonNull(svc, "Service interface cannot be null");
//检测classloader是否为空,为空使用系统classloader加载器
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
// Android-changed: Do not use legacy security code.
// On Android, System.getSecurityManager() is always null.
// acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
public void reload() {
//清理provides配置加载器
providers.clear();
//初始化懒加载迭代器
lookupIterator = new LazyIterator(service, loader);
}
复制代码
能够看到使用的是懒加载的迭代器,只有迭代器被使用的时候,才会真正初始化每个继承接口的实体类。app
//判断是否有下一个对象
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
//PREFIX = "META-INF/services/"
//加载配置地址
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;
}
//解析config的节点
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) {
fail(service,
// Android-changed: Let the ServiceConfigurationError have a cause.
"Provider " + cn + " not found", x);
// "Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
// Android-changed: Let the ServiceConfigurationError have a cause.
ClassCastException cce = new ClassCastException(
service.getCanonicalName() + " is not assignable from " + c.getCanonicalName());
fail(service,
"Provider " + cn + " not a subtype", cce);
// fail(service,
// "Provider " + cn + " not a subtype");
}
try {
//反射初始化类,并转化成接口
S p = service.cast(c.newInstance());
//记录映射关系
providers.put(cn, p);
//返回接口实体
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
复制代码
ServiceLoader实际仍是经过路径名反射来完成,只是其经过配置到META_INF中的目录文件来完成解耦。
ServiceLoader使用场景是用于不须要区分module加载顺序的状况,若是有加载顺序,还须要从新排序后再初始化方法,这里最后仍是使用优先级机制。
在开编的时候已经介绍了SPI的优势和局限性,跳出SPI,依然能作一个更灵活更可控的加载机制,例如json脚本,xml脚本动态更新。