IT_Programming/Dev Tools

Android Gradle 빌드 속도 높이기

JJun ™ 2015. 9. 30. 06:57



 출처

 : https://blog.asamaru.net/2015/09/29/android-gradle-builds-speed-up/

 : http://blog.daum.net/skyapp10047/125

 : https://medium.com/@henen/android-studio-속도-향상-스크립트-9887106ad970

 : https://www.liaohuqiu.net/posts/speed-up-your-build/




안드로이드 개발을 하면서 여러가지 불만이 있지만 그중에 가장 맘에 들지않는 것 중 하나가 빌드 속도이다.
프로젝트의 규모에 따라 다르겠지만 xcode와의 빌드 시간을 비교하면 전체 빌드의 시간은 비슷한 것 같으나
수시로 하게 되는 빌드는 안드로이드 스튜디오가 훨씬 느리게 느껴진다.

안드로이드 개발시 빌드에 사용되는 Gradle은 많은 유연성을 제공하지만 빌드에 많은 프로세스 비용이 든다는 것이 단점이다.
그래서 빌드 속도를 높일 수 있는 방법을 찾아서 적용해 보았다.


그리고 빌드 시간에 크게 영향을 주는 것 중에는 proguard와 multidex가 포함된다.
proguard는 코드 최적화/난독화 등을 위한 처리이며 multidex는 65K이상의 메소드를 사용할 경우에 대한 대응책으로
사용되며 자세한 내용은 Building Apps with Over 65K Methods에서 참고하면 된다.

메소드 수가 어떻게하면 65K를 넘나 싶을 수 있지만 금방 넘어선다. 라이브러리를 막 추가하다보면 금방 문제를 만나게 된다.
(proguard 등을 통해 메소드 수를 줄일 수 있다). 이 글에서도 나와있는 것이지만 빌드 속도 문제를 떠나서도
최대한 multidex 를 사용하는 상황을 만들지 않는 것이 좋다. 그리고 나는 proguard를 항상 켜둔 상태도 개발을 하는 편이다.

프로젝트 막판에 proguard를 적용하게되면 proguard로 인한 여러가지 문제를 만날 수 있기 때문에 충돌을 미리 발견하기
위해서다. 하지만 이렇게하다보면 빌드 속도가 너무 떨어진다. 그래서 켰다가 껐다가 하긴하는데 너무 귀찮다.

그럼 본격적으로 설정을 시작해보자.


1. gradle.properties 파일을 변경


프로젝트 Root에 있는 gradle.properties을 편집한다. 기존에 파일이 없다면 추가한다.
여기에는 gradle 빌드 할 때 인수를 설정할 수 있다. 한가지 더 알려주자면 프로젝트별로 설정하는 것 외에도
모든 프로젝트에 설정을 추가할 수도 있다. 해당 파일의 경로는 OSX 기준으로 ~/.gradle/gradle.properties 이다.
윈도우나 리눅스도 비슷한 경로에 있을 것으로 생각된다. (없다면 추가해도 된다).

gradle.properties

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Project-wide Gradle settings.
# IDE (eg Android Studio) users :
# Settings specified in this file will override any Gradle settings
# configured through the IDE.
# For more details on how to configure your build environment visit
# http : // www.gradle.org/docs/current/userguide/build_environment.html
# The Gradle daemon aims to improve the startup and execution time of Gradle.
# When set to true the Gradle daemon is to run the build.
# TODO : disable daemon on CI, Since Builds should BE clean and Reliable on servers
org.gradle.daemon = true
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value : -Xmx10248m -XX : MaxPermSize = 256M
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec : decoupled_projects
org.gradle.parallel = true
# Enables new incubating mode that makes Gradle selective when configuring projects.
# only relevant projects are configured which results in faster builds for large multi-projects.
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html #sec : configuration_on_demand
org.gradle.configureondemand = true


위 설정에 대한 간략한 설명은 아래와 같다.

  • org.gradle.daemon 
    : 데몬 프로세스를 사용할지 여부 설정한다.
      true 설정시 gradle을 daemon 모드로 실행하여 빌드시 gradle을 다시 실행하는 시간을 줄여준다.

  • org.gradle.jvmargs 
    : 실행시 JVM 인수로 메모리를 설정을 늘려 메모리 부족으로 인한 속도저하를 막는 것이 목적이다.
      따라서 적절한 양의 메모리를 할당해 주면 된다.

  • org.gradle.parallel 
    : gradle 병렬 빌드 모드를 설정한다.
     여러 프로젝트를 빌드 할 때 효과가 있다.

  • org.gradle.configureondemand 
    : 관련 프로젝트가 있다면 필요한 부분만 빌드 설정한다.

