본문 바로가기
User Guide

빌드 변형 컨피규어 하기 Configure Build Variants (28)

by 각종 잡상식 지식 모음 2016. 6. 10.
반응형

빌드 변형 컨피규어 하기
Configure Build Variants

This page builds on the Configure Your Build Overview to show you how you can configure build variants to create different versions of your app from a single project, and how to properly manage your dependencies and signing configurations.

Each build variant represents a different version of your app that you can build. They are the result of Gradle using a specific set of rules to combine settings, code, and resources configured in your build types and product flavors. Although you do not configure build variants directly, you do configure the build types and product flavors that form them.

For example, a "demo" product flavor can specify different features and device requirements, such as custom source code, resources, and minimum API levels, while the "debug" build type applies different build and packaging settings, such as debug options and signing keys. The resulting build variant is the "demoDebug" version of your app, and it includes a combination of the configurations and resources included in the "demo" product flavor, "debug" build type, and main/ source set.

Configure Build Types


You can create and configure build types in the module-level build.gradle file inside the android {} block. When you create a new module, Android Studio automatically creates the debug and release build types for you. Although the debug build type doesn't appear in the build configuration file, Android Studio configures it with debuggable true. This allows you to debug the app on secure Android devices and configures APK signing with a generic debug keystore.

You can add the the debug build type to your configuration if you want to add or change certain settings. The following sample specifies anapplicationIdSuffix for the debug build type, and configures a "jnidebug" build type that is initialized using settings from the debug build type.

android {
   
...
    defaultConfig
{...}
    buildTypes
{
        release
{
            minifyEnabled
true
            proguardFiles getDefaultProguardFile
('proguard-android.txt'), 'proguard-rules.pro'
       
}

        debug
{
            applicationIdSuffix
".debug"
       
}

       
/**
         * The 'initWith' property allows you to copy configurations from other build types,
         * so you don't have to configure one from the beginning. You can then configure
         * just the settings you want to change. The following line initializes
         * 'jnidebug' using the debug build type, and changes only the
         * applicationIdSuffix and versionNameSuffix settings.
         */


        jnidebug
{

           
// This copies the debuggable attribute and debug signing configurations.
            initWith debug

            applicationIdSuffix
".jnidebug"
            jniDebuggable
true
       
}
   
}
}

Note: When you make changes to a build configuration file, Android Studio requires that you sync your project with the new configuration. To sync your project, you can click Sync Now in the notification bar that appears as soon as you make a change or Sync Project  from the toolbar. If Android Studio notices any errors with your configuration, the Messages window appears to describe the issue.

To learn more about all the properties you can configure with build types, read the build type DSL reference.

Configure Product Flavors


Creating product flavors is similar to creating build types: add them to the productFlavors {} block and configure the settings you want. The product flavors support the same properties as defaultConfig—this is because defaultConfig actually belongs to the ProductFlavor class. This means you can provide the base configuration for all flavors in the defaultConfig {} block, and each flavor can override any of these default values, such as theapplicationId.

Note: You still need to specify a package name using the package attribute in the main/ manifest file. You must also use that package name in your source code to refer to the R class, or resolve any relative activity or service registration. This allows you to use applicationId to give each product flavor a unique ID for packaging and distribution, without having to change your source code.

The following code sample creates a "demo" and "full" product flavor with their own applicationId and versionName

android {
   
...
    defaultConfig
{...}
    buildTypes
{...}
    productFlavors
{
        demo
{
            applicationId
"com.example.myapp.demo"
            versionName
"1.0-demo"
       
}
        full
{
            applicationId
"com.example.myapp.full"
            versionName
"1.0-full"
       
}
   
}
}

Note: To distribute your app using Multiple APK Support in Google Play, assign the same applicationId value to all variants and give each variant a different versionCode. To distribute different variants of your app as separate apps in Google Play, you need to assign a different applicationId to each variant.

