Android-SPI 학습 노트
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.loadServiceLoader 의 원리 해석 은 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);
}
LazyIteratorServiceLoader 는 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 의 단점
설정 이 너무 번거롭다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.