박상권의 삽질블로그

[안드로이드/Android]빌드시간 최적화하기(빌드시간 70%감소) 본문

IT/Android-TIP (한글)

[안드로이드/Android]빌드시간 최적화하기(빌드시간 70%감소)

박상권 2017.09.28 00:35



안드로이드 개발을 마치고 에뮬레이터나 실제 기기에서 테스트해보려면 우리는 빌드를 하고 설치해야만 합니다.

이때 빌드시간이 어떤상황이냐에 따라서 몇십분이 될수도 있고 단 몇십초가 될 수도 있습니다.


Eclipse에서 Android Studio로 넘어오신분들은 아시겠지만 제일 처음에 넘어왔을때 불만이 빌드속도가 너무 느리다는것이었습니다.

그래서 빌드속도를 빠르게 하기위하여 'android studio speed up', 'android build time' 등으로 각종 팁을 찾아보고 적용해 보았습니다.

daemon을 활성화하거나 병렬로 실행하거나 기타 등등의 여러 방법이 있었지만 실제로는 빌드시간이 감소하는 효과를 보지 못했습니다.


하지만 아래 소개할 이 방법은 확실하게 빌드시간을 줄일 수 있습니다.

저는 70%의 빌드시간 감소를 경험했습니다.


안드로이드 빌드시간이 감소하면 안좋을 수도 있습니다.

빌드를 기다리는 시간동안 개발자들은 밀린 카카오톡 메세지를 확인하거나 화장실을 가거나 볼일을 보곤 합니다.

그런데 빌드시간이 줄어들면 이런 시간이 줄어들어서 줄인시간만큼 우린 더 많이 일해야할지도 모릅니다.

이 글을 대표님은 좋아하지만 직원들은 싫어합니다.




제가 적용한 방법은 Google I/O 17에서 소개된 내용을 참고해서 적용했습니다.

https://www.youtube.com/watch?v=7ll-rkLCtyk

그 세션에서 소개한 모든 방법을 적용하지는 않았고 제게 맞는 방법으로 맞추어서 적용했습니다.

각 항목별로 빌드시간을 줄이는법에 대해 알아보겠습니다.


바쁘신분(읽기 귀찮으신분)들은 Sample 프로젝트를 다운받아서 확인해보셔도 좋습니다.

https://github.com/ParkSangGwon/BuildTimeSpeedUpSample



1. Avoid legacy multidex

65,536개 이상의 함수를 사용하게되면 우리는 Multidex를 사용합니다.

예전에는 Multidex가 생소했지만 요즘에는 라이브러리들을 많이 사용하면서 65,536개 이상은 훌쩍 넘기게되고 Multidex를 자주 사용합니다.

여기서 Android 5.0(API 레벨 21)이상이냐 미만이냐에 따라서 Multidex를 다루는 방법이 달라집니다.


  • Android 5.0(API 레벨 21) 미만의 플랫폼 버전에서는 앱 코드 실행을 위해 Dalvik 런타임을 사용합니다. 기본적으로, Dalvik에서는 APK당 하나의 classes.dex 바이트코드 파일로 앱을 제한합니다.

  • Android 5.0(API 레벨 21) 이상에서는 ART라는 런타임을 사용합니다. 기본적으로 이 런타임은 APK 파일로부터 여러 개의 DEX 파일을 로드하는 것을 지원합니다.

즉, Android 5.0(API 레벨 21) 이상에서는 Multidex가 훨씬 빠릅니다.

그래서 minSdkVersion를 21로 설정해주면 아주 빠른 빌드속도를 경험할 수 있습니다.

하지만 대부분 minSdkVersion를 14나 16으로 설정해주기때문에 함부로 minSdkVersion을 21로 설정할 수 없습니다.

그럴때 Flavor를 활용해줍니다.

[IT/Android-TIP (한글)] - [안드로이드]Flavors로 같은 소스코드 다른앱 만들기


dev/prd로 Flavor를 나누어서 개발할때만 minSdkVersion를 21로 설정하고 배포할때는 원래의 minSdkVersion를 적용하도록 설정해주면 됩니다.

dev {
dimension "stage"
minSdkVersion 21
}
prd {
dimension "stage"
versionCode new Date().format('yyMMddHHmm').toInteger()
}


free {
dimension "mode"
applicationIdSuffix ".free"
}
paid {
dimension "mode"
applicationIdSuffix ".paid"
}

여기서는 기존에 flavor로 무료앱과 유료앱을 나누어서 운영하고 있다가 dev/prd를 추가한 예시입니다.

dimension을 stage/mode로 나누면 각각 dev/prd - free/paid 중에 선택되서 결정되게 됩니다.

여기에 원래의 debug/release까지 있으니 build할수있는 variant는 아래와 같습니다.


  • devFreeDebug

  • devFreeRelease

  • devPaidDebug

  • devPaidRelease

  • prdFreeDebug

  • prdFreeRelease

  • prdPaidDebug

  • prdPaidRelease

실제 개발할때는 devFreeDebug, devPaidDebug를 사용할 것이고 스토어에 올릴때는 prdFreeRelease, prdPaidRelease를 사용할 것입니다.

사실상 나머지 다른 build variant들은 만들어졌지만 쓸모없는것들입니다.


그럴때 ignore를 사용해주면 좋습니다.


android.variantFilter { variant ->
def buildName = variant.buildType.name;
def flavorName = variant.getFlavors().get(0).name;

if (flavorName == 'dev' && buildName == 'release'
|| flavorName == 'prd' && buildName == 'debug') {
variant.setIgnore(true);
}
}

