Desugaring

권민주·2025년 11월 17일

안드로이드

목록 보기
23/23
post-thumbnail

1. 개념

1)개념

  • 컴파일러와 빌드 도구는 최신 Java 언어 기능이나 표준 라이브러리 API를 구형 안드로이드 런타임과 DEX 형식이 처리할 수 있는 형태로 변환. DEX 형식은 컴파일된 Java 코드를 안드로이드가 실행할 수 있는 바이트코드 형식.
  • 클래스 파일을 dex 코드로 변환하는 과정에서 desugar 단계가 먼저 적용되어 최신 언어 기능을 하위 버전에서도 동작하도록 변환. 이후 D8/R8이 최적화, 난독화 수행
  • 즉, 안드로이드 기본 빌드 도구들은 최신 언어나 API를 사용하더라도 낮은 Android API 레벨에서도 동작할 수 있게 지원

2)주요 도구

  • D8: dex 변환기이자 desugaring을 수행하는 도구
  • R8: D8 기능을 포함하며 코드 축소, 난독화, 최적화와 함께 desugaring을 수행
  • desugar_jdk_libs: 구형 플랫폼에서도 최신 API를 사용할 수 있도록 호환 코드를 제공하는 라이브러리. 앱에 포함됨.

3)동작 흐름

  • 개발자가 Java/Kotlin 코드 작성(예. 람다 사용, java.time.LocalDate 사용)
  • javac(또는 kotlinc)가 바이트코드(class 파일)를 생성
  • Android 빌드 도구(D8/R8)가 class 파일을 처리하면서 desugaring 수행
  • 보조 메서드/클래스로 변환하거나 적절한 바이트코드 패턴으로 치환
  • 호환 라이브러리(desugar_jdk_libs)의 구현으로 변경. 필요 시 해당 라이브러리 클래스를 앱에 자동으로 포함
  • 결과물은 dex 바이트코드로 변환되어 APK/AAB에 포함

즉, desugaring은 컴파일 후 또는 dex 변환 시에 일어나며, D8/R8이 주요 역할을 담당

4)Android 빌드

  • Android Gradle 플러그인은 특정 Java 8 언어 기능과 해당 기능을 사용하는 서드 파티 라이브러리를 사용해 내부적으로 지원
  • Android Gradle 플러그인 3.0.0 이상에서는 모든 Java 7 언어 기능과 플랫폼 버전에 따라 다른 Java 8 언어 기능의 일부를 지원
  • Android Gradle 플러그인 4.0.0 이상을 에서는 앱의 최소 API 수준 없이도 일부 Java 8 언어 API를 사용
  • Java 바이트코드 수준(sourceCompatibility / targetCompatibility)에 따라 기능과 빌드 속도가 다름
    • 바이트 코드 수준 6: 빌드 속도는 더 빠르고 기능은 더 적음
    • 바이트 코드 수준 7: 기능과 빌드 속도의 균형을 유지
    • 바이트 코드 8: 빌드 속도는 느리지만 기능은 풍부

5)사용 이유

  • 안드로이드는 다양한 기기와 오래된 런타임을 포함
  • Java의 최신 문법(예. 람다, 메서드 레퍼런스, 인터페이스의 default/static 메서드 등)이나 Java 표준 라이브러리의 새 API(java.time 등)를 그대로 실행할 수 없는 경우 존재
  • Desugaring은 호환성 문제를 해결해 최신 문법 또는 API를 넓은 범위의 Android 버전에서 사용할 수 있게 도움

2. Android Gradle 플러그인 3.0.0 이상

1)Java 8 사용 단계

Java 8 언어 기능을 사용하려면 다음 단계를 따르세요.

  • 3.0.0 이상으로 Android Gradle 플러그인을 업데이트
  • 소스 코드 또는 종속 항목을 통해 Java 8 언어 기능을 사용하는 각 모듈의 build.gradle 또는 build.gradle.kts 파일을 업데이트
