Android-SPI 학습 노트

6876 단어 Androidspi
개술
SPI(Service Provider Interface,서비스 제공 자 인터페이스)는 서 비 스 는 보통 하나의 인터페이스 나 추상 적 인 유형 을 말 하 는데 서비스 제공 자 는 이 인터페이스 나 추상 적 인 유형 에 대한 구체 적 인 실현 이 고 제3자 가 인터페이스 에 구체 적 인 서 비 스 를 제공한다.디 결합 서비스 와 구체 적 인 실현 류 를 통 해 프로그램의 확장 성 을 크게 강화 하고 심지어 삽입 할 수 있 습 니 다.서비스의 등록 과 발견 체 제 를 바탕 으로 서비스 제공 자 는 시스템 에 서 비 스 를 등록 하고 서비스 사용 자 는 검색 을 통 해 서 비 스 를 발견 하면 서비스의 제공 과 사용 의 분 리 를 이 룰 수 있다.
안 드 로 이 드 구성 요소 화 에 SPI 를 적용 할 수 있 습 니 다.SPI 를 직접 사용 하 는 경 우 는 드 물 지만 이 를 바탕 으로 기능 을 확장 하고 사용 절 차 를 간소화 할 수 있 습 니 다.
기본 사용
1.저층 modulecommon 성명 서비스

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.다른 상위 모듈 에서 서비스 사용

// 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 의 장점
4.567917.서비스 인터페이스 만 제공 하고 구체 적 인 서 비 스 는 다른 구성 요소 에 의 해 이 루어 지 며 인터페이스 와 구체 적 인 분 리 를 실현 한다.
SPI 의 단점
설정 이 너무 번거롭다.
  • 구체 적 인 서비스의 실례 화 는 ServiceLoader 에 의 해 반사 되 고 생명 주 기 는 통제 할 수 없다
  • 여러 개의 실현 대상 이 존재 할 때 ServiceLoader 는 하나의 Iterator 만 제공 하고 구체 적 인 실현 대상 을 정확하게 얻 지 못 한다
  • 분석 프로필 을 읽 어야 합 니 다.성능 손실
  • 이상 은 안 드 로 이 드-SPI 학습 노트 의 상세 한 내용 입 니 다.안 드 로 이 드-SPI 에 관 한 자 료 는 저희 의 다른 관련 글 을 주목 하 세 요!

    좋은 웹페이지 즐겨찾기