After you create and configure your product flavors, click Sync Now in the notification bar. After the sync completes, Gradle automatically creates build variants based on your build types and product flavors, and names them according to <product-flavor><Build-Type>. For example, if you created 'demo' and 'full' product flavors, and kept the default 'debug' and 'release' build types, Gradle creates the following build variants:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

You can change the build variant to whichever one you want to build and run—just go to Build > Select Build Variant and select a one from the drop-down menu. To start customizing each build variant with its own features and resources, however, you'll need to know how to create and manage source sets.

Create Source Sets for Build Variants


By default, Android Studio creates the main/ source set and directories for everything you want to share between all your build variants. However, you can create new source sets to control exactly what files Gradle compiles and packages for specific build types, product flavors, and build variants. For example, you can define basic functionality in the main/ source set and use product flavor source sets to change the branding of your app for different clients, or include special permissions and logging functionality only for build variants that use the debug build type.

Gradle expects you to organize source set files and directories a certain way, similar to the main/ source set. For example, Gradle expects Java class files that are specific to your "debug" build type to be located in the src/debug/java/ directory.

The Android Plugin for Gradle provides a useful Gradle task that shows you how to organize your files for each of your build types, product flavors, and build variants. For example, the following section of the report describes where Gradle expects to find certain files for the "debug" build type:

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: compile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

To generate and view this report for your build configuration, proceed as follows:

  1. Click Gradle  on the right side of the IDE window.
  2. Navigate to MyApplication > Tasks > android and double-click sourceSets.
  3. To view the report, click Gradle Console  at the bottom of the IDE window.

Note: The report also shows you how to organize source sets for files you want to use to run tests for your app, such as the test/ and androidTest/testing source sets.

When you create a new build variant, Android Studio doesn't create the source set directories for you, but it does give you a few options to help you. For example, to create just the java/ directory for your "debug" build type:

  1. Open the Project pane and select the Project view from the drop-down menu at the top of the pane.
  2. Navigate to MyProject/app/src/.
  3. Right-click the src directory and select New > Folder > Java Folder.
  4. From the drop-down menu next to Target Source Set, select debug.
  5. Click Finish.

Android Studio creates a source set directory for your debug build type, and then creates the java/ directory inside it. Alternatively, you can also make Android Studio create the directories for you when create a new file for a specific build variant. For example, to create a values XML file for your "debug" build type:

  1. In the same Project pane, right-click the src directory and select New > XML > Values XML File.
  2. Enter the name for the XML file or keep the default name.
  3. From the drop-down menu next to Target Source Set, select debug.
  4. Click Finish.

Because the "debug" build type was specified as the target source set, Android Studio automatically creates the necessary directories when it creates the XML file. The resulting directory structure should look like figure 2.

Figure 2. New source set directories for the debug build type.

Using the same procedure, you can also create source set directories for product flavors, such as src/demo/, and build variants, such assrc/demoDebug/. Additionally, you can create testing source sets that target specific build variants, such as src/androidTestDemoDebug/. To learn more, go to Testing source sets.

Build with source sets

You can use source set directories to contain the the code and resources you want packaged only with certain configurations. For example, if you are building the "demoDebug" build variant, which is the crossproduct of a "demo" product flavor and "debug" build type, Gradle looks at these directories, and gives them the following priority:

  1. src/demoDebug/ (build variant source set)
  2. src/debug/ (build type source set)
  3. src/demo/ (product flavor source set)
  4. src/main/ (main source set)