크게 성능이 향상되는 것 같지는 않지만 조금의 성능 향상은 있는 것 같다.
사실 다음에 소개할 gradle 버전 업이 훨씬 체감 성능 향상에 도움을 준다.



2. Android Studio 1.3 / Gradle 2.4 이상 사용


Android Studio 1.3 / Gradle 2.4 이상으로 버전업되면서 대폭 성능 향상이 있었다.
실제로 나의 경우엔 상당한 속도 향상이 있었다. 그래도 proguard를 적용하게되면 느리긴 마찬가지긴 하다.
구글의 설명을 보려면 Google I/O 2015 - What’s New in Android Development Tools을 참고하면 된다.

단, 주의할 것은 Android Studio 1.3 이상을 사용한다고 Gradle 2.4 이상을 무조건 사용하는 것은 아니라는 것이다.
신규 프로젝트가 아니어서 그런지 나의 프로젝트에서는 2.2.1을 그대로 사용하고 있었다.
(File > Project Structure > Project > Gradle version에서 2.4로 변경함으로써 적용할 수 있다).


3. gradle를 오프라인 모드로 변경


다른 사람들은 이 방법이 아주 효과적이었다고 하는데 나의 경우는 크게 차이가 나지 않았다.
(proguard로 인한 속도 저하라서 이 방법에 큰 도움이 되지 않았던 것 같다).

어쨌든 이 설정을 적용하면 gradle에서 매번 최신 버전을 확인하러가는 것을 생략 해 준다.
단, 신규 dependency를 추가하거나 변경하는 경우는 이 설정을 해제해야 한다.
약간은 귀찮을 수 있지만 dependency 변경은 크게 자주 발생하는 경우는 아니므로 문제되지 않을 것으로 생각된다.



Android Studio> Preferences> Gradle 의 Offline work에 체크.



4. jcenter Maven Repository 사용


jcenter와 Maven Central은 모두 Java / Android 라이브러리들을 배포한다. 초기 안드로이드 스튜디오에서 프로젝트를
생성하면 기본적으로 Maven Central를 사용(repositories { mavenCentral() })하도록 세팅되었다.

하지만 이제는 안드로이드 스튜디오에서 프로젝트를 생성하면 jcenter가 사용(repositories { jcenter() })되도록 세팅된다.
이 부분이 변경된 이유는 여러가지가 있다고 하지만 그중에서 빌드 속도에 영향을 줄 수 있는 부분이 있다.
jcenter는 CDN을 통해 라이브러리를 배포하기 때문에 Maven Central에 비해 빠르다는 것이다.
자세한 내용은 Android Studio – Migration from Maven Central to JCenter를 참고하자.



5. Speeding up Gradle builds

Speeding up Gradle builds에서는 몇가지 방법을 더 안내하고 있다. 간략하게 요약하면 아래와 같다.
(위의 설명들과 유사한 부분도 많다). 상세한 설명은 위 링크를 참조하기 바란다.


Maven Central vs. JCenter

mavenCentral() 대신 jcenter()를 사용해라.


Get the latest version of Gradle

최신 버전의 Gradle을 사용하라.


Gradle properties

아래의 Gradle properties를 적용하라(위의 설명이 더 상세하다).

1
2
3
org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m


How to use Gradle properties

Gradle properties를 프로젝트별로 설정하기보다 전역으로 설정하라.


다중 모듈 빌드

다중 모뮬을 사용하는 경우에 속도 향상 방법이 안내되어 있으니 필요한 경우에 원본글에 가서 참조 바란다.






안드로이드 스튜디오 빌드 속도 향상 시키기 


프로젝트의 루트에 gradle.properties를 생성하고 안드로이드 스튜디오를 재실행하면 반영이 됩니다.

마지막 라인이 중요해요

project.android.dexOptions.preDexLibraries = false

빌드서버에서 빌드를 하는 경우에는 기존 빌드된 결과물을 재활용하는 대신 항상 클린 빌드를 수행할 필요가 없어요, 따라서 그래들 증분 빌드의 효과를 누릴 수가 없어요, 하지만, 기본적으로 그래들 안드로이드 플로그인은 증분 빌드 기능을 최적화 하기 위하여 최초 빌드시 모든 서브 모듈들을 pre-dexing 하는 작업을 수행해요, 다만, 이런 경우 첫 번째 빌드 시에는 더 오랜 시간이 걸리게 되며 빌드 서버에서는 별로 적절하지 않은 작업이 된다, 따라서 안드로이드 그래들 플러그인의 preDexLibraries 속성을 사용하지 않도록 위와 같이 사용하는 것이 좋아요
# Project-wide Gradle settings.
# IDE (eg Android Studio) users :
# Settings specified in this file will override any Gradle settings
# configured through the IDE.
# For more details on how to configure your build environment visit
# http : // www.gradle.org/docs/current/userguide/build_environment.html
# The Gradle daemon aims to improve the startup and execution time of Gradle.
# When set to true the Gradle daemon is to run the build.
# TODO : disable daemon on CI, Since Builds should BE clean and Reliable on servers
org.gradle.daemon = true

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value : -Xmx10248m -XX : MaxPermSize = 256M
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