//build.gradle.kts
android {
    ...
    // Java 8 언어 기능을
    // 사용하는 모듈에 대해서만 설정
    // (직접 작성한 코드나 의존 라이브러리 코드 중 어느 쪽이든)
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }
}
//build.gradle
android {
    ...
    // Java 8 언어 기능을
    // 사용하는 모듈에 대해서만 설정
    // (직접 작성한 코드나 의존 라이브러리 코드 중 어느 쪽이든)
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

2)지원되는 Java 8 기능

  • Android Gradle 플러그인 3.0.0 이상을 사용하여 앱을 빌드할 때, 일부 Java 8 언어 기능 지원 불가능
  • 다음 언어 기능들은 모든 API 수준에서 사용 가능
Java 8 언어 기능참고
람다 표현식Android는 람다 표현식의 직렬화를 지원 불가
메서드 참조-
Type AnnotationType Annotation 정보는 런타임이 아닌 컴파일 시간에만 확인 가능. API 수준 24 이하에서 TYPE은 지원, ElementType.TYPE_USE 또는 ElementType.TYPE_PARAMETER는 지원 불가
기본 및 정적 인터페이스 메서드-
Annotation 중복 선언-

🛑🛑🛑
사용할 수 있는 자바 8+ API: https://developer.android.com/studio/write/java8-support-table?hl=ko

3)지원 불가능한 Java 8 기능

  • MethodHandle.invoke 또는 MethodHandle.invokeExact 지원 불가. 해당 메소드들은 자바 리플렉션의 고급 형태로, java.lang.invoke.MethodHandle 클래스에서 제공. 메서드 직접 호출용 API.
  • 소스 코드나 모듈 종속 항목 중 하나라도 해당 메서드를 사용하는 경우,
    minSdkVersion 26 이상 지정 필요. 그러지 않으면 다음 오류가 발생
Dex: Error converting bytecode to dex:
Cause: signature-polymorphic method called without --min-sdk-version >= 26
  • 일부 서드파티 라이브러리는 내부적으로 invoke 또는 invokeExact 메서드 포함. minSdkVersion 25 이하 버전에서 해당 메서드를 사용하지 않아도 존재 자체만으로 문제 발생
  • 해당 라이브러리를 계속 사용하려면 코드 축소를 활성화하여 사용되지 않는 메서드 제거 필요. 제거할 수 없다면, 다른 라이브러리 사용해야 함

3. Android Gradle 플러그인 4.0.0 이상

1)추가 지원되는 기능

  • 앱의 최소 API 수준 제한 없이, 다양한 Java 8 언어 API를 사용할 수 있도록 지원

  • Java 언어 API도 desugaring 되도록 desugaring 엔진을 확장. java.util.streams 같이 최신 Android에서만 사용 가능한 표준 언어 API를 이전 Android 버전을 지원하는 앱에서도 포함 가능.

  • 다음 API를 지원

    • 순차적 스트림(java.util.stream)
    • java.time의 하위 집합
    • java.util.function
    • 최근 java.util.{Map,Collection,Comparator}에 추가된 사항
    • Optional (java.util.Optional, java.util.OptionalInt, java.util.OptionalDouble) 및 일부 새 클래스
    • 일부 java.util.concurrent.atomic에 추가된 사항(AtomicInteger, AtomicLong, AtomicReference의 새 메서드)
    • ConcurrentHashMap(Android 5.0 버그 수정 포함)
  • Android Gradle 플러그인 7.4.0 이상을 사용하면, desugar_jdk_libs 2.0.0 이상에서 여러 Java 11 언어 API 사용 가능

  • java.nio.file 패키지의 하위 집합 같은 추가 Java 11 API가 지원

  • 이전에 지원되지 않는 API의 구현을 포함하는 별도의 DEX 파일을 컴파일하고 이를 앱에 포함

🛑🛑🛑
디슈가링을 통해 사용할 수 있는 Java 11+ API:
https://developer.android.com/studio/write/java11-default-support-table?hl=ko

🛑🛑🛑

2)사용 단계

  • 모든 Android 플랫폼 버전에서 API를 지원 받으려면 4.0.0 이상으로 Android Gradle 플러그인을 업데이트
  • 앱 모듈의 build.gradle 또는 build.gradle.kts 파일에 다음을 포함