The order listed above determines which source set has a higher priority when Gradle combines code and resources. Because the demoDebug/ source set directory likely contains files that are specific to that build variant, if demoDebug/ includes a file that is also defined in debug/, Gradle uses the file in the demoDebug/ source set. Similarly, Gradle gives files in the build type and product flavor source sets a higher priority over the same files in main/. Gradle considers this priority order when applying the following build rules:

  • All source code in the java/ directories are compiled together to generate a single output.

    Note: For a given build variant, Gradle throws a build error if it encounters two or more source set directories that have defined the same Java class. For example, when building a debug APK, you cannot define both src/debug/Utility.java and src/main/Utility.java. This is because Gradle looks at both these directories during the build process and throws a 'duplicate class' error. If you want different versions of Utility.javafor different build types, you can have each build type define its own version of the file and not include it in the main/ source set.

  • Manifests are merged together into a single manifest. Priority is given in the same order as the list above. That is, manifest settings for a build type override the manifest settings for a product flavor, and so on. To learn more, read about manifest merging.
  • Similarly, files in the values/ directories are merged together. If two files share the same name, such as two strings.xml files, priority is given in the same order as the list above. That is, values defined in a file in the build type source set override the values defined in the same file in a product flavor, and so on.
  • Resources in the res/ and asset/ directories are packaged together. If there are resources with the same name defined in two or more source sets, priority is given in the same order as the list above.
  • Finally, Gradle gives resources and manifests included with library module dependencies the lowest priority when building the APK.

Declare Dependencies


The following example declares three different types of direct dependencies in the app/ module's build.gradle file:

android {...}
...
dependencies
{
   
// The 'compile' configuration tells Gradle to add the dependency to the
   
// compilation classpath and include it in the final package.

   
// Dependency on the "mylibrary" module from this project
    compile project
(":mylibrary")

   
// Remote binary dependency
    compile
'com.android.support:appcompat-v7:23.4.0'

   
// Local binary dependency
    compile fileTree
(dir: 'libs', include: ['*.jar'])
}

Each of these direct dependencies is described below.

Module dependencies
The compile project(':mylibrary') line declares a local Android library module named "mylibrary" as a dependency, and requires the build system to compile and include the local module when building your app.
Remote binary dependencies
The compile 'com.android.support:appcompat-v7:23.4.0' line declares a dependency on version 23.4.0 of the Android Support Library by specifying its JCenter coordinates. By default, Android Studio configures projects to use the JCenter Repository in the top-level build file. Gradle automatically pulls the dependency from JCenter when you sync your project with the build configuration files. Alternatively, you can download and install certain dependencies by using the SDK Manager.
Local binary dependencies
The compile fileTree(dir: 'libs', include: ['*.jar']) line tells the build system to include any JAR files inside the app/libs/ directory in the compilation classpath and in the final package of your app. If you have modules that require local binary dependencies, copy the JAR files for these dependencies into <moduleName>/libs inside your project.

Some direct dependencies of the module may have dependencies of their own, called transitive dependencies of the module. Rather than having to manually declare each transitive dependency, Gradle automatically gathers and adds them for you. The Android Plugin for Gradle provides a useful Gradle task that can generate a dependency tree for each build variant and testing source set, so you can easily visualize both the direct and transitive dependencies of your module. To generate this report, proceed as follows:

  1. Click Gradle  on the right side of the IDE window.
  2. Navigate to MyApplication > Tasks > android and double-click androidDependencies.
  3. To view the report, click Gradle Console  at the bottom of the IDE window.

The following sample report shows the dependency tree for the debug build variant, and includes the local module dependency and remote dependency from the previous example.

Executing tasks: [androidDependencies]
:app:androidDependencies
debug
/**
 * Both the library module dependency and remote binary dependency are listed
 * with their transitive dependencies.
 */
+--- MyApp:mylibrary:unspecified
|    \--- com.android.support:appcompat-v7:23.4.0
|         +--- com.android.support:animated-vector-drawable:23.4.0
|         |    \--- com.android.support:support-vector-drawable:23.4.0
|         |         \--- com.android.support:support-v4:23.4.0
|         |              \--- LOCAL: internal_impl-23.4.0.jar
|         +--- com.android.support:support-v4:23.4.0
|         |    \--- LOCAL: internal_impl-23.4.0.jar
|         \--- com.android.support:support-vector-drawable:23.4.0
|              \--- com.android.support:support-v4:23.4.0
|                   \--- LOCAL: internal_impl-23.4.0.jar
\--- com.android.support:appcompat-v7:23.4.0
     +--- com.android.support:animated-vector-drawable:23.4.0
     |    \--- com.android.support:support-vector-drawable:23.4.0
     |         \--- com.android.support:support-v4:23.4.0
     |              \--- LOCAL: internal_impl-23.4.0.jar
     +--- com.android.support:support-v4:23.4.0
     |    \--- LOCAL: internal_impl-23.4.0.jar
     \--- com.android.support:support-vector-drawable:23.4.0
          \--- com.android.support:support-v4:23.4.0
               \--- LOCAL: internal_impl-23.4.0.jar
