Android-SPI学习笔记

编辑: admin 分类: Android 发布时间: 2021-11-29 来源:互联网
目录
  • 概述
  • 基本使用
    • 1. 在低层 module_common 中声明服务
    • 2. 在上层 module 中实现服务
    • 3. 在其它上层 module 中使用服务
  • ServiceLoader.load
    • ServiceLoader实例创建
      • LazyIterator
        • 总结
          • SPI的优点
          • 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<IPrinter> printers = ServiceLoader.load(IPrinter.class);
        for (IPrinter printer : printers) {
          printer.print();
        }

        ServiceLoader.load

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

        public static <S> ServiceLoader<S> load(Class<S> service) {
          // 获取当前线程的类加载器
          ClassLoader cl = Thread.currentThread().getContextClassLoader();
          return ServiceLoader.load(service, cl);
        }
        
        public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
          // 创建 ServiceLoader 实例
          return new ServiceLoader<>(service, loader);
        }

        ServiceLoader实例创建

        private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
        
        private ServiceLoader(Class<S> 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<S> iterator() {
          return new Iterator<S>() {
            Iterator<Map.Entry<String,S>> 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<S> {
          Class<S> service;
          ClassLoader loader;
          Enumeration<URL> configs = null;
          Iterator<String> pending = null;
          String nextName = null;
        
          private LazyIterator(Class<S> 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的资料请关注海外IDC网其它相关文章!

        【文章出处:http://www.yidunidc.com/hkzq.html欢迎转载】