[안드로이드] kakao sample gradle 분석 3.applications.gradle

프로그래밍/Android 관련2017. 12. 6. 23:41

안녕하세요. 드리머즈입니다.


다음으로 볼 gradle파일은, applications/ 경로에 있는 applications.gradle 파일입니다.

역시 조금씩 나눠서 보겠습니다.

subprojects {
apply plugin: 'com.android.application'

android {
compileOptions.encoding = "UTF-8"
version = project.APP_VERSION
project.ext.set("defaultDeployPhase", "${project.hasProperty('deploy_phase') ? deploy_phase.toString() : "$DEFAULT_PHASE"}")

compileSdkVersion ANDROID_BUILD_SDK_VERSION
buildToolsVersion ANDROID_BUILD_TOOL_VERSION

defaultConfig {
minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION
targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION
versionCode Integer.parseInt(project.APP_VERSION)
versionName project.APP_VERSION_NAME
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

buildTypes {
debug {
debuggable true
// minifyEnabled true
zipAlignEnabled true
// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

lintOptions {
checkReleaseBuilds true
abortOnError false
checkAllWarnings true
xmlReport true
htmlReport true
disable "InvalidPackage", "MissingTranslation"
}

packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/maven/com.google.guava/guava/pom.properties'
exclude 'META-INF/maven/com.google.guava/guava/pom.xml'
}

android.applicationVariants.all { variant ->
variant.outputs.all { output ->
output.processManifest.doLast {
String manifestPath = "$manifestOutputDirectory/AndroidManifest.xml"
println ">> processManifest $manifestPath"
def appKeyName = 'kakao_app_key'
def appSchemeName = 'kakao_scheme'
def clientSecret = 'kakao_client_secret'
def file = file(manifestPath)
replaceString(file, appKeyName, addPrefix(project, appKeyName))
replaceString(file, appSchemeName, addPrefix(project, appSchemeName))
replaceString(file, clientSecret, addPrefix(project, clientSecret))
}
}
}
}

preBuild.dependsOn rootProject.bumpVersionInProperties

dependencies {
androidTestImplementation('com.android.support.test:runner:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
}
androidTestImplementation('com.android.support.test:rules:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
}
// Optional -- Hamcrest library
// Optional -- UI testing with Espresso
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
}
androidTestImplementation('com.android.support.test.espresso:espresso-intents:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
}
androidTestImplementation('com.android.support.test.espresso:espresso-web:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
}

androidTestImplementation "com.android.support:support-annotations:$ANDROID_SUPPORT_LIB_VERSION"
androidTestImplementation "org.hamcrest:hamcrest-library:$HAMCREST_VERSION"
}
}

subprojects{} 블럭은, applications 의 하위폴더에 존재하는 하위 모듈의 gradle에서도 위의 사항을 동일하게 적용하기 위해 사용됐습니다.

그 다음 보이는 코드는

apply plugin: 'com.android.application'
입니다. 이 플러그인을 적용해야 그 다음의 android{} 블럭을 사용할 수 있는 것으로 보입니다.

그리고.. 이 android{} 블럭이 apk빌드를 위한 핵심 부분입니다. 여기서 apk빌드와 관련된 설정들을 정할 수 있습니다.


preBuild.dependsOn rootProject.bumpVersionInProperties

이 코드가 왜 있는지는 모르겠지만.. 빌드 이전에 실행되는 preBuild라는 task에 의존성(dependsOn)을 설정하여

빌드를 하기 전에 rootProject에 있는 bumpVersionInProperties task가 실행되게 만드는 코드입니다.


그 다음은 dependencies{} 블럭입니다. gradle에 대해 자세히 공부하기 전에 이 부분의 정확한 역할이 궁금했습니다.

여기 코드에서는 블럭 안에 androidTestImplementation만 존재하네요.

여러 종속성이 존재합니다. 이 프로젝트의 빌드를 위해 필요한 라이브러리를 명시하는 부분입니다.

여기서는 안드로이드 테스트를 지원하기 위해 필요한 라이브러리들이 명시되어 있습니다.


안드로이드 디벨로퍼 사이트 - 지원 라이브러리 : https://developer.android.com/topic/libraries/testing-support-library/index.html

스택오버플로우 - implementation 과 compile에 대한 질문글 : https://stackoverflow.com/questions/44493378/whats-the-difference-between-implementation-and-compile-in-gradle


위의 사이트를 참고하면 도움이 됩니다.


그리고 마지막 코드는

static def addPrefix(Project project, String value) {
def result = value
if(project.defaultDeployPhase.toLowerCase() != 'release') {
result = project.defaultDeployPhase.toLowerCase() + "_" + result
}
return result
}

static def replaceString(file, fromString, toString) {
def updatedContent = file.getText('UTF-8').replaceAll(fromString, toString)
file.write(updatedContent, 'UTF-8')
}

위와 같습니다. 이 gradle파일에서 사용되는 함수를 정의한 부분이고 특별한 점은 없는 것 같습니다.




*참고

안드로이드 디벨로퍼 - 빌드 구성 : https://developer.android.com/studio/build/index.html?hl=ko


안드로이드 디벨로퍼 사이트의 설명이 참 좋은 것 같습니다. 한글 번역 추가합니다.


모듈 레벨 빌드 파일

 <project>/<module>/ 디렉토리에 있는 모듈 레벨 build.gradle 파일을 사용하면 이 파일이 위치하는 특정 모듈의 빌드 설정을 구성할 수 있습니다. 이러한 빌드 설정을 구성하면 사용자 지정 패키징 옵션을 제공할 수 있으며(예: 추가적인 빌드 유형 및 제품 버전), main/ 앱 매니페스트 또는 최상위 build.gradle 파일에 있는 설정을 재정의할 수 있습니다.

아래의 샘플 Android 앱 모듈 build.gradle 파일에서는 여러분이 알아야 하는 기본 DSL 요소과 설정에 대해 간략히 설명합니다.

 

/**
 * The first line in the build configuration applies the Android plugin for
 * Gradle to this build and makes the android {} block available to specify
 * Android-specific build options.

빌드 설정에서 첫 줄은 빌드를 위해 Gradle에 안드로이드 플러그인을 적용하고 안드로이드 특유의

빌드 옵션을 명시할 수 있는 android {} 블럭을 사용할 수 있게 합니다.
 */


apply plugin
: 'com.android.application'

/**
 * The android {} block is where you configure all your Android-specific
 * build options.

android {} 블럭에서 안드로이드 특유의 모든 빌드 옵션을 설정할 수 있습니다.
 */


android
{

 
/**
   * compileSdkVersion specifies the Android API level Gradle should use to
   * compile your app. This means your app can use the API features included in
   * this API level and lower.

compileSdkVersio은 Gradle이 앱을 컴파일하기 위해 사용해야 하는 안드로이드 API level

을 명시합니다. 이것은 앱에서 명시된 API level이하의 API featrue를 사용할 수 있다는 것을

의미합니다.
   *
   * buildToolsVersion specifies the version of the SDK build tools, command-line
   * utilities, and compiler that Gradle should use to build your app. You need to
   * download the build tools using the SDK Manager.

buildTollVersion은 Gradle이 app을 빌드하기 위해 사용해야 하는 SDK build tools,

command-line 유틸리티 와 컴파일러의 버전을 명시합니다. SDK Manager를 통해 build tools를

다운해야 합니다.
   */


  compileSdkVersion
26
  buildToolsVersion
"26.0.2"

 
/**
   * The defaultConfig {} block encapsulates default settings and entries for all
   * build variants, and can override some attributes in main/AndroidManifest.xml
   * dynamically from the build system. You can configure product flavors to override
   * these values for different versions of your app.

defaultConfig {} 블럭은 모든 빌드 variants를 위한 기본 세팅과 입력을 포함하며,

빌드 시스템으로부터 동적으로 main/AndroidManifest.xml의 특정 attribute를

오버라이드할 수 있습니다.
   */


  defaultConfig
{

   
/**
     * applicationId uniquely identifies the package for publishing.
     * However, your source code should still reference the package name
     * defined by the package attribute in the main/AndroidManifest.xml file.

applicationId를 이용하여 출시를 위한 패키지를 구별합니다.

(마켓에 앱 출시할 때, 다른 앱에서 동일한 applicationId를 사용하고 있으면 안된다는 뜻)

그러나 소스 코드에서는 main/AndroidManifext.xml 파일의 package attribute에

정의된 package name을 여전히 사용합니다.
     */


    applicationId
'com.example.myapp'

   
// Defines the minimum API level required to run the app.

// 앱을 실행하기 위해 필요한 최소 API level을 정의합니다.
    minSdkVersion
15

   
// Specifies the API level used to test the app.

// 앱을 테스트하기 위한 API level을 명시합니다.
    targetSdkVersion
26

   
// Defines the version number of your app.

// 앱의 버전 넘버를 정의합니다.
    versionCode
1

   
// Defines a user-friendly version name for your app.

// 사용자에게 친숙한 앱 버전 이름을 정의합니다.
    versionName
"1.0"
 
}

 
/**
   * The buildTypes {} block is where you can configure multiple build types.
   * By default, the build system defines two build types: debug and release. The
   * debug build type is not explicitly shown in the default build configuration,
   * but it includes debugging tools and is signed with the debug key. The release
   * build type applies Proguard settings and is not signed by default.

buildTypes {} 블럭에서 다수의 빌드 타입을 설정합니다.

기본적으로, 빌드 시스템은 debug와 release 두 가지의 빌드 타입을 정의합니다.

debug 빌드 타입은 기본 빌드 설정에서 명시적으로 보이진 않지만, 이는 디버깅 툴을 포함하며

디버그 키로 사인된다. release 빌드 타입은 Proguard(난독화)설정을 적용하며 기본적으로

사인되지 않는다.
   */


  buildTypes
{

   
/**
     * By default, Android Studio configures the release build type to enable code
     * shrinking, using minifyEnabled, and specifies the Proguard settings file.

기본적으로, Android Studio는 minifyEnabled를 이용한 코드 축소를 하기위해

release 빌드 타입을 설정하며, Proguard 설정 파일을 명시합니다.
     */


    release
{
        minifyEnabled
true // Enables code shrinking for the release build type.
        proguardFiles getDefaultProguardFile
('proguard-android.txt'), 'proguard-rules.pro'
   
}
 
}

 
/**
   * The productFlavors {} block is where you can configure multiple product
   * flavors. This allows you to create different versions of your app that can
   * override defaultConfig {} with their own settings. Product flavors are
   * optional, and the build system does not create them by default. This example
   * creates a free and paid product flavor. Each product flavor then specifies
   * its own application ID, so that they can exist on the Google Play Store, or
   * an Android device, simultaneously.

productFlavors {}블럭에서 다수 제품 특색을 설정할 수 있습니다. 이는 defaultConfig {}

블럭을 다른 버전의 세팅으로 오버라이드할 수 있는 앱의 다른 버전들을 만들 수 있게 합니다.
   */


  productFlavors
{
    free
{
      applicationId
'com.example.myapp.free'
   
}

    paid
{
      applicationId
'com.example.myapp.paid'
   
}
 
}

 
/**
   * The splits {} block is where you can configure different APK builds that
   * each contain only code and resources for a supported screen density or
   * ABI. You'll also need to configure your build so that each APK has a
   * different versionCode.

split {} 블럭에서 각각의 APK 빌드에서 지원되는 스크린 density나 ABI를 위한 코드와 리소스만을

포함하는 다른 APK 빌드를 설정할 수 있습니다.
   */


  splits
{
   
// Screen density split settings

// 스크린 density 분리 세팅
    density
{

     
// Enable or disable the density split mechanism

// density 분리 메카니즘을 조절함
      enable
false

     
// Exclude these densities from splits

// 분리할 때 이러한 density는 제외함
      exclude
"ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
   
}
 
}
}

/**
 * The dependencies {} block in the module-level build configuration file
 * only specifies dependencies required to build the module itself.

모듈 레벨의 빌드 설정 파일에서 denpendencies {} 블럭은 그 모듈 자신을 빌드하기 위해 필요한

종속성dependency만 명시합니다.
 */


dependencies
{
    compile project
(":lib")
    compile
'com.android.support:appcompat-v7:27.0.2'
    compile fileTree
(dir: 'libs', include: ['*.jar'])
}



dependencies{}에 대한 내용 : https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.dsl.DependencyHandler.html


//Our Gradle plugin is written in groovy
apply plugin: 'groovy'
//now we can use the 'compile' configuration for declaring dependencies

dependencies {
  //we will use the Groovy version that ships with Gradle:
  compile localGroovy()

  //our plugin requires Gradle API interfaces and classes to compile:
  compile gradleApi()

  //we will use the Gradle test-kit to test build logic:
  testCompile gradleTestKit()
}


stackoverflow 질문글-dependency configuraton : https://stackoverflow.com/questions/35947897/gradle-dependencies-difference-between-compile-apk-project-compile-project-pro

There's two separate things to discuss here: Dependency Configurations and Dependency Sources.

Dependency Configurations

Configurations help define the transitivity of a dependency, which in turn removes the pain of having to discover and specify the libraries your own project/library requires, including them automatically. This notion of configurations in gradle is very similar to that of Maven's scopes:

  1. compile: Compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects. A compile-time dependency is generally required at runtime.
  2. apk: Defines a runtime dependency. A dependency with this scope will not be required at compile time, but it will be for execution. This means that you can save time while compiling and still have the dependency available when your project actually runs. This is a good example of when to use an apk dependency.
  3. provided: It means that this dependency is available on the runtime environment. As a consequence, this scope is only available on the compilation and test classpath, and is not transitive. It is not supported on Android projects, though you can workaround it by defining your own configuration as discussed here.

There are more configurations that you can encounter on Android, such as testCompile, which allows you to specify a compile-time dependency that will only be used for testing, say you want to use junit in your tests, then you would do as follows:

testCompile 'junit:junit:4.12'

Dependency Source

Once you understand the configurations available for you, you need to specify an actual dependency. Dependencies might be internal or external, you may rely on another library you are working on, as well as on publicly available libraries. Here's where the project keyword comes in, allowing you to specify a dependency to an internal module or library. By defining a dependency as compile project, you are adding that module or library as a transitive dependency to your project.

Assume you have a project messages with three modules (producer, consumer and shared), the project structure would look as follows:

messages/
    build.gradle
    settings.gradle
    consumer/
        build.gradle
    producer/
        build.gradle
    shared/
        build.gradle

Now assume that both consumer and producer store messages in json format and that you want to use google-gson for that purpose. Assume that both projects have some common source code that they depend on, your shared module. consumer's build.gradle could then define the following dependencies:

dependencies {
   // Internal dependency to project shared
   compile project (':shared')

   // External dependency to publicly available library, 
   // through public repositories such as jcenter() or mavencentral()
   compile 'com.google.code.gson:gson:1.7.2'
}

To sum up, it is the combination of both configurations and sources that enables you to declare dependencies as compile, compile project, apk project and more! 




stackoverflow 질문글-dependencies에서 implementation과 compile의 차이점 : https://stackoverflow.com/questions/44493378/whats-the-difference-between-implementation-and-compile-in-gradle

It is one of the breaking changes coming with gradle:3.0 that Google announced at IO17 gradle:3.0

the compile configuration is now deprecated and should be replaced by implementation or api

From the gradle docs :

dependencies {
    api 'commons-httpclient:commons-httpclient:3.1'
    implementation 'org.apache.commons:commons-lang3:3.5' 
}

Dependencies appearing in the api configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers.

Dependencies found in the implementation configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath. This comes with several benefits:

  • dependencies do not leak into the compile classpath of consumers anymore, so you will never accidentally depend on a transitive dependency
  • faster compilation thanks to reduced classpath size
  • less recompilations when implementation dependencies change: consumers would not need to be recompiled
  • cleaner publishing: when used in conjunction with the new maven-publish plugin, Java libraries produce POM files that distinguish exactly between what is required to compile against the library and what is required to use the library at runtime (in other words, don't mix what is needed to compile the library itself and what is needed to compile against the library).

The compile configuration still exists but should not be used as it will not offer the guarantees that the api and implementation configurations provide.


tl;dr:
just replace compile with implementation, debugCompile with debugImplementation, testCompile with testImplementation and androidtestcompile with androidTestImplementation
 


댓글들

Who are the "consumers"? – Suragch Sep 8 at 6:35
the consumer is the module using the library. in the case of Android, it's the Android application. I think this is clear and I'm not sure if this is what you are asking for. – humazed Sep 8 at 6:40
That is what it sounded like to me, too. But if I am making a library, of course I want its API to be exposed to the app. Otherwise, how would the app developer use my library? That's why I don't get the meaning of implementation hiding the dependency. Does my question make sense? – Suragch Sep 8 at 6:53
yes, it makes sense now, if your app depends on library x which itself depends on y,z. if you use implementation only x api will be exposed, but if you use api y,z also will be exposed. – humazed Sep 8 at 7:16
Got it! That makes more sense now. You could add this explanation into your answer. It is more clear than the quoted documentation. – Suragch Sep 8 at 8:29
Also, replace provided with compileOnly – Tarek360 Sep 29 at 5:08
Can somebody please explain in simple language? – Ishaan Kumar Oct 25 at 9:12
Note that if you're actually building a public library and publishing it to a maven repository, Google doesn't provide the tools to complete the job. You must use the android-maven-publish plugin to create the POMs correctly and translate from Gradle's api/implementation to Maven's runtime/compile scope syntax. Link: github.com/wupdigital/android-maven-publish – Nilzor Oct 30 at 18:58
I have follow up question. This solves conflicts between dependency version if they were hidden by implementation, but I still need to include the dependencies in app? Even though library that uses them has them as implementation? – lsrom Nov 9 at 15:05
@humazed what is the replacement for flavor compile. e.g. devCompile to? – iDroid Explorer Nov 13 at 10:59
I don't understand what you mean by "flavor compile" if you mean you have flavor called "devCompile" then it's not related to this change and you don't have to do anything; – humazed Nov 13 at 13:21


작성자

Posted by 드리머즈

관련 글

댓글 영역