Android에서 SDK 종속성 해결

공유 패키지나 라이브러리에서 여러 가지 패키지에 종속성이 있지만 공유 패키지의 서로 다르고 호환되지 않는 버전에 종속되는 경우에는 종속성 이슈가 발생합니다. Gradle의 경우 build.gradle에 종속성 하나만 추가하면 필수 라이브러리가 빌드에 매끄럽게 포함되므로 Android 개발자의 삶을 더욱 편리하게 해줍니다. 하지만 두 개의 종속성이 동일한 라이브러리의 서로 다른 버전에 종속성을 가지고 있다면 어떻게 될까요?

Gradle은 두 종속성이 동일한 구성(즉, 앱 구성)에 속하는 경우 종속성을 자동으로 해결할 수 있습니다. 예를 들어 Audience Network Android SDK는 "exoplayer" 라이브러리에 종속됩니다. 다른 버전의 "exoplayer" 라이브러리를 추가해야 할 경우 Gradle이 더 높은 버전을 선택하여 포함합니다. 하지만 두 종속성이 서로 다른 구성(즉, 앱과 테스트)에 속하는 경우 Gradle이 오류를 발생시킵니다. 아래에서 여러 가지 충돌과 해결책을 살펴볼 것입니다.

필수 조건

계속하기 전에 Audience Network 시작하기Android 시작하기 가이드를 완료했는지 확인하세요.

종속성 충돌

1: 동일한 구성에 있는 종속성 내의 공유 라이브러리

2: 서로 다른 구성에 있는 종속성 내의 공유 라이브러리

종속성 충돌 해결책

1: Gradle이 대신 종속성 해결

2: 특정 버전의 종속성 제외

3: Gradle에서 충돌한 라이브러리를 명시적으로 정의

4: 라이브러리에서 강제 해결



종속성 충돌

1: 동일한 구성에 있는 종속성 내의 공유 라이브러리

다음의 예시를 고려해 보세요. 아래 코드에 있는 두 종속성은 동일한 구성에 속하고 "org.hamcrest:hamcrest-core" 라이브러리에 내부 종속성이 있습니다. 두 종속성이 내부적으로 동일한 라이브러리의 서로 다른 버전을 사용하므로 최상위 버전이 빌드에 포함됩니다. Gradle 동기화의 결과를 보면, 최종 빌드에서 hamcrest 라이브러리 버전이 1.1에서 1.3으로 자동 업그레이드되는 것으로 명확히 나와 있습니다.

// Depends on version 1.3 of org.hamcrest:hamcrest-core
androidTestImplementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19'  

2: 서로 다른 구성에 있는 종속성 내의 공유 라이브러리

두 종속성이 서로 다른 구성(예: 앱과 테스트 구성)에 속하는 경우 Gradle이 오류를 발생시킬 것입니다. 다음의 픽셀 코드를 고려해 보세요. 첫 번째 종속성이 앱 구성에 속하고 두 번째 종속성이 테스트 구성에 속합니다. 그러므로 프로젝트를 빌드하면 예외가 발생하며 실패하게 됩니다.

계측 테스트를 실행하면 메인 APK와 테스트 APK가 동일한 클래스 경로를 공유합니다. 메인 APK 및 테스트 APK가 동일한 라이브러리(예: Guava)를 사용하지만 버전이 서로 다른 경우에는 Gradle 빌드가 실패합니다. Gradle에서 실패를 탐지하지 못하면 앱이 테스트와 정상 실행 중에 다르게 동작할 수 있습니다(테스트와 정상 실행 중 충돌하는 경우 포함).

// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19' 

종속성 충돌 해결책

1: Gradle이 대신 종속성 해결

이 접근 방법은 가장 간단하지만 동일한 구성 내에 있는 종속성으로 제한됩니다. 예를 들어 모듈이 ExoPlayer의 특정 버전에 명시적으로 종속되지만 Audience Network SDK가 이미 다른 버전의 ExoPlayer를 포함했다고 생각해 보세요. 기본적으로 최상위 버전이 빌드에 포함됩니다. 아래의 픽셀 코드에서 Gradle 동기화의 결과를 보면, 최종 빌드에서 ExoPlayer 라이브러리 버전이 r2.4.2에서 2.7.3으로 자동 업그레이드되는 것으로 명확히 나와 있습니다.

