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的资料请关注脚本之家其它相关文章!
android spi读写不通,Android-SPI学习笔记相关推荐
- Android 《第一行代码》学习笔记
目录 第一章 准备工作 一.Android 系统架构 二.Android 开发特点 1.四大组件 2.系统控件丰富 3.SQLite数据库 4.多媒体功能强大 5.地理位置定位 三.日志工具的使用 第 ...
- android 注册动态广播 注销_Android学习笔记(十一)BroadcastReceiver动态注册、注销示例...
在上一篇博文中简单介绍了一下BroadcastReceiver的相关知识点,本篇举一个在代码中动态的注册.注销BroadcastReceiver的栗子. 1.首先创建一个MyReceiver并继承Br ...
- android如何适配,Android适配全攻略(学习笔记总结)
Android适配全攻略(学习笔记总结) 一.为什么要进行屏幕适配 某厂商统计如下数据 2012年,支持Android的设备共有3997种 2013年,支持Android的设备共有11868种 201 ...
- 《Android平台开发之旅》学习笔记
第三章:Android应用程序组件 3.2 Android应用程序组件 Activity 活动--形象大使 Service 服务--老黄牛 BroadcastReceiver 广播接收器 --倾听者 ...
- android 点击edittext全选,学习笔记--EditText点击全选效果
需要上,EditText是有内容的,用户点击后就全选,最终达到方便编辑的效果. 方案一: 百度了一下,原来的思路是通过点击事件实现,但是效果很差,有时间点击中间能全选,有时候只是选中光标而已.估计是第 ...
- 《黑马程序员Android移动应用基础教程》学习笔记(2)
文章目录 Ch 2 Android常见界面布局 2.1 View视图 2.2 界面布局编写方式 2.2.1 在XML文件中编写布局 2.2.2 在Java代码中编写布局 2.3 常见界面布局 2.3. ...
- eeprom stm8l 擦除 读写_STM8L探索套件学习笔记-EEPROM(十一)
原标题:STM8L探索套件学习笔记-EEPROM(十一) 上节将到官方例程使用 EEPROM存储外围电路消耗的电流值,今天我们对 STM8L的EEPROM介绍下.STM8L带有的32K的FLASH和1 ...
- android源代码 abi,Android内核源码Abi目录学习笔记
好记性不如烂笔头,今天要学习的是Android中Abi目录下的代码.下面是基本的学习的笔记的汇总. 首先是include中的头文件的说明. 在cxxabi的头文件中主要需要掌握下面的几个点: 1.这个 ...
- Android的LitePal开源数据库框架学习笔记
LitePal是一款非常好用的数据库开源框架,它采用了对象关系映射(ORM)的模式,使用这个框架从创建数据库到操作数据库都变成极其简单,那么我们就开始学习吧.它的开源地址:https://github ...
最新文章
- jquery radio 取值
- RStudio中为R创建Notebook文件
- Jsoup:用Java也可以爬虫,怎么使用Java进行爬虫,用Java爬取网页数据,使用Jsoup爬取数据,爬虫举例:京东搜索
- IBASE read hierarchy
- MFC界面编程新思路--模仿MATLAB式的界面
- 使用RxJava从多个数据源获取数据
- python中decode的用法_python中list怎么使用decode方法设置编码
- 配置接口IP地址并通过默认路由配置实现全网互通。
- Java中getclass(),class()与iinstanceof的区别与联系
- JS 微信emoji表情数组
- 阿里datav地图json地址
- python求不规则图形面积_python计算不规则图形面积算法
- 克服弱点,愈发完美-自我篇——《人性的弱点》读后感
- 精准填报志愿、一分不浪费……靠谱吗?
- Joint Pose and Expression Modeling for Facial Expression Recognition 论文翻译
- HTG审核CODE键盘:老式建筑满足现代设施
- 刘备学Android目录
- 中国探月工程首席科学家欧阳自远:“嫦娥”月面图千真万确
- AI留给人类的时间已不多:《未来简史》作者赫拉利Nature来信
- 隐私计算头条周刊(12.11-12.17)