사용하지 않을것 같은 variant들은 없애주면 실제로 사용하게될 4개의 variant들만 남습니다.





Google I/O 발표내용에 따르면이 방법을 적용하면 아래와 같은 빌드시간 감소를 줄일 수 있다고 합니다.








2. Disable multi-APK

APK파일의 크기가 클때 abi별/density별로 APK파일을 나누어서 용량을 줄일 수 있습니다.

https://developer.android.com/studio/build/configure-apk-splits.html


실제로 많은 서비스에서 x86용, x86_64용, armeabi-v7a용, arm64-v8a용으로 APK를 분할해서 제공하고 있습니다.

아래와 같은 방식으로 split을 할 수 있습니다.

splits {
abi {
enable true
reset()
include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
universalApk false
}
}

그런데 이런 split작업은 빌드시간을 증가시키는 원인이 됩니다.

그러므로 우리는 개발단계에서는 abi별로 split하는 작업을 하지 않게 만들어주어야 합니다.

1번의 방법에서 우리는 dev/prd flavor를 만들었습니다.

그러므로 prd일때만 split해주도록 설정하면 개발에서는 split하지않고 배포할때만 split하도록 설정할 수 있습니다.

android.variantFilter { variant ->
def flavorName = variant.getFlavors().get(0).name;
splits.abi.enable = (flavorName == 'prd');
}









3. Minimize included resources / Disable PNG Crunching

개발을 하면서 우리는 보통 1개의 테스트폰만을 사용합니다.

한국어이면서 xxxhdpi이거나 특정 해상도인 테스트폰을 사용할것입니다.

그럴때 resource를 특정 언어와 해상도로 고정해두면 빌드시간을 줄일 수 있습니다.


또한 안드로이드에서 기본으로 PNG를 최적화하는 기능을 끄면 빌드시간을 줄일 수 있습니다.

dev flavor에서 아래와 같이 선언해주면 간단히 적용할 수 있습니다.

dev {
dimension "stage"
minSdkVersion 21
aaptOptions.cruncherEnabled = false
resConfigs "ko", "xxxhdpi"
}











4. Avoid inadvertent changes

앱을 개발하면서 불필요하게 증가시키는 요소들을 줄일 수 있습니다.

앱의 versionCode를 빌드시간으로 정의해서 사용하는 경우도 많이 볼 수 있습니다.

def buildDateTime = new Date().format('yyMMddHHmm').toInteger()
android {
defaultConfig {
versionCode buildDateTime
}
}

하지만 계속 새로 갱신이 되면서 Manifest가 변경되고 이는 빌드시간을 증가시키는 원인이 됩니다.

이러한 방법도 빌드시간을 줄일 수 있습니다.


개발할때는 versionCode를 특정값으로 fix하고 release할때만 실제로 적용시키면 됩니다.

dev {
dimension "stage"
versionCode 100
}

prd {
dimension "stage"
versionCode new Date().format('yyMMddHHmm').toInteger()
}












5. Fabric

요즘에는 Fabric을 활용하여 사용자분석이나 오류로그를 분석하는 경우가 많습니다.

[IT/Android-TIP (한글)] - [안드로이드]오류 리포팅 서비스 비교(Google Analytics, URQA, Userhabit, Fabric)


개발단계에서는 fabric이 사실 필요 없습니다.

그럴때 fabric을 사용하지 않는것만으로도 빌드시간을 줄일 수 있습니다.

buildTypes {

debug {

...
ext.enableCrashlytics = false
        ...
}

}

혹은 개발에서도 Fabric을 사용해야하는경우는 Build id를 update해주는 기능을 꺼주어도 됩니다.

buildTypes {

debug {
ext.alwaysUpdateBuildId = false
}

}







6. 라이브러리 버전

라이브러리를 사용할때 버전을 끝까지 명시해주는것이 좋습니다.

x.x.+ 로 사용할 경우 24시간마다 최신버전을 알아오기때문에 이는 빌드시간이 늘어나는 원인이 됩니다.


dependencies {
compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
compile 'com.android.support:appcompat-v7:26.+'
}







결과


실제 프로젝트에 적용했을때 73%의 빌드시간 감소효과를 경험 했습니다.

측정방법은 Android Studio에서 [Build]->[Clean Project]후 [Build]->[Build APK]의 방식으로 수행했습니다.


평균 

110초 

30초 

72.99% 감소 

1회 

112초 

26초 

 

2회 

103초 

25초 

 

3회 

116초 

33초 

 

4회 

110초 

34초 

 

5회 

107초 

30초 

 


이 빌드시간은 프로젝트의 크기에 따라 다르고 측정하는 PC에 따라 다릅니다.

각자의 프로젝트와 PC환경에서 위와 같은 방법을 적용하기 전/후의 시간을 측정해서 비교해봐야 할것입니다.


이미 구현되어있는 Sample프로젝트를 참고하시기 바랍니다.

https://github.com/ParkSangGwon/BuildTimeSpeedUpSample


이 Sample프로젝트가 유용하셨다면 오른쪽 상단에 Star버튼도 꾹 눌러주시면 감사하겠습니다.



어때요? 

이제 빌드시간 70%줄이고 아낀시간으로 그만큼 더 불꽃코딩 할 수 있겠죠?



모두 행복한 코딩하시기 바랍니다.

감사합니다.


2 Comments
댓글쓰기 폼