implementation 'com.google.android.exoplayer:exoplayer-core:2.7.3'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.7.3'
...
// audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2
implementation 'com.facebook.android:audience-network-sdk:4.28.1' 

2: 특정 버전의 종속성 제외

대부분의 경우, 개발자는 최종적으로 빌드에 포함할 라이브러리 버전을 통제하고자 할 수 있습니다. build.gradle을 구성하면 통제가 가능합니다. 예를 들어 개발자가 Audience Network SDK에서 r2.4.2 대신 ExoPlayer r2.4.0의 더 낮은 버전을 선호한다면 "audience-network-sdk" 종속성을 선언할 때 해당 모듈을 제외할 수 있습니다. 제외 태그는 다른 구성에 있는 종속성에도 적용됩니다.

실제 프로젝트에서는 동일한 라이브러리의 서로 다른 버전이 있는 종속성이 많을 것입니다. 이 경우 각각의 종속성에 대해 제외 태그를 달아서 해당 라이브러리의 예상 버전을 포함할 수 있습니다.

시나리오 1: 동일한 구성의 종속성
implementation 'com.google.android.exoplayer:exoplayer-core:r2.4.0'
implementation 'com.google.android.exoplayer:exoplayer-dash:r2.4.0'
...
// audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2
implementation ('com.facebook.android:audience-network-sdk:4.28.1') {
    exclude group: 'com.google.android.exoplayer', module:'exoplayer-core'
    exclude group: 'com.google.android.exoplayer', module:'exoplayer-dash'
}
시나리오 2: 서로 다른 구성의 종속성
// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation ('org.mockito:mockito-core:1.10.19'){
    exclude group: 'org.hamcrest', module:'hamcrest-core'
}

3: Gradle에서 충돌한 라이브러리를 명시적으로 정의

이 방법은 서로 다른 구성에 있는 종속성의 충돌을 더욱 깔끔하게 해결할 수 있습니다. 이 경우, 서로 다른 구성 중 하나에 대해 최종 빌드에 포함하고자 하는 라이브러리의 버전을 명시적으로 언급해야 합니다.

이 접근 방법은 더욱 깔끔하게 충돌을 해결할 수는 있지만 junit, mockito와 같은 실제 종속성을 업데이트하는 동안 개발자가 충돌한 라이브러리도 업데이트해야 한다는 단점이 있습니다.

// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19' 
// Explictly mention that include version 1.3 of org.hamcrest:hamcrest-core
androidTestCompile 'org.hamcrest:hamcrest-core:1.3' 

4: 라이브러리에서 강제 해결

이는 하나의 구성에 대해 선언하는 대신 충돌을 해결하는 또 다른 방법으로, 모든 구성에 대해 강제 적용합니다. 아래의 예시에서 자체적인 "resolutionStrategy"를 module level build.gradle에 추가하여 특정 버전의 패키지를 포함하도록 강제 적용합니다(이때 종속성이 동일한 구성에 있든 서로 다른 구성에 있든 무관).

이 접근 방법은 다소 주의해서 사용해야 합니다. 첫 번째 예시에서 audience-network-sdk가 업데이트되고 이러한 라이브러리가 exoplayer-core 및 exoplayer-dash 라이브러리의 버전을 업데이트하더라도 이전 버전을 사용하도록 강제 적용될 것입니다. 이 시나리오는 두 번째 해결 방법에도 동일하게 적용되지만, 이 경우에는 하나의 구성이 아니라 모든 구성에 종속성 버전이 강제 적용됩니다.

시나리오 1: 동일한 구성의 종속성
android {
    configurations.all {
        resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-core:r2.4.0'
        resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-dash:r2.4.0'
    }
}
시나리오 2: 서로 다른 구성의 종속성
android {
    configurations.all {
        resolutionStrategy.force 'org.hamcrest:hamcrest-core:1.1'
    }
}

다음 단계

추가 리소스

시작하기 가이드

Audience Network 시작을 위한 기술 가이드

API 참고 자료

iOS용 Facebook SDK 참고 자료