...

For more information about managing dependencies in Gradle, see Dependency Management Basics in the Gradle User Guide.

Configure dependencies

You can use certain configuration keywords to tell Gradle how and when to use a dependency, such as the compile keyword from previous examples. The following describes some of the keywords you can use to configure your dependencies:

compile
Specifies a compile time dependency. Gradle adds dependencies with this configuration to both the classpath and your app’s APK. This is the default configuration.
apk
Specifies a runtime-only dependency that Gradle needs to package with your app’s APK. You can use this configuration with JAR binary dependencies, but not with other library module dependencies or AAR binary dependencies.
provided
Specifies a compile time dependency that Gradle does not package with your app’s APK. This helps reduce the size of your APK if the dependency is not required during runtime. You can use this configuration with JAR binary dependencies, but not with other library module dependencies or AAR binary dependencies.

Additionally, you can configure a dependency for a specific build variant or testing source set by applying the name of the build variant or testing source set to the configuration keyword, as shown in the following sample.

dependencies {
   
...
   
// Adds specific library module dependencies as compile time dependencies
   
// to the fullRelease and fullDebug build variants.
    fullReleaseCompile project
(path: ':library', configuration: 'release')
    fullDebugCompile project
(path: ':library', configuration: 'debug')

   
// Adds a compile time dependency for local tests.
    testCompile
'junit:junit:4.12'

   
// Adds a compile time dependency for the test APK.
    androidTestCompile
'com.android.support.test.espresso:espresso-core:2.2.2'
}

Configure Signing Settings


Gradle does not sign your release build's APK unless you explicitly define a signing configuration for this build. You can easily create a release key andsign your release build type using Android Studio.

To manually configure the signing configurations for your release build type using Gradle build configurations:

  1. Create a keystore. A keystore is a binary file that contains a set of private keys. You must keep your keystore in a safe and secure place.
  2. Create a private key. A private key represents the entity to be identified with the app, such as a person or a company.
  3. Add the signing configuration to the module-level build.gradle file:

    ...
    android
    {
       
    ...
        defaultConfig
    {...}
        signingConfigs
    {
            release
    {
                storeFile file
    ("myreleasekey.keystore")
                storePassword
    "password"
                keyAlias
    "MyReleaseKey"
                keyPassword
    "password"
           
    }
       
    }
        buildTypes
    {
            release
    {
               
    ...
                signingConfig signingConfigs
    .release
           
    }
       
    }
    }

To generate a signed APK, select Build > Generate Signed APK from the main menu. The package in app/build/apk/app-release.apk is now signed with your release key.

Note: Including the passwords for your release key and keystore inside the build file is not a good security practice. Alternatively, you can configure the build file to obtain these passwords from environment variables or have the build process prompt you for these passwords.

To obtain these passwords from environment variables:

storePassword System.getenv("KSTOREPWD")
keyPassword
System.getenv("KEYPWD")

To have the build process prompt you for these passwords if you are invoking the build from the command line:

storePassword System.console().readLine("\nKeystore password: ")
keyPassword
System.console().readLine("\nKey password: ")

After you complete this process, you can distribute your app and publish it on Google Play.

Warning: Keep your keystore and private key in a safe and secure place, and ensure that you have secure backups of them. If you publish an app to Google Play and then lose the key with which you signed your app, you will not be able to publish any updates to your app, since you must always sign all versions of your app with the same key.

Signing Android Wear Apps

When publishing Android Wear apps, you package the wearable app inside of a handheld app, because users cannot browse and install apps directly on the wearable. Both apps must be signed. For more information on packaging and signing Android Wear apps, see Packaging Wearable Apps.


반응형

댓글