DEV Community

Sewon Ann
Sewon Ann

Posted on

Google AutoService를 사용할 땐 의존성 순서를 조심해라.

구글의 AutoService 를 사용하면 손쉽게 ServiceLoader 를 사용할 수 있다.

AutoService 프로젝트 소개 문서에 나와있듯,

@AutoService(Processor.class)
final class MyProcessor implements Processor {
  // …
}

위와 같이 Processor라는 인터페이스의 구현체를 선언하면, 아래와 같이 동적으로 Processor 인터페이스의 구현체를 모을 수 있다.

ServiceLoader.load(Processor::class.java, Processor::class.java.classLoader)

안드로이드의 경우라면 library project에 이런 구현체를 만들어두고, app project에서 library project들이 제공하는 구현체들을 쉽게 수집할 수 있다.

그런데 어느날, 특정 라이브러리의 구현체를 수집하지 못하는 문제가 발생했다. 다행히 개발 단계에서 바로 크래시가 나서 금방 찾았다. 7개의 구현체가 나와야 하는데, 6개의 구현체가 나왔다.

코드의 문제인가 싶었지만, 코드는 별달리 수정한게 없다. local jvm test를 돌리니 문제가 없었는데 instrumentation test를 돌리니 실패했다. 아흐.

예상되는 문제의 원인은 문제가 되는 라이브러리는 직접 의존하면서 간접 의존하기도 하는데, 뭔가 간접 의존이 먼저 인식이 되어서 그런 것 같다.

즉 아래와 같은 상황에서, gradle이 libB의 직접 의존을 먼저 인식하면 문제가 없고, 간접 의존을 먼저 인식해서 직접 의존을 건너뛰어버리면(이 표현이 맞는지 모르겠다) 문제가 생긴다.

app
 ㄴ libA
     ㄴ libB
 ㄴ libB

해결 방법은 의존 선언 위치를 바꾸면 된다. 하지만 너무 위험하고, 안정성이 떨어지는 해결책이다.


//실패 : libB의 간접 의존을 먼저 인식
dependencies {
  implementation project(":libA")
  implementation project(":libB")
}

//성공 : libB의 직접 의존을 먼저 인식
dependencies {
  implementation project(":libB")
  implementation project(":libA")
}

gradle의 의존 선언 순서에 따라 runtime의 동작이 성공하고 실패하는 아주 위험한 상황이다. 결국 이 경우엔 AutoService 적용을 포기했다.

하지만 한 모듈 안에 구현체가 몰려있다면 위와 같은 문제는 발생하지 않기 때문에 꼭 쓰지 말아야 할 필요도 없다. 주의해서 잘 사용하자.

Top comments (0)