//build.gradle.kts 
android {
    defaultConfig {
        // Required when setting minSdkVersion to 20 or lower
        multiDexEnabled = true
    }

    compileOptions {
        // Flag to enable support for the new language APIs

        // For AGP 4.1+
        isCoreLibraryDesugaringEnabled = true
        // For AGP 4.0
        // coreLibraryDesugaringEnabled = true

        // Sets Java compatibility to Java 8
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
}

dependencies {
    // For AGP 7.4+
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
    // For AGP 7.3
    // coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.2.3")
    // For AGP 4.0 to 7.2
    // coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.9")
}
//build.gradle
android {
    defaultConfig {
        // Required when setting minSdkVersion to 20 or lower
        multiDexEnabled true
    }

    compileOptions {
        // Flag to enable support for the new language APIs
        coreLibraryDesugaringEnabled true
        // Sets Java compatibility to Java 8
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    // For AGP 7.4+
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
    // For AGP 7.3
    // coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.3'
    // For AGP 4.0 to 7.2
    // coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.9'
}
  • 아래의 경우 라이브러리 모듈의 build.gradle 또는 build.gradle.kts 파일에도 위 코드 필요
    • 라이브러리 모듈의 계측 테스트가 언어 API를 직접 또는 라이브러리 모듈이나 모듈의 종속 항목을 통해 사용하는 경우. 이는 누락된 API를 계측 테스트 APK에 제공
    • 라이브러리 모듈에서 린트를 독립적으로 실행하려는 경우. 린트에서 언어 API의 올바른 사용을 인식하여 거짓 경고 보고를 방지

4. Gradle 버전

Java 8+ API 라이브러리의 버전과 각 버전을 지원하는 최소 Android Gradle 플러그인 버전입니다.

버전최소 Android Gradle 플러그인 버전
1.1.94.0.0
1.2.37.3.0
2.0.37.4.0-alpha10

Java 8+ API 라이브러리 버전에 관한 자세한 내용은 desugar_jdk_libs GitHub 저장소의 CHANGELOG.md 파일을 참고하세요.

https://github.com/google/desugar_jdk_libs/blob/master/CHANGELOG.md

5. 대체 라이브러리

Desugaring 대신하여 보완용으로 사용할 수 있는 라이브러리는 다음과 같습니다.

1)ThreeTenABP

  • java.time 대체하여 날짜 및 시간에 사용
  • Desugaring 불가능하거나 D8/R8 호환성 문제 있을 때 사용
  • desugar_jdk_libs와 중복되면 충돌 위험 있음. 둘 중 하나만 사용.

2)StreamSupport

  • java.util.stream 대체하여 사용 가능
  • Desugaring을 쓰기 어려운 환경에서 스트림 연산(map, filter 등) 흉내 가능

6. Desugaring 사용이 제약되는 경우

1)빌드 도구 버전 제약

  • Android Gradle Plugin(AGP) 3.0 미만
  • D8/R8 사용하지 않고, 구형 툴 기반이라 desugaring 기능이 없는 경우.

2)MethodHandle 포함한 라이브러리 사용

  • 지원하지 않는 메서드이기에 minSdk 25 이하에서 사용하면 빌드 오류
  • R8로 제거 후 해당 라이브러리 사용 가능

3)빌드 속도 및 메모리 제약 환경

  • 제한된 환경에서는 desugar가 메모리나 시간 제약으로 실패
  • 대규모 프로젝트에서는 desugaring 비용이 커져 조절해야 하는 경우 존재

4)특정 테스트 환경에서 누락

  • 테스트 APK에는 java.time/stream 등이 자동 포함되지 않을 수 있어 테스트 환경에서만 오류 발생
  • 별도 라이브러리 추가 필요

5)Desugar_jdk_libs 라이브러리와의 충돌

  • 특정 라이브러리는 java.time자체 구현을 포함
  • desugar_jdk_libs와 충돌하여 클래스 중복 오류 발생
  • 둘 중 하나 제거 필요

출처: https://developer.android.com/?hl=ko, Chat GPT

profile
안드로이드 개발자:D

0개의 댓글