라이브러리 작성자는 앱 개발자가 고품질 최종 사용자 환경을 유지하면서 라이브러리를 앱에 쉽게 통합할 수 있도록 해야 합니다. 라이브러리가 추가 설정 없이 Android 최적화와 호환되는지 확인하거나 라이브러리가 Android에서 사용하기에 적합하지 않을 수 있음을 문서화해야 합니다.
이 문서는 게시된 라이브러리의 개발자를 대상으로 하지만, 대규모 모듈화된 앱의 내부 라이브러리 모듈 개발자에게도 유용할 수 있습니다.
앱 개발자로서 Android 앱 최적화에 대해 알아보려면 앱 최적화 사용 설정을 참고하세요. 사용하기에 적합한 라이브러리에 관해 알아보려면 라이브러리 현명하게 선택하기를 참고하세요.
리플렉션 대신 코드 생성 사용
가능하면 리플렉션 대신 코드 생성 (codegen)을 사용하세요. 코드 생성과 리플렉션은 프로그래밍 시 상용구 코드를 피하는 일반적인 접근 방식이지만 코드 생성은 R8과 같은 앱 최적화 프로그램과 더 호환됩니다.
- 코드 생성기를 사용하면 빌드 프로세스 중에 코드가 분석되고 수정됩니다. 컴파일 시간 이후에는 주요 수정사항이 없으므로 최적화 프로그램은 궁극적으로 필요한 코드와 안전하게 삭제할 수 있는 코드를 알고 있습니다.
- 리플렉션을 사용하면 런타임에 코드가 분석되고 조작됩니다. 코드는 실행될 때까지 실제로 최종 확정되지 않으므로 최적화 프로그램은 안전하게 삭제할 수 있는 코드를 알지 못합니다. 런타임 중에 리플렉션을 통해 동적으로 사용되는 코드가 삭제되어 사용자에게 앱 비정상 종료가 발생할 수 있습니다.
최신 라이브러리에서는 리플렉션 대신 코드 생성을 사용합니다. Room, Dagger2 등에서 사용하는 공통 진입점은 KSP를 참고하세요.
복습이 허용되는 경우
리플렉션을 사용해야 하는 경우 다음 중 하나로만 리플렉션해야 합니다.
- 특정 타겟팅 유형 (특정 인터페이스 구현자 또는 하위 클래스)
- 특정 런타임 주석을 사용하는 코드
이 방식으로 리플렉션을 사용하면 런타임 비용이 제한되고 타겟팅된 소비자 유지 규칙을 작성할 수 있습니다.
이 구체적이고 타겟팅된 형태의 리플렉션은 Android 프레임워크 (예: 활동, 뷰, 드로어블을 인플레이션할 때)와 AndroidX 라이브러리 (예: WorkManager
ListenableWorkers
또는 RoomDatabases
를 생성할 때) 모두에서 볼 수 있는 패턴입니다. 반면 Gson의 개방형 리플렉션은 Android 앱에서 사용하기에 적합하지 않습니다.
보관 라이브러리의 보관 규칙 유형
보관 규칙에는 두 가지 유형이 있습니다.
- 소비자 유지 규칙은 라이브러리가 반영하는 모든 항목을 유지하는 규칙을 지정해야 합니다. 라이브러리가 리플렉션이나 JNI를 사용하여 코드나 클라이언트 앱에서 정의한 코드를 호출하는 경우 이러한 규칙은 유지해야 하는 코드를 설명해야 합니다. 라이브러리는 앱 유지 규칙과 동일한 형식을 사용하는 소비자 유지 규칙을 패키징해야 합니다. 이러한 규칙은 라이브러리 아티팩트(AAR 또는 JAR)로 번들링되며 라이브러리가 사용될 때 Android 앱 최적화 중에 자동으로 사용됩니다. 이러한 규칙은
build.gradle.kts
(또는build.gradle
) 파일의consumerProguardFiles
속성으로 지정된 파일에 유지됩니다. 자세한 내용은 소비자 유지 규칙 작성을 참고하세요. - 라이브러리 빌드 유지 규칙은 라이브러리가 빌드될 때 적용됩니다. 빌드 시점에 라이브러리를 부분적으로 최적화하기로 결정한 경우에만 필요합니다.
라이브러리의 공개 API가 삭제되지 않도록 해야 합니다. 그렇지 않으면 공개 API가 라이브러리 배포에 표시되지 않아 앱 개발자가 라이브러리를 사용할 수 없습니다. 이러한 규칙은
build.gradle.kts
(또는build.gradle
) 파일의proguardFiles
속성으로 지정된 파일에 유지됩니다. 자세한 내용은 AAR 라이브러리 빌드 최적화를 참고하세요.
소비자 유지 규칙 작성
일반적인 유지 규칙 안내 외에도 라이브러리 작성자를 위한 권장사항이 있습니다.
- 부적절한 전역 규칙을 사용하지 마세요. 라이브러리를 사용하는 모든 앱에 영향을 미치므로
-dontobfuscate
또는-allowaccessmodification
과 같은 전역 설정을 라이브러리의 소비자 유지 규칙 파일에 넣지 마세요. - 라이브러리의 소비자 유지 규칙 파일에서
-repackageclasses
를 사용하지 마세요. 하지만 라이브러리 빌드를 최적화하려면 라이브러리의 빌드 유지 규칙 파일에서<your.library.package>.internal
과 같은 내부 패키지 이름과 함께-repackageclasses
를 사용하면 됩니다. 이렇게 하면 라이브러리를 사용하는 앱이 최적화되지 않은 경우에도 라이브러리가 더 효율적으로 작동할 수 있지만, 앱도 최적화해야 하므로 일반적으로 필요하지 않습니다. 라이브러리 최적화에 관한 자세한 내용은 라이브러리 작성자를 위한 최적화를 참고하세요. proguard-android-optimize.txt
에 정의된 속성과 중복될 수 있더라도 라이브러리가 작동하는 데 필요한 속성을 라이브러리의 keep 규칙 파일에 선언합니다.- 라이브러리 배포에 다음 속성이 필요한 경우 라이브러리의 보관 규칙 파일에 유지하고 라이브러리의 소비자 보관 규칙 파일에는 유지하지 마세요.
AnnotationDefault
EnclosingMethod
Exceptions
InnerClasses
RuntimeInvisibleAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeInvisibleTypeAnnotations
RuntimeVisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
Signature
- 라이브러리 작성자는 런타임에 주석을 사용하는 경우 소비자 유지 규칙에
RuntimeVisibleAnnotations
속성을 유지해야 합니다. - 라이브러리 작성자는 소비자 유지 규칙에서 다음 전역 옵션을 사용하면 안 됩니다.
-include
-basedirectory
-injars
-outjars
-libraryjars
-repackageclasses
-flattenpackagehierarchy
-allowaccessmodification
-overloadaggressively
-renamesourcefileattribute
-ignorewarnings
-addconfigurationdebugging
-printconfiguration
-printmapping
-printusage
-printseeds
-applymapping
-obfuscationdictionary
-classobfuscationdictionary
-packageobfuscationdictionary
AAR 라이브러리
AAR 라이브러리의 소비자 규칙을 추가하려면 Android 라이브러리 모듈의 빌드 스크립트에서 consumerProguardFiles
옵션을 사용하세요. 자세한 내용은 라이브러리 모듈 생성에 관한 안내를 참고하세요.
Kotlin
android { defaultConfig { consumerProguardFiles("consumer-proguard-rules.pro") } ... }
Groovy
android { defaultConfig { consumerProguardFiles 'consumer-proguard-rules.pro' } ... }
JAR 라이브러리
JAR로 제공되는 Kotlin/Java 라이브러리와 함께 규칙을 번들로 묶으려면 최종 JAR의 META-INF/proguard/
디렉터리에 파일 이름을 사용하여 규칙 파일을 넣습니다.
예를 들어 <libraryroot>/src/main/kotlin
에 코드가 있는 경우 <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
에 소비자 규칙 파일을 넣으면 규칙이 출력 JAR의 올바른 위치에 번들로 제공됩니다.
규칙이 META-INF/proguard
디렉터리에 있는지 확인하여 최종 JAR이 규칙을 올바르게 번들링하는지 확인합니다.
AAR 라이브러리 빌드 최적화 (고급)
일반적으로 라이브러리 빌드 시 가능한 최적화는 매우 제한적이므로 라이브러리 빌드를 직접 최적화할 필요는 없습니다. 라이브러리가 애플리케이션의 일부로 포함되는 애플리케이션 빌드 중에만 R8이 라이브러리의 모든 메서드가 사용되는 방식과 전달되는 매개변수를 알 수 있습니다. 라이브러리 개발자는 최적화하기 전에 라이브러리 및 앱 빌드 시간 모두에서 동작을 유지하면서 최적화의 여러 단계를 추론해야 합니다.
빌드 시에 라이브러리를 최적화하려면 Android Gradle 플러그인에서 지원합니다.
Kotlin
android { buildTypes { release { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } configureEach { consumerProguardFiles("consumer-rules.pro") } } }
Groovy
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } configureEach { consumerProguardFiles "consumer-rules.pro" } } }
proguardFiles
의 동작은 consumerProguardFiles
과 매우 다릅니다.
proguardFiles
는 빌드 시간에getDefaultProguardFile("proguard-android-optimize.txt")
와 함께 자주 사용되어 라이브러리 빌드 중에 라이브러리의 어떤 부분을 유지해야 하는지 정의합니다. 최소한 공개 API입니다.- 반면
consumerProguardFiles
는 라이브러리로 패키징되어 나중에 라이브러리를 사용하는 앱을 빌드하는 동안 발생하는 최적화에 영향을 미칩니다.
예를 들어 라이브러리에서 리플렉션을 사용하여 내부 클래스를 생성하는 경우 proguardFiles
와 consumerProguardFiles
모두에서 유지 규칙을 정의해야 할 수 있습니다.
라이브러리의 빌드에서 -repackageclasses
를 사용하는 경우 클래스를 라이브러리 패키지 내부의 하위 패키지로 리패키징합니다. 예를 들어 -repackageclasses 'internal'
대신 -repackageclasses
'com.example.mylibrary.internal'
를 사용합니다.
다양한 R8 버전 지원 (고급)
R8의 특정 버전을 타겟팅하도록 규칙을 맞춤설정할 수 있습니다. 이렇게 하면 최신 R8 버전을 사용하는 프로젝트에서 라이브러리가 최적으로 작동할 수 있으며, 기존 규칙은 이전 R8 버전이 있는 프로젝트에서 계속 사용할 수 있습니다.
타겟팅된 R8 규칙을 지정하려면 AAR의 classes.jar
내에 있는 META-INF/com.android.tools
디렉터리 또는 JAR의 META-INF/com.android.tools
디렉터리에 규칙을 포함해야 합니다.
In an AAR library:
proguard.txt (legacy location, the file name must be "proguard.txt")
classes.jar
└── META-INF
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
In a JAR library:
META-INF
├── proguard/<ProGuard-rule-files> (legacy location)
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
META-INF/com.android.tools
디렉터리에는 규칙이 작성된 R8 버전을 나타내는 r8-from-<X>-upto-<Y>
형식의 이름이 지정된 하위 디렉터리가 여러 개 있을 수 있습니다. 각 하위 디렉터리에는 R8 규칙이 포함된 파일이 하나 이상 있을 수 있으며 파일 이름과 확장자는 자유롭게 지정할 수 있습니다.
-from-<X>
및 -upto-<Y>
부분은 선택사항이며 <Y>
버전은 배타적입니다. 버전 범위는 일반적으로 연속적이지만 중복될 수도 있습니다.
예를 들어 r8
, r8-upto-8.0.0
, r8-from-8.0.0-upto-8.2.0
, r8-from-8.2.0
는 타겟팅된 R8 규칙의 집합을 나타내는 디렉터리 이름입니다. r8
디렉터리의 규칙은 모든 R8 버전에서 사용할 수 있습니다. r8-from-8.0.0-upto-8.2.0
디렉터리의 규칙은 버전 8.0.0부터 버전 8.2.0(비포함)까지 R8에서 사용할 수 있습니다.
Android Gradle 플러그인은 이 정보를 사용하여 현재 R8 버전에서 사용할 수 있는 모든 규칙을 선택합니다. 라이브러리가 타겟팅된 R8 규칙을 지정하지 않으면 Android Gradle 플러그인이 기존 위치(AAR의 경우 proguard.txt
, JAR의 경우 META-INF/proguard/<ProGuard-rule-files>
)에서 규칙을 선택합니다.