# When configured, Gradle will run in incubating parallel mode.
# This option should on ly be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec : decoupled_projects
org.gradle.parallel = true

# Enables new incubating mode that makes Gradle selective when configuring projects.
# on ly relevant projects are configured which results in faster builds for large multi-projects.
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html #sec : configuration_on_demand
org.gradle.configureondemand = true

project.android.dexOptions.preDexLibraries = false






Android Studio 속도 향상 스크립트


[build.Gradle]


defaultConfig {
      applicationId ; dfsdfdsf
      minSdkVersion 21 // 21이상으로 하면 빨라집니다 (art모드로만 컴파일해서)
      targetSdkVersion 22
      versionCode 1
      versionName “1.0”
      multiDexEnabled true
}
buildTypes {
      release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
      }
}
dexOptions {
      jumboMode = true
      javaMaxHeapSize “4g”
      incremental true
}



[Gradle.properties]

org.gradle.daemon=true
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.configureondemand=true








Speed up your build in Android Studio


Gradle provides a lot of flexibility, but this power comes at the cost of a slower build process. Android Studio is based on Gradle.

When we are working with Android Studio, as our project is getting bigger and bigger, our daily / development build is getting slower and slower.

For example, one of our project need more than 40s to complete build once after we modified the Java code. If we build 100 times per day, the build process will take nearly an hour. Image that we can do nothing but only wait in an hour, that is really a waste of time.

Today we will try to make the build a little faster.

There are some solution we can find easily by a simple search:

  • org.gradle.daemon = true

  • org.gradle.parallel = true

  • make gradle work in offline work mode

  • set up large vm heap size: 2G

But all of them can not bring significant speeding up, the build tool in Android Studio has already done most these work.

Profile

If you want to figure out the cause, you can try profiling your build process.
You can add the --profile flag to any Gradle task. Adding this flag will create a report under build/reports/profile.

It looks like:

Here is an example.

To enable profile in Android Studio, you must config the command line options like bellow:

Why it is so slow

After we inspect the profile again and again, we can figure out some reasons that make build process slow:

  • some long running tasks

  • multiDexEnabled

  • too much dependencies

Solutions

1. Disable long running task

We can disable some long running tasks in dev mode.

tasks.whenTaskAdded { task ->
    if (task.name.startsWith(":zxing:") || task.name.startsWith(":share-lib:")) {
        task.enabled = false
    }
}

2. Shrink the project

We can not really shrink the project unless we remove some functions of our APP.
But we can shrink our project in dev mode. We can disable some module, here is the structure of our project:

There are two grops of module:

  • share-lib and share-lib-no-op

  • zxing and zxing-no-op

    The no-op project is an empty module, we include this empty module in debug complie:

    debugCompile(project(':share-lib-no-op')) {
    }
    releaseCompile(project(':share-lib')) {
    }
    debugCompile(project(':zxing-no-op')) {
    }
    releaseCompile(project(':zxing')) {
    }
    

    The code in share-lib do the real work:

    public class ShareProxyImpl {
        public void sendShare(ShareRequestDO request, Activity activity) {
            new ISShareController().startShare(activity, new ShareRequest(request));
        }
        public void onActivityResult(Context context, int requestCode, int resultCode, Intent data) {
            ISShareController.onActivityResult(requestCode, resultCode, data);
        }
    }
    

    And the code in share-lib-no-op does nothing:

    public class ShareProxyImpl {
        public void sendShare(ShareRequestDO request, Activity activity) {
            Toast.makeText(activity, "ShareProxyImpl:no-op:sendShare", Toast.LENGTH_SHORT).show();
        }
        public void onActivityResult(Context context, int requestCode, int resultCode, Intent data) {
            Toast.makeText(context, "ShareProxyImpl:no-op:onActivityResult", Toast.LENGTH_SHORT).show();
        }
    }
    


In this way, we can reduce a lot of dependence in debug complie.
Once the project is shrinked, we do not need multiDexEnabled in debug mode.

  • This is the profile before we adjust our project:


  • And this is the current profile:




    We only need about 15 seconds to finish a debug build, if we only modify the resource, that will be faster.



3. Jack and Jill

It is a experimental new Android tool chain, it may be faster in the future, for it can meger the dex file in a very fast way.

But currently(2015/07/28) it is very slow, the complie process take a very long time, it is nearly 2 minitues in our project.