Gradle
Android Studio의 빌드 시스템은 Gradle이라는 툴체인 프레임워크를 기반으로 하며 Android Gradle Plugin(AGP)는 안드로이드 앱을 빌드하는 과정을 내부적으로 처리해줍니다.
Gradle과 Android Gradle 플러그인은 Android 스튜디오와 독립적으로 실행됩니다. 즉, Android 스튜디오 내에서, 컴퓨터의 명령줄에서, 또는 Android 스튜디오가 설치되지 않은 컴퓨터(예: 지속적 통합 서버)에서 Android 앱을 빌드할 수 있습니다.
툴체인 프레임워크란?
툴체인(toolchain) 프레임워크는 소프트웨어 개발 및 컴파일링 과정을 관리하고 자동화하기 위한 도구의 모음입니다. 주로 하드웨어 아키텍처에 따라 코드를 컴파일하고 빌드하여 실행 가능한 바이너리 형태로 변환하는 과정을 관리하며, 이는 주로 효율적인 소프트웨어 개발 및 배포를 위해 필요합니다.
DSL
Gradle은 빌드 스크립트를 작성하기 위해 DSL(Domain Specific Language) 언어를 사용합니다.
초기 버전의 Gradle에서는 Groovy DSL을 사용했습니다. 이를 활용하면 기존 Java 라이브러리와의 호환성을 유지하면서 Gradle을 활용할 수 있습니다.
Gradle 3.0 버전부터는 Kotlin DSL을 도입했습니다. Kotlin DSL은 코틀린의 언어적인 특징을 그대로 가져와 가독성이 좋고 간략한 코드를 사용할 수 있게 해줍니다. Kotlin DSL을 사용했을 때의 장점은 아래와 같습니다.
- 컴파일 타임에 에러 확인
- 코드 탐색
- 자동 완성
- 구문 강조
- IDE의 지원으로 향상된 편집환경
- 소스코드와 동일한 언어의 사용
gradle 파일을 아래와 같이 변경하면 Gradle 스크립트에 Kotlin DSL을 사용할 수 있습니다.
build.gradle -> build.gradle.kts
dependencies {
// Groovy DSL을 사용
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
// Kotlin DSL을 사용
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
}
Build Process
빌드는 결국 프로젝트를 APK와 AAB로 변환하는 과정입니다. 아래의 그림은 이런 과정을 보여줍니다.
이제부터 이 과정을 차근차근 알아 보겠습니다.
Application Recources
애플리케이션의 리소스 파일들은 AAPT(Android Asset Packging Tool)에 의해 해당 리소스 파일을 참조할 수 있는 ID가 부여되어 R.java 클래스 안에 담기게 됩니다.
그렇기 때문에 우리는 아래와 같이 R 클래스에서 여러 리소스들을 참조할 수 있습니다.
// XML 레이아웃 파일에서 정의한 UI 요소를 참조
val myButton: Button = findViewById(R.id.my_button)
val myTextView: TextView = findViewById(R.id.my_textview)
AIDL(Android Interface Definition Language)
AIDL은 안드로이드 플랫폼에서 서로 다른 프로세스 간에 통신하기 위해 사용되는 인터페이스 정의 언어입니다. AIDL을 사용하여 서로 다른 앱 또는 프로세스 사이에서 데이터와 메소드를 주고받을 수 있습니다.
프로세스간 통신을 위해 작성된 AIDL 파일은 클라이언트와 서비스 간에 주고받을 데이터의 형태를 명시하고 있습니다. 안드로이드 빌드 시스템은 이 파일을 자동으로 Java 인터페이스로 변환합니다.
Application Source Code
우리가 작성한 자바나 코틀린의 (1) 소스 코드와 (2) R.java, (3) Java Interface는 javac와 kotlinc 컴파일러를 통해 자바 바이트 코드, 즉 .class 파일로 변환됩니다.
Dex로의 변환
이제는 이렇게 변환된 .class 파일과 3rd Party Libraries 파일을 .dex 파일로 만들어야 합니다.
3rd Party Libraries란?
소프트웨어 개발에서 사용되는 외부 제공자가 제작한 코드의 모음입니다. 이 라이브러리는 개발자가 직접 작성하는 코드 대신 사용하여 소프트웨어 개발을 더 빠르고 효율적으로 진행할 수 있도록 돕습니다.
서드 파티 라이브러리에는 라이브러리 모듈과 AAR, JAR이 포함됩니다.
JAR Java 플랫폼에서 사용되는 일반적인 라이브러리 파일 형식입니다. 안드로이드에서도 Java 코드를 포함한 라이브러리로 사용될 수 있습니다.
AAR 안드로이드 플랫폼에서 사용되는 라이브러리 파일 형식입니다. AAR 파일은 JAR 파일을 확장하여 안드로이드 리소스까지 포함하고 있습니다.
.dex 파일로 변환은 안드로이드 앱의 최적화 및 보안 강화를 위해 중요한 단계입니다.
Code shrinking: 우리의 소스 코드나 라이브러리의 코드에서 실제로 사용되지 않는 클래스들, 필드들, 메소드들, 속성들을 제거합니다.
Resource shrinking: 사용되지 않는 리소스들을 제거합니다. 이는 Code shrinking이 진행된 후에 진행되는 것이 유리합니다. Code shrinking이 끝난 후에 어떤 리소스들이 참조되며 사용될 지를 정확히 판단할 수 있기 때문입니다.
Obfuscation(난독화): 난독화는 앱의 코드를 의도적으로 어렵게 만들어서 앱을 역공학 분석으로부터 보호하고 코드의 가독성을 낮추는 작업을 의미합니다. 이를 통해 앱의 소스 코드가 누출되는 것을 어렵게 하거나, 악성 사용자가 앱의 동작을 파악하거나 변조하기 어렵도록 합니다. 다만 이런 난독화 과정을 거치고 나면 앱이 난독화가 된 후에 Crash가 나거나 우리가 알아볼 수 없게 됩니다. 그러기 때문에 난독화 도구들은 난독화를 수행할 때 매핑 파일을 생성해줍니다. R8 컴파일러는 mapping.txt 라는 매핑 파일을 생성합니다.
멀티덱스(MultiDex) 지원: 안드로이드 앱이 커질수록 단일 .dex 파일에 모든 클래스를 포함하기 어려울 수 있습니다. 이 때, 멀티덱스 기능을 통해 여러 개의 .dex 파일을 사용할 수 있습니다. 멀티덱스는 .dex 파일을 여러 개로 나누어 앱 실행 시 필요한 부분만 로딩하여 메모리 절약을 도모합니다.
Optimization(최적화): 코드를 분석해서 최종 APK파일의 크기를 줄이는 최적화를 진행할 수 있습니다. 예를 들어, R8 컴파일러는 절대 수행될 수 없는 else 문을 스스로 제거해서 최적화를 진행해주기도 합니다.
위의 과정을 진행하게 도와주는 도구로는 Proguard와 R8이 있습니다.
Proguard
Proguard는 위에서 설명했던 .class 파일을 .dex 파일로 변환하는 도구입니다.
위의 그림에서 desugar라는 개념이 새롭게 나옵니다.
"Desugar"는 자바 8 이상의 기능을 지원하기 위해 이전 안드로이드 버전에서는 사용할 수 없었던 새로운 자바 기능을 대체하거나 변환하는 과정을 말합니다. 안드로이드 앱 개발에서 자바 8의 편리한 기능을 활용하기 위해 필요한 작업 중 하나입니다.
다만 이는 매우 큰 단점을 야기했습니다. 바로 더 긴 빌드 시간입니다.
D8
이 문제를 해결하기 위해, Android Studio 3.2에서 Google은 Dex 컴파일러를 "D8"이라는 새로운 컴파일러로 대체했습니다. 주요 아이디어는 desugaring 변환을 제거하고 이를 .class2dex 컴파일의 일부로 만들어 빌드 시간을 더 빠르게 만드는 것이었습니다.
D8이란 이름은 "Dope(멋지다)" 라고 중얼거린 것을 "Dope(멋지다) * 8" 이라고 잘못 기록해서 생겨났다고 합니다 ㅎㅎ
R8
R8는 D8의 파생 버전입니다. 둘은 같은 코드베이스를 공유합니다. 그러나 R8은 추가적인 문제를 해결합니다. D8과 마찬가지로 R8은 오래된 Dalvik/ART에서 새로운 Java 8 기능을 사용할 수 있게 해줍니다. 하지만 그것만이 아닙니다.
R8은 올바른 opcode 사용을 돕습니다. 안드로이드 개발자들에게서 앱 개발과 유지보수를 어렵게 만드는 요소 중 가장 큰 것은 단편화입니다. Android를 실행하는 장치 수가 너무 많습니다. 심지어 Play 스토어를 마지막으로 확인한 때에는 2만 개가 넘는 장치가 있었습니다.
안드로이드 단편화(Android Fragmentation)는 안드로이드 운영체제가 다양한 디바이스 제조사와 모델, 버전에 대응하며 다양한 하드웨어와 소프트웨어 환경에서 실행되기 때문에 발생하는 현상을 나타냅니다. 다양한 크기의 화면, 다양한 하드웨어 성능, 다양한 안드로이드 버전 및 업데이트 정책 등으로 인해 동일한 앱이 서로 다른 환경에서 다르게 동작하거나 보이는 경우가 발생할 수 있습니다.
R8의 가장 큰 장점은 앱에서 특정 디바이스나 API 레벨을 지원하기 위해 필요한 opcodes만 남기고 .dex 코드를 최적화하는 것입니다.
APK
남은 과정은 apk 파일을 만드는 과정입니다.
Apkbuilder - .apk로 패키징합니다.
Jarsigner - debug 또는 release용 keystore로 signing을 합니다.
zpalign (release mode) - zipalign tool을 이용해 align 시킵니다.
마무리
이렇게 해서 우리가 작성한 파일이 .apk 파일에 패키징 되는 것까지의 과정을 알아 보았습니다. 다음 게시글에서는 이렇게 생성한 apk가 안드로이드 운영체제에서 어떻게 실행되는지를 알아 보려고 합니다.
Reference
'Android' 카테고리의 다른 글
Android 4대 컴포넌트 - Service (0) | 2023.08.21 |
---|---|
안드로이드의 부팅과 애플리케이션의 실행 (0) | 2023.08.20 |
UI 상태 저장과 ViewModel로 상태 저장하기 (0) | 2023.08.05 |
작업 및 백 스택 이해 (0) | 2023.08.04 |
Android의 Intent (인텐트) (0) | 2023.07.23 |