Android-SPI学习笔记
概述
SPI(ServiceProviderInterface,服务提供方接口),服务通常是指一个接口或者一个抽象类,服务提供方是对这个接口或者抽象类的具体实现,由第三方来实现接口提供具体的服务。通过解耦服务与其具体实现类,使得程序的可扩展性大大增强,甚至可插拔。基于服务的注册与发现机制,服务提供者向系统注册服务,服务使用者通过查找发现服务,可以达到服务的提供与使用的分离。
可以将SPI应用到Android组件化中,很少直接使用SPI,不过可基于它来扩展其功能,简化使用步骤。
基本使用
1.在低层module_common中声明服务
publicinterfaceIPrinter{ voidprint(); }
2.在上层module中实现服务
//module_a--implementationproject(':module_common') //com.hearing.modulea.APrinter publicclassAPrinterimplementsIPrinter{ @Override publicvoidprint(){ Log.d("LLL","APrinter"); } } //src/main/resources/META-INF/services/com.hearing.common.IPrinter //可以配置多个实现类 com.hearing.modulea.APrinter //----------------------------------------------------------------// //module_b--implementationproject(':module_common') //com.hearing.moduleb.BPrinter publicclassBPrinterimplementsIPrinter{ @Override publicvoidprint(){ Log.d("LLL","BPrinter"); } } //src/main/resources/META-INF/services/com.hearing.common.IPrinter com.hearing.moduleb.BPrinter
3.在其它上层module中使用服务
//implementationproject(':module_common') ServiceLoaderprinters=ServiceLoader.load(IPrinter.class); for(IPrinterprinter:printers){ printer.print(); }
ServiceLoader.load
ServiceLoader的原理解析从load方法开始:
publicstaticServiceLoaderload(Classservice){ //获取当前线程的类加载器 ClassLoadercl=Thread.currentThread().getContextClassLoader(); returnServiceLoader.load(service,cl); } publicstaticServiceLoaderload(Classservice,ClassLoaderloader){ //创建ServiceLoader实例 returnnewServiceLoader<>(service,loader); }
ServiceLoader实例创建
privateLinkedHashMapproviders=newLinkedHashMap<>(); privateServiceLoader(Class svc,ClassLoadercl){ service=Objects.requireNonNull(svc,"Serviceinterfacecannotbenull"); loader=(cl==null)?ClassLoader.getSystemClassLoader():cl; reload(); } //Clearthisloader'sprovidercachesothatallproviderswillbereloaded. publicvoidreload(){ providers.clear(); //创建了一个懒迭代器 lookupIterator=newLazyIterator(service,loader); }
LazyIterator
ServiceLoader实现了Iterable接口,可以使用iterator/forEach方法来迭代元素,其iterator方法实现如下:
publicIteratoriterator(){ returnnewIterator(){ Iterator>knownProviders=providers.entrySet().iterator(); publicbooleanhasNext(){ if(knownProviders.hasNext())returntrue; returnlookupIterator.hasNext(); } publicSnext(){ //如果knownProviders缓存中已经存在,则直接返回,否则加载 if(knownProviders.hasNext())returnknownProviders.next().getValue(); returnlookupIterator.next(); } publicvoidremove(){ thrownewUnsupportedOperationException(); } }; }
上面使用了懒加载的方式,不至于一开始便去加载所有服务实现,否则反射影响性能。LazyIterator类如下:
privatestaticfinalStringPREFIX="META-INF/services/"; privateclassLazyIteratorimplementsIterator{ Classservice; ClassLoaderloader; Enumerationconfigs=null; Iterator pending=null; StringnextName=null; privateLazyIterator(Class service,ClassLoaderloader){ this.service=service; this.loader=loader; } privatebooleanhasNextService(){ if(nextName!=null){ returntrue; } if(configs==null){ try{ //获取服务配置文件 StringfullName=PREFIX+service.getName(); if(loader==null) configs=ClassLoader.getSystemResources(fullName); else configs=loader.getResources(fullName); }catch(IOExceptionx){ fail(service,"Errorlocatingconfigurationfiles",x); } } while((pending==null)||!pending.hasNext()){ if(!configs.hasMoreElements()){ returnfalse; } //解析服务配置 pending=parse(service,configs.nextElement()); } nextName=pending.next(); returntrue; } privateSnextService(){ if(!hasNextService())thrownewNoSuchElementException(); Stringcn=nextName; nextName=null; Class>c=null; try{ //反射通过类加载器加载指定服务 c=Class.forName(cn,false,loader); }catch(ClassNotFoundExceptionx){ //throwException } if(!service.isAssignableFrom(c)){ //throwException } try{ Sp=service.cast(c.newInstance()); providers.put(cn,p); returnp; }catch(Throwablex){ //throwException } thrownewError();//Thiscannothappen } publicbooleanhasNext(){ returnhasNextService(); } publicSnext(){ returnnextService(); } publicvoidremove(){ thrownewUnsupportedOperationException(); } }
总结
ServiceLoader的原理比较简单,其实就是使用一个懒迭代器,用时加载的方式可以减少性能损耗,在加载新服务的时候通过解析服务配置文件获取配置的服务,然后通过类加载器去加载配置的服务实现类,最后将其实例返回。
SPI的优点
- 只提供服务接口,具体服务由其他组件实现,接口和具体实现分离。
SPI的缺点
- 配置过于繁琐
- 具体服务的实例化由ServiceLoader反射完成,生命周期不可控
- 当存在多个实现类对象时,ServiceLoader只提供了一个Iterator,无法精确拿到具体的实现类对象
- 需要读取解析配置文件,性能损耗
以上就是Android-SPI学习笔记的详细内容,更多关于Android-SPI的资料请关注毛票票其它相关文章!