JDK
안드로이드 스튜디오를 사용해 빌드하면, 자체적으로 설정된 JDK를 사용한다. sudio.jdk 디렉토리가 있음. 또는, JetBrains Runtime(JBR)이라고 JDK확장버전을 안드로이드 스튜디오랑 함께 배포하게 되는데, 이를 사용하고 이 방법이 권장된다.
커맨드 라인으로 빌드하게 되면, JAVA_HOME을 찾고 없으면 PATH에 있는 java를 실행하게 되는데, 이 경우 안드로이드 스튜디오와 JDK가 안맞게 된다. 같이 사용한다면, 두 버전을 맞춰줘야 함.
빌드 할 때 처음에는 Gradle 데몬이 실행된다. 빌드가 완료되도 이 데몬은 사라지지 않으며 설정된 JDK버전과 Gradle 버전을 유지해서 반복된 빌드를 빠르게 실행해줌. 만약에 여러 프로젝트를 빌드하게 되면, 버전이 다른경우 새로운 데몬들이 실행된다.
안드로이드 스튜디오 상에서 jdk 설정은 File>Settings>Build, Execution, Deployment>Build Tools>Gradle에서 설정 가능.
안드로이드에서 JDK의 모든 API를 사용하진 못한다. build.gradle.kts내에 compileSdk 속성이 어떤 Android SDK 버전을 사용할지 나타냄.
android {
...
compileSdk = 33
}
만약, compileSdk에서 가능한 API지만 minSdk에서 사용할 수 없는 경우에는 desugaring이라는 과정을 거쳐 사용할 수 있다. 자세히 파본 내용은 아니지만, 대충 새로운 API를 바이트코드로 변환해서 사용 가능하게 만들어 주는 기능인 것 같다.
sourceCompatibility 속성은 컴파일 과정에서 사용가능한 자바 소스 feature의 버전을 표시한다. 이는 자바소스에만 영향을 준다. 코틀린과 자바의 컴파일 때 사용할 자바의 바이너리 feature는 targetCompatibility와 jvmTarget속성으로 정해준다. targetCompatibility는 디폴트로 sourceCompatibility와 같은 값을 갖는다. 명시해 주는 경우에는 이보다 같거나 큰 값이어야 한다. jvmTarget은 디폴트로 툴체인 버전이다.
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
Configure app module
application ID
android {
defaultConfig {
applicationId = "com.example.myapp
"
minSdk = 15
targetSdk = 24
versionCode = 1
versionName = "1.0"
}
...
}
application ID는 바로 package name과 묶여 있으며, 앱 내에서는 Context.getPackageName()으로 application ID를 얻어올 수 있다.
instrumentation test를 위한 application ID는 자동으로 뒤에 .test가 붙는다.
namespace
모든 안드로이드 모듈은 namespace를 갖는다. 이는 생성되는 리소스 클래스 R과 BuildConfig 클래스에 사용된다.
android {
namespace = "com.example.myapp
"
...
}
Dependencies
Version Catalogs
version catalog를 이용하는걸 권장하도록 변경되었다. version catalog는 gradle 폴더 및에 libs.versions.toml파일로 작성됨. [versions] 항목에 버전 gradle 빌드 스크립트에서 참조 할 수 있는 alias들을 정의하고, [libraries]항목에는 로컬 또는 원격 라이브러리들, [plugin]항목에는 플러그인에 대한 alias를 정의한다. 안드로이드 스튜디오에서 새로 프로젝트 생성시 다음과 같은 toml파일이 생성됨.
[versions]
agp = "8.4.1"
kotlin = "1.9.0"
coreKtx = "1.13.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
appcompat = "1.7.0"
material = "1.12.0"
activity = "1.9.0"
constraintlayout = "2.1.4"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
이렇게 작성된 version catalog를 build.gradle.kts에서 다음과 같이 사용한다.
plugins {
alias(libs.plugins.androidApplication)
}
dependencies {
implementation(libs.androidx.benchmark.macro)
implementation(libs.my.library)
}
plugin의 경우, libs.plugins가 앞에 붙은걸 볼 수 있고, dependencies의 경우 libs가 붙어있다. 이는 libs가 현재 카탈로그 이름이며, 그 뒤에 plugins가 붙어 [plugins]항목을 참조하고 있는 것이다. 마찬가지로 [versions]을 참조하고 싶다면 alias앞에 libs.versions를 접미어로 붙이면 된다.
dependencies block
build.gradle.kts의 dependencies 블럭에서는 라이브러리 의존성을 선언할 수 있다.
implementation : 가장 익숙한 라이브러리 의존성을 기술하는 configuration이다. 이는 해당 모듈에만 의존성을 가지고 있다는 의미로, 멀티 모듈 앱에서 라이브러리가 변경되어도 의존성을 가진 모듈들만 재컴파일이 된다.
api : implementation과 달리 다른 모듈까지 의존성을 가진다. 라이브러리 변경시, 모든 모듈이 컴파일 되기 때문에 사용에 주의. 그냥 implementation 써라.
compileOnly : implementaion과 api가 컴파일이나 아웃풋 모두에 적용된다면, 이건 compile시에만 적용된다. 예를들어, 컴파일 타임에 코드를 생성하는 annotation같은 경우 이를 사용하면된다.
runtimeOnly : compileOnly와 반대로 build output에만 적용된다. 안드로이드에서는 거의 안보이고 서버 어플리케이션의 경우 사용되기도 한다고.
ksp, kapt, annotationProcessor : annotation들과 다른 심볼들을 컴파일 전에 처리하기 위한 configuration들이다. ksp는 Kotlin Symbol Processor이며, 코틀린 컴파일 타임에 실행된다. kapt/apt는 코틀린이나 자바 컴파일 이전에 어노테이션을 처리하는 별도의 툴이다. 코틀린이 발전하면서 ksp가 구현된 것으로 ksp 사용이 권장되고 있으며, 기존 kapt도 ksp로 마이그레이션 하도록 권장된다. 이경우, ksp 구현이 안되어 있는 기능을 사용하는 경우 불가능 하기도 하다.
lintChecks : 음… 린트 관련같은데 잘 이해가 안되네.
lintPublish : lintChecks 이후에 나온거같다. 같은 기능인거 같은데, 이걸로 마이그레이션이 필요하다고 함.
특정한 build variant에 대해서만 dependency를 적용하고 싶은 경우, 해당 build variant를 Implementation앞에 접두어로 붙여준다. 무료버전인 “free”를 빌드한다면, 다음과 같이 쓸 수 있다.
dependencies {
freeImplementation("com.google.firebase:firebase-ads:21.5.1")
}
product flavor와 build type을 섞어서 다음과 같이 사용도 가능하다.
// Initializes a placeholder for the freeDebugImplementation dependency configuration.
val freeDebugImplementation by configurations.creating
dependencies {
freeDebugImplementation(project(":free-support"))
}
테스트의 경우 다음처럼 사용한다.
dependencies {
// Adds a remote binary dependency only for local tests.
testImplementation("junit:junit:4.12")
// Adds a remote binary dependency only for the instrumented test APK.
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
BOM
compose 사용시 BOM을 사용하여 버전을 일관되게 유지할 수 있다. version catalog 사용시에도 BOM 사용이 가능하다.
[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
build.gradle.kts에는 다음과 같이 추가한다.
dependencies {
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom) androidTestImplementation(composeBom)
// import Compose dependencies as usual
}
Multimodule Dependency
멀티모듈에 공통으로 적용하고 싶은 라이브러리는 루트에 있는 build.gradle.kts파일에 써서 적용할 수 있다. 예를 들어, AGP같은경우 다같이 사용한다. 루트에 의존성을 쓰는 경우, “apply false”를 뒤에 추가해준다. 이는 AGP를 gradle에게 알려주지만 루트빌드시 이를 사용하지 말라는 표시이다. 일반적으로 루트 빌드 스크립트는 이 플러그인 외에 비어있다.
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false
}
코드 블럭 저장에 문제가 있어 다음 포스팅으로 넘김.