Советы и рецепты Gradle

Gradle и плагин Android для Gradle предоставляют гибкий способ компиляции, сборки и упаковки вашего приложения или библиотеки Android. На этой странице собраны некоторые полезные советы и настройки, которые помогут вам максимально эффективно использовать каждую сборку. Если вы хотите узнать о способах ускорения сборки, прочтите «Оптимизация скорости сборки» .

Если вы новичок в Gradle, изучите основы, прочитав «Настройка сборки» . Вы также можете просмотреть справочную документацию DSL плагина Android, чтобы узнать больше о свойствах, используемых на этой странице.

Управление проектами и источниками

Вот несколько конфигураций для управления модулями вашего проекта и их источниками. Чтобы узнать больше о создании проектов и модулей и управлении ими, прочтите Обзор проектов .

Изменение конфигураций исходного набора по умолчанию

Вы можете использовать блок sourceSets в файле build.gradle на уровне модуля, чтобы изменить, где Gradle ищет файлы для каждого компонента исходного набора .

классный

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.srcDirs = ['other/res1', 'other/res2']

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Котлин

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    getByName("main") {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.setSrcDirs("other/java")

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.setSrcDirs("other/res1", "other/res2")

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile("other/AndroidManifest.xml")
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
    }
  }
}
...

Управление библиотеками и зависимостями

Gradle предоставляет надежный механизм управления зависимостями , будь то удаленные библиотеки или модули локальных библиотек .

Целевые сборки с конфигурациями зависимостей

Если вам нужна зависимость только для определенного набора исходных кодов вариантов сборки или набора исходных кодов для тестирования , напишите имя конфигурации зависимостей с заглавной буквы и добавьте к нему префикс имени варианта сборки или набора исходных кодов для тестирования.

классный

android {...}

// Creates Gradle dependency configurations to use in the dependencies block.
configurations {
  // For variants that combine a product flavor and build type, you need to
  // intitialize a placeholder for its dependency configuration.
  freeDebugRuntimeOnly{}
  ...
}

dependencies {
    // Adds an implementation dependency only to the "free" product flavor.
    freeImplementation 'com.google.firebase:firebase-ads:21.5.1'
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

Котлин

android {...}

dependencies {
    // Use ""() notation for custom flavors and build types
    // Adds an implementation dependency only to the "free" product flavor.
    "freeImplementation"("com.google.firebase:firebase-ads:21.5.1")
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    "freeDebugRuntimeOnly"(fileTree("dir" to "libs", "include" to "*.jar"))
    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

Создавайте разные версии вашего приложения

Gradle и плагин Android позволяют создавать разные версии вашего приложения из одного модуля путем настройки вариантов сборки .

Настройка динамических кодов версий

По умолчанию, когда Gradle генерирует APK для вашего проекта, каждый APK имеет ту же информацию о версии, которая указана в файле build.gradle на уровне модуля. Поскольку в магазине Google Play не допускается использование нескольких APK для одного и того же приложения, имеющих одинаковую информацию о версии, перед загрузкой в ​​Play Store необходимо убедиться, что каждый APK имеет свой уникальный код версии .

Вы можете сделать это с помощью собственной логики сборки, которая назначает разный код версии каждому APK во время сборки. Например, при создании отдельных APK для каждого ABI автоматическое создание версий APK выглядит примерно так:

классный

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, mips:2, x86:3]

// For per-density APKs, create a similar map like this:
// ext.densityCodes = ['hdpi': 1, 'xhdpi': 2, 'xxhdpi': 3, 'xxxhdpi': 4]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code does not override the version code for universal APKs.
    // However, because we want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the version code
      // for only the output APK, not for the variant itself. Skipping this step simply
      // causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Котлин

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "mips" to 2, "x86" to 3)

// For per-density APKs, create a similar map like this:
// val densityCodes = mapOf("hdpi" to 1, "xhdpi" to 2, "xxhdpi" to 3, "xxxhdpi" to 4)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code does not override the version code for universal APKs.
            // However, because we want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

Комбинируйте несколько вкусов продуктов

В некоторых случаях вам может потребоваться объединить конфигурации из нескольких вариантов продукта . Для этого плагин Android для Gradle позволяет создавать группы вкусов продукта, называемые измерениями вкусов .

В следующем примере кода свойство flavorDimensions используется для создания измерения вкуса «mode» для группировки «полных» и «демо» вариантов продукта, а также измерения аромата «api» для группировки конфигураций вариантов продукта на основе уровня API. Затем Gradle объединяет вкусы продукта из измерения «режим» с ароматами из измерения «api».

классный

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion '24'
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion '23'
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion '21'
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdkVersion(24)
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode = 30000 + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdkVersion(23)
      versionCode = 20000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdkVersion(21)
      versionCode = 10000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Фильтровать варианты

Вы можете фильтровать варианты сборки , которые вам не нужны, используя variantFilter в файле build.gradle модуля. Следующий пример кода сообщает Gradle не создавать варианты, сочетающие в себе версии продукта «minApi21» и «демо»:

классный

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

Котлин

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enabled = false
        }
    }
}
...

Проверьте свое приложение

Чтобы узнать больше о запуске локальных и интегрированных модульных тестов, прочтите статью «Протестируйте свое приложение» .

Настройка параметров ворса

Вы можете настроить определенные параметры lint, используя блок lintOptions в файле build.gradle уровня модуля. Чтобы узнать больше об использовании lint для вашего проекта Android, прочтите статью «Улучшите свой код с помощью Lint» .

классный

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable 'TypographyFractions','TypographyQuotes'
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly 'NewApi', 'InlinedApi'
        // If set to true, turns off analysis progress reporting by lint.
        quiet true
        // if set to true (default), stops the build if errors are found.
        abortOnError false
        // if true, only report errors.
        ignoreWarnings true
    }
}
...

Котлин

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable("TypographyFractions")
        disable("TypographyQuotes")
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable("RtlHardcoded")
        enable("RtlCompat")
        enable("RtlEnabled")
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly("NewApi", "InlinedApi")
        // If set to true, turns off analysis progress reporting by lint.
        quiet = true
        // if set to true (default), stops the build if errors are found.
        abortOnError = false
        // if true, only report errors.
        ignoreWarnings = true
    }
}
...

Настройка параметров манифеста инструментария

Когда Gradle собирает тестовый APK, он автоматически генерирует файл AndroidManifest.xml и настраивает его с помощью узла <instrumentation> . Вы можете изменить некоторые параметры этого узла, создав другой файл манифеста в наборе исходного кода теста или настроив файл build.gradle на уровне модуля, как показано в следующем примере кода.

классный

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest true
  }
}
...

Котлин

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId = "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner = "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling = true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest = true
  }
}
...

Изменение типа тестовой сборки

По умолчанию все тесты выполняются для типа сборки отладки. Вы можете изменить его на другой тип сборки, используя свойство testBuildType в файле build.gradle уровня модуля. Например, если вы хотите запустить тесты для «промежуточного» типа сборки, отредактируйте файл, как показано в следующем фрагменте.

классный

android {
    ...
    testBuildType "staging"
}

Котлин

android {
    ...
    testBuildType "staging"
}

Настройка параметров теста Gradle

Чтобы указать параметры, которые изменяют способ запуска всех ваших тестов Gradle, настройте блок testOptions в файле build.gradle на уровне модуля.

классный

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Котлин

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Чтобы указать параметры только для локальных модульных тестов, настройте блок testOptions.unitTests .

классный

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Котлин

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Оптимизируйте свою сборку

В этом разделе представлены некоторые конфигурации, которые помогут ускорить полную и инкрементную сборку. Чтобы узнать больше, прочтите «Оптимизируйте скорость сборки» .

Уменьшите свой код

Android Studio использует R8, который использует файлы правил ProGuard для сжатия вашего кода . Для новых проектов Android Studio использует файл настроек по умолчанию ( proguard-android.txt ) из tools/proguard/folder Android SDK. Для еще большего сокращения кода попробуйте файл proguard-android-optimize.txt , который находится в том же месте.

классный

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

Котлин

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

Чтобы добавить правила, специфичные для каждого варианта сборки, настройте дополнительное свойство proguardFiles для каждого варианта. Например, в следующем примере к «flavor2» добавляется flavor2-rules.pro . Теперь релизная версия «flavor2» использует все три файла правил, поскольку также применяются правила из блока релиза.

классный

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Опубликуйте свое приложение

Чтобы узнать больше о публикации вашего приложения в Google Play, прочтите статью «Опубликуйте свое приложение» .

Подпишите свое приложение

Хотя Android Studio предоставляет простой способ настройки подписи для выпускных сборок из пользовательского интерфейса, вы можете вручную настроить блок signingConfigs в файле build.gradle вашего модуля:

классный

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Котлин

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Удалите личную информацию для подписи из вашего проекта

По умолчанию конфигурации подписи записываются в виде обычного текста в файл build.gradle модуля. Если вы работаете в команде или над проектом с открытым исходным кодом, вы можете удалить эту конфиденциальную информацию из файлов сборки, выполнив следующие действия.

  1. Создайте файл с именем keystore.properties в корневом каталоге вашего проекта и включите следующую информацию:
    storePassword=myStorePassword
    keyPassword=myKeyPassword
    keyAlias=myKeyAlias
    storeFile=myStoreFileLocation
    
  2. В файле build.gradle загрузите файл keystore.properties следующим образом (это должно быть до блока android):

    классный

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    

    Котлин

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    
  3. Введите информацию о подписи, хранящуюся в объекте keystoreProperties :

    классный

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    

    Котлин

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    
  4. Нажмите «Синхронизировать сейчас» на панели уведомлений.

Чтобы узнать больше о подписании приложений, прочтите «Подпишите свое приложение» .

Упрощение разработки приложений

Следующие советы помогут упростить разработку приложения для Android.

Делитесь настраиваемыми полями и значениями ресурсов с кодом вашего приложения.

Во время сборки Gradle генерирует класс BuildConfig , чтобы код вашего приложения мог проверять информацию о текущей сборке. Вы также можете добавить настраиваемые поля в класс BuildConfig из файла конфигурации сборки Gradle с помощью метода buildConfigField() и получить доступ к этим значениям в коде среды выполнения вашего приложения. Аналогичным образом вы можете добавить значения ресурсов приложения с помощью resValue() .

классный

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

В коде вашего приложения вы можете получить доступ к свойствам следующим образом:

Котлин

...
Log.i(TAG, BuildConfig.BUILD_TIME)
Log.i(TAG, getString(R.string.build_time))

Ява

...
Log.i(TAG, BuildConfig.BUILD_TIME);
Log.i(TAG, getString(R.string.build_time));

Делитесь свойствами с манифестом

В некоторых случаях вам может потребоваться объявить одно и то же свойство как в манифесте, так и в коде (например, при объявлении полномочий для FileProvider ). Вместо того чтобы обновлять одно и то же свойство в нескольких местах для отражения изменений, определите одно свойство в файле build.gradle вашего модуля, чтобы оно было доступно как для манифеста, так и для вашего кода, как показано в следующем примере. Чтобы узнать больше, прочтите «Внедрение переменных сборки в манифест» .

классный

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    def filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders =
      [filesAuthority: filesAuthorityValue]
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

Котлин

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    val filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders["filesAuthority"] = filesAuthorityValue
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

В вашем манифесте получите доступ к заполнителю следующим образом:

<manifest>
  ...
  <application>
    ...
    <provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="${filesAuthority}"
      android:exported="false"
      android:grantUriPermissions="true">
      ...
    </provider>
  </application>
</manifest>

Доступ к полю FILES_AUTHORITY в коде вашего приложения выглядит примерно так:

Котлин

...
val contentUri: Uri = FileProvider.getUriForFile(context, BuildConfig.FILES_AUTHORITY, myFile)

Ява

...
Uri contentUri = FileProvider.getUriForFile(getContext(),
  BuildConfig.FILES_AUTHORITY,
  myFile);
,

Gradle и плагин Android для Gradle предоставляют гибкий способ компиляции, сборки и упаковки вашего приложения или библиотеки Android. На этой странице собраны некоторые полезные советы и настройки, которые помогут вам максимально эффективно использовать каждую сборку. Если вы хотите узнать о том, как ускорить сборку, прочтите «Оптимизация скорости сборки» .

Если вы новичок в Gradle, изучите основы, прочитав «Настройка сборки» . Вы также можете просмотреть справочную документацию DSL плагина Android, чтобы узнать больше о свойствах, используемых на этой странице.

Управление проектами и источниками

Вот несколько конфигураций для управления модулями вашего проекта и их источниками. Чтобы узнать больше о создании проектов и модулей и управлении ими, прочтите Обзор проектов .

Изменение конфигураций исходного набора по умолчанию

Вы можете использовать блок sourceSets в файле build.gradle на уровне модуля, чтобы изменить, где Gradle ищет файлы для каждого компонента исходного набора .

классный

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.srcDirs = ['other/res1', 'other/res2']

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Котлин

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    getByName("main") {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.setSrcDirs("other/java")

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.setSrcDirs("other/res1", "other/res2")

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile("other/AndroidManifest.xml")
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
    }
  }
}
...

Управление библиотеками и зависимостями

Gradle предоставляет надежный механизм управления зависимостями , будь то удаленные библиотеки или модули локальных библиотек .

Целевые сборки с конфигурациями зависимостей

Если вам нужна зависимость только для определенного набора исходных кодов вариантов сборки или набора исходных кодов для тестирования , напишите имя конфигурации зависимостей с заглавной буквы и добавьте к нему префикс имени варианта сборки или набора исходных кодов для тестирования.

классный

android {...}

// Creates Gradle dependency configurations to use in the dependencies block.
configurations {
  // For variants that combine a product flavor and build type, you need to
  // intitialize a placeholder for its dependency configuration.
  freeDebugRuntimeOnly{}
  ...
}

dependencies {
    // Adds an implementation dependency only to the "free" product flavor.
    freeImplementation 'com.google.firebase:firebase-ads:21.5.1'
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

Котлин

android {...}

dependencies {
    // Use ""() notation for custom flavors and build types
    // Adds an implementation dependency only to the "free" product flavor.
    "freeImplementation"("com.google.firebase:firebase-ads:21.5.1")
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    "freeDebugRuntimeOnly"(fileTree("dir" to "libs", "include" to "*.jar"))
    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

Создавайте разные версии вашего приложения

Gradle и плагин Android позволяют создавать разные версии вашего приложения из одного модуля путем настройки вариантов сборки .

Настройка динамических кодов версий

По умолчанию, когда Gradle генерирует APK для вашего проекта, каждый APK имеет ту же информацию о версии, которая указана в файле build.gradle на уровне модуля. Поскольку в магазине Google Play не допускается использование нескольких APK для одного и того же приложения, имеющих одинаковую информацию о версии, перед загрузкой в ​​Play Store необходимо убедиться, что каждый APK имеет свой уникальный код версии .

Вы можете сделать это с помощью собственной логики сборки, которая назначает разный код версии каждому APK во время сборки. Например, при создании отдельных APK для каждого ABI автоматическое создание версий APK выглядит примерно так:

классный

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, mips:2, x86:3]

// For per-density APKs, create a similar map like this:
// ext.densityCodes = ['hdpi': 1, 'xhdpi': 2, 'xxhdpi': 3, 'xxxhdpi': 4]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code does not override the version code for universal APKs.
    // However, because we want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the version code
      // for only the output APK, not for the variant itself. Skipping this step simply
      // causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Котлин

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "mips" to 2, "x86" to 3)

// For per-density APKs, create a similar map like this:
// val densityCodes = mapOf("hdpi" to 1, "xhdpi" to 2, "xxhdpi" to 3, "xxxhdpi" to 4)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code does not override the version code for universal APKs.
            // However, because we want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

Комбинируйте несколько вкусов продуктов

В некоторых случаях вам может потребоваться объединить конфигурации из нескольких вариантов продукта . Для этого плагин Android для Gradle позволяет создавать группы вкусов продукта, называемые измерениями вкусов .

В следующем примере кода свойство flavorDimensions используется для создания измерения вкуса «mode» для группировки «полных» и «демо» вариантов продукта, а также измерения аромата «api» для группировки конфигураций вариантов продукта на основе уровня API. Затем Gradle объединяет вкусы продукта из измерения «режим» с ароматами из измерения «api».

классный

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion '24'
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion '23'
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion '21'
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdkVersion(24)
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode = 30000 + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdkVersion(23)
      versionCode = 20000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdkVersion(21)
      versionCode = 10000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Фильтровать варианты

Вы можете фильтровать варианты сборки , которые вам не нужны, используя variantFilter в файле build.gradle модуля. Следующий пример кода сообщает Gradle не создавать варианты, сочетающие в себе версии продукта «minApi21» и «демо»:

классный

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

Котлин

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enabled = false
        }
    }
}
...

Проверьте свое приложение

Чтобы узнать больше о запуске локальных и интегрированных модульных тестов, прочтите статью «Протестируйте свое приложение» .

Настройка параметров ворса

Вы можете настроить определенные параметры lint, используя блок lintOptions в файле build.gradle уровня модуля. Чтобы узнать больше об использовании lint для вашего проекта Android, прочтите статью «Улучшите свой код с помощью Lint» .

классный

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable 'TypographyFractions','TypographyQuotes'
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly 'NewApi', 'InlinedApi'
        // If set to true, turns off analysis progress reporting by lint.
        quiet true
        // if set to true (default), stops the build if errors are found.
        abortOnError false
        // if true, only report errors.
        ignoreWarnings true
    }
}
...

Котлин

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable("TypographyFractions")
        disable("TypographyQuotes")
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable("RtlHardcoded")
        enable("RtlCompat")
        enable("RtlEnabled")
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly("NewApi", "InlinedApi")
        // If set to true, turns off analysis progress reporting by lint.
        quiet = true
        // if set to true (default), stops the build if errors are found.
        abortOnError = false
        // if true, only report errors.
        ignoreWarnings = true
    }
}
...

Настройка параметров манифеста инструментария

Когда Gradle собирает тестовый APK, он автоматически генерирует файл AndroidManifest.xml и настраивает его с помощью узла <instrumentation> . Вы можете изменить некоторые параметры этого узла, создав другой файл манифеста в наборе исходного кода теста или настроив файл build.gradle на уровне модуля, как показано в следующем примере кода.

классный

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest true
  }
}
...

Котлин

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId = "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner = "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling = true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest = true
  }
}
...

Изменение типа тестовой сборки

По умолчанию все тесты выполняются для типа сборки отладки. Вы можете изменить его на другой тип сборки, используя свойство testBuildType в файле build.gradle уровня модуля. Например, если вы хотите запустить тесты для «промежуточного» типа сборки, отредактируйте файл, как показано в следующем фрагменте.

классный

android {
    ...
    testBuildType "staging"
}

Котлин

android {
    ...
    testBuildType "staging"
}

Настройка параметров теста Gradle

Чтобы указать параметры, которые изменяют способ запуска всех ваших тестов Gradle, настройте блок testOptions в файле build.gradle на уровне модуля.

классный

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Котлин

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Чтобы указать параметры только для локальных модульных тестов, настройте блок testOptions.unitTests .

классный

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Котлин

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Оптимизируйте свою сборку

В этом разделе представлены некоторые конфигурации, которые помогут ускорить полную и инкрементную сборку. Чтобы узнать больше, прочтите «Оптимизируйте скорость сборки» .

Уменьшите свой код

Android Studio использует R8, который использует файлы правил ProGuard для сжатия вашего кода . Для новых проектов Android Studio использует файл настроек по умолчанию ( proguard-android.txt ) из tools/proguard/folder Android SDK. Для еще большего сокращения кода попробуйте файл proguard-android-optimize.txt , который находится в том же месте.

классный

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

Котлин

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

Чтобы добавить правила, специфичные для каждого варианта сборки, настройте дополнительное свойство proguardFiles для каждого варианта. Например, в следующем примере к «flavor2» добавляется flavor2-rules.pro . Теперь релизная версия «flavor2» использует все три файла правил, поскольку также применяются правила из блока релиза.

классный

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Опубликуйте свое приложение

Чтобы узнать больше о публикации вашего приложения в Google Play, прочтите статью «Опубликуйте свое приложение» .

Подпишите свое приложение

Хотя Android Studio предоставляет простой способ настройки подписи для выпускных сборок из пользовательского интерфейса, вы можете вручную настроить блок signingConfigs в файле build.gradle вашего модуля:

классный

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Котлин

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Удалите личную информацию для подписи из вашего проекта

По умолчанию конфигурации подписи записываются в виде обычного текста в файл build.gradle модуля. Если вы работаете с командой или проектом с открытым исходным кодом, вы можете удалить эту конфиденциальную информацию из файлов сборки, выполнив следующие действия.

  1. Создайте файл с именем keystore.properties в корневом каталоге вашего проекта и включите следующую информацию:
    storePassword=myStorePassword
    keyPassword=myKeyPassword
    keyAlias=myKeyAlias
    storeFile=myStoreFileLocation
    
  2. В файле build.gradle загрузите файл keystore.properties следующим образом (это должно быть до блока android):

    классный

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    

    Котлин

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    
  3. Введите информацию о подписи, хранящуюся в объекте keystoreProperties :

    классный

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    

    Котлин

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    
  4. Нажмите «Синхронизировать сейчас» на панели уведомлений.

Чтобы узнать больше о подписании приложений, прочтите «Подпишите свое приложение» .

Упрощение разработки приложений

Следующие советы помогут упростить разработку приложения для Android.

Делитесь настраиваемыми полями и значениями ресурсов с кодом вашего приложения.

Во время сборки Gradle генерирует класс BuildConfig , чтобы код вашего приложения мог проверять информацию о текущей сборке. Вы также можете добавить настраиваемые поля в класс BuildConfig из файла конфигурации сборки Gradle с помощью метода buildConfigField() и получить доступ к этим значениям в коде среды выполнения вашего приложения. Аналогичным образом вы можете добавить значения ресурсов приложения с помощью resValue() .

классный

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

В коде вашего приложения вы можете получить доступ к свойствам следующим образом:

Котлин

...
Log.i(TAG, BuildConfig.BUILD_TIME)
Log.i(TAG, getString(R.string.build_time))

Ява

...
Log.i(TAG, BuildConfig.BUILD_TIME);
Log.i(TAG, getString(R.string.build_time));

Делитесь свойствами с манифестом

В некоторых случаях вам может потребоваться объявить одно и то же свойство как в манифесте, так и в коде (например, при объявлении полномочий для FileProvider ). Вместо того чтобы обновлять одно и то же свойство в нескольких местах для отражения изменений, определите одно свойство в файле build.gradle вашего модуля, чтобы оно было доступно как для манифеста, так и для вашего кода, как показано в следующем примере. Чтобы узнать больше, прочтите «Внедрение переменных сборки в манифест» .

классный

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    def filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders =
      [filesAuthority: filesAuthorityValue]
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

Котлин

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    val filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders["filesAuthority"] = filesAuthorityValue
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

В вашем манифесте получите доступ к заполнителю следующим образом:

<manifest>
  ...
  <application>
    ...
    <provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="${filesAuthority}"
      android:exported="false"
      android:grantUriPermissions="true">
      ...
    </provider>
  </application>
</manifest>

Доступ к полю FILES_AUTHORITY в коде вашего приложения выглядит примерно так:

Котлин

...
val contentUri: Uri = FileProvider.getUriForFile(context, BuildConfig.FILES_AUTHORITY, myFile)

Ява

...
Uri contentUri = FileProvider.getUriForFile(getContext(),
  BuildConfig.FILES_AUTHORITY,
  myFile);
,

Gradle и плагин Android для Gradle предоставляют гибкий способ компиляции, сборки и упаковки вашего приложения или библиотеки Android. На этой странице собраны некоторые полезные советы и настройки, которые помогут вам максимально эффективно использовать каждую сборку. Если вы хотите узнать о том, как ускорить сборку, прочтите «Оптимизация скорости сборки» .

Если вы новичок в Gradle, изучите основы, прочитав «Настройка сборки» . Вы также можете просмотреть справочную документацию DSL плагина Android, чтобы узнать больше о свойствах, используемых на этой странице.

Управление проектами и источниками

Вот несколько конфигураций для управления модулями вашего проекта и их источниками. Чтобы узнать больше о создании проектов и модулей и управлении ими, прочтите Обзор проектов .

Изменение конфигураций исходного набора по умолчанию

Вы можете использовать блок sourceSets в файле build.gradle на уровне модуля, чтобы изменить, где Gradle ищет файлы для каждого компонента исходного набора .

классный

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.srcDirs = ['other/res1', 'other/res2']

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Котлин

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    getByName("main") {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.setSrcDirs("other/java")

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.setSrcDirs("other/res1", "other/res2")

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile("other/AndroidManifest.xml")
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
    }
  }
}
...

Управление библиотеками и зависимостями

Gradle предоставляет надежный механизм управления зависимостями , будь то удаленные библиотеки или модули локальных библиотек .

Целевые сборки с конфигурациями зависимостей

Если вам нужна зависимость только для определенного набора исходных кодов вариантов сборки или набора исходных кодов для тестирования , напишите имя конфигурации зависимостей с заглавной буквы и добавьте к нему префикс имени варианта сборки или набора исходных кодов для тестирования.

классный

android {...}

// Creates Gradle dependency configurations to use in the dependencies block.
configurations {
  // For variants that combine a product flavor and build type, you need to
  // intitialize a placeholder for its dependency configuration.
  freeDebugRuntimeOnly{}
  ...
}

dependencies {
    // Adds an implementation dependency only to the "free" product flavor.
    freeImplementation 'com.google.firebase:firebase-ads:21.5.1'
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

Котлин

android {...}

dependencies {
    // Use ""() notation for custom flavors and build types
    // Adds an implementation dependency only to the "free" product flavor.
    "freeImplementation"("com.google.firebase:firebase-ads:21.5.1")
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    "freeDebugRuntimeOnly"(fileTree("dir" to "libs", "include" to "*.jar"))
    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

Создавайте разные версии вашего приложения

Gradle и плагин Android позволяют создавать разные версии вашего приложения из одного модуля путем настройки вариантов сборки .

Настройка динамических кодов версий

По умолчанию, когда Gradle генерирует APK для вашего проекта, каждый APK имеет ту же информацию о версии, которая указана в файле build.gradle на уровне модуля. Поскольку в магазине Google Play не допускается использование нескольких APK для одного и того же приложения, имеющих одинаковую информацию о версии, вам необходимо убедиться, что каждый APK имеет свой собственный уникальный код версии, прежде чем загружать его в Play Store.

Вы можете сделать это с помощью собственной логики сборки, которая назначает разный код версии каждому APK во время сборки. Например, при создании отдельных APK для каждого ABI автоматическое создание версий APK выглядит примерно так:

классный

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, mips:2, x86:3]

// For per-density APKs, create a similar map like this:
// ext.densityCodes = ['hdpi': 1, 'xhdpi': 2, 'xxhdpi': 3, 'xxxhdpi': 4]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code does not override the version code for universal APKs.
    // However, because we want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the version code
      // for only the output APK, not for the variant itself. Skipping this step simply
      // causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Котлин

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "mips" to 2, "x86" to 3)

// For per-density APKs, create a similar map like this:
// val densityCodes = mapOf("hdpi" to 1, "xhdpi" to 2, "xxhdpi" to 3, "xxxhdpi" to 4)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code does not override the version code for universal APKs.
            // However, because we want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

Комбинируйте несколько вкусов продуктов

В некоторых случаях вам может потребоваться объединить конфигурации из нескольких вариантов продукта . Для этого плагин Android для Gradle позволяет создавать группы вкусов продукта, называемые измерениями вкусов .

В следующем примере кода свойство flavorDimensions используется для создания измерения вкуса «mode» для группировки «полных» и «демо» вариантов продукта, а также измерения вкуса «api» для группировки конфигураций вариантов продукта на основе уровня API. Затем Gradle объединяет вкусы продукта из измерения «режим» с ароматами из измерения «api».

классный

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion '24'
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion '23'
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion '21'
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdkVersion(24)
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode = 30000 + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdkVersion(23)
      versionCode = 20000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdkVersion(21)
      versionCode = 10000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Фильтровать варианты

Вы можете фильтровать варианты сборки , которые вам не нужны, используя variantFilter в файле build.gradle модуля. Следующий пример кода сообщает Gradle не создавать варианты, сочетающие в себе версии продукта «minApi21» и «демо»:

классный

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

Котлин

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enabled = false
        }
    }
}
...

Проверьте свое приложение

Чтобы узнать больше о запуске локальных и интегрированных модульных тестов, прочтите статью «Протестируйте свое приложение» .

Настройка параметров ворса

Вы можете настроить определенные параметры lint, используя блок lintOptions в файле build.gradle уровня модуля. Чтобы узнать больше об использовании lint для вашего проекта Android, прочтите статью «Улучшите свой код с помощью Lint» .

классный

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable 'TypographyFractions','TypographyQuotes'
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly 'NewApi', 'InlinedApi'
        // If set to true, turns off analysis progress reporting by lint.
        quiet true
        // if set to true (default), stops the build if errors are found.
        abortOnError false
        // if true, only report errors.
        ignoreWarnings true
    }
}
...

Котлин

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable("TypographyFractions")
        disable("TypographyQuotes")
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable("RtlHardcoded")
        enable("RtlCompat")
        enable("RtlEnabled")
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly("NewApi", "InlinedApi")
        // If set to true, turns off analysis progress reporting by lint.
        quiet = true
        // if set to true (default), stops the build if errors are found.
        abortOnError = false
        // if true, only report errors.
        ignoreWarnings = true
    }
}
...

Настройка параметров манифеста инструментария

Когда Gradle собирает тестовый APK, он автоматически генерирует файл AndroidManifest.xml и настраивает его с помощью узла <instrumentation> . Вы можете изменить некоторые параметры этого узла, создав еще один файл манифеста в наборе исходного кода теста или настроив файл build.gradle на уровне модуля, как показано в следующем примере кода.

классный

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest true
  }
}
...

Котлин

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId = "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner = "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling = true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest = true
  }
}
...

Изменение типа тестовой сборки

По умолчанию все тесты выполняются для типа сборки отладки. Вы можете изменить его на другой тип сборки, используя свойство testBuildType в файле build.gradle уровня модуля. Например, если вы хотите запустить тесты для «промежуточного» типа сборки, отредактируйте файл, как показано в следующем фрагменте.

классный

android {
    ...
    testBuildType "staging"
}

Котлин

android {
    ...
    testBuildType "staging"
}

Настройка параметров теста Gradle

Чтобы указать параметры, которые изменяют способ запуска всех ваших тестов Gradle, настройте блок testOptions в файле build.gradle на уровне модуля.

классный

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Котлин

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Чтобы указать параметры только для локальных модульных тестов, настройте блок testOptions.unitTests .

классный

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Котлин

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Оптимизируйте свою сборку

В этом разделе представлены некоторые конфигурации, которые помогут ускорить полную и инкрементную сборку. Чтобы узнать больше, прочтите «Оптимизируйте скорость сборки» .

Уменьшите свой код

Android Studio использует R8, который использует файлы правил ProGuard для сжатия вашего кода . Для новых проектов Android Studio использует файл настроек по умолчанию ( proguard-android.txt ) из tools/proguard/folder Android SDK. Для еще большего сокращения кода попробуйте файл proguard-android-optimize.txt , который находится в том же месте.

классный

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

Котлин

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

Чтобы добавить правила, специфичные для каждого варианта сборки, настройте дополнительное свойство proguardFiles для каждого варианта. Например, в следующем примере к «flavor2» добавляется flavor2-rules.pro . Теперь релизная версия «flavor2» использует все три файла правил, поскольку также применяются правила из блока релиза.

классный

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Опубликуйте свое приложение

Чтобы узнать больше о публикации вашего приложения в Google Play, прочтите статью «Опубликуйте свое приложение» .

Подпишите свое приложение

Хотя Android Studio предоставляет простой способ настройки подписи для выпускных сборок из пользовательского интерфейса, вы можете вручную настроить блок signingConfigs в файле build.gradle вашего модуля:

классный

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Котлин

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Удалите личную информацию для подписи из вашего проекта

По умолчанию конфигурации подписи записываются в виде обычного текста в файл build.gradle модуля. Если вы работаете с командой или проектом с открытым исходным кодом, вы можете удалить эту конфиденциальную информацию из файлов сборки, выполнив следующие действия.

  1. Создайте файл с именем keystore.properties в корневом каталоге вашего проекта и включите следующую информацию:
    storePassword=myStorePassword
    keyPassword=myKeyPassword
    keyAlias=myKeyAlias
    storeFile=myStoreFileLocation
    
  2. В файле build.gradle загрузите файл keystore.properties следующим образом (это должно быть до блока android):

    классный

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    

    Котлин

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    
  3. Введите информацию о подписи, хранящуюся в объекте keystoreProperties :

    классный

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    

    Котлин

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    
  4. Нажмите «Синхронизировать сейчас» на панели уведомлений.

Чтобы узнать больше о подписании приложений, прочтите «Подпишите свое приложение» .

Упрощение разработки приложений

Следующие советы помогут упростить разработку приложения для Android.

Делитесь настраиваемыми полями и значениями ресурсов с кодом вашего приложения.

Во время сборки Gradle генерирует класс BuildConfig , чтобы код вашего приложения мог проверять информацию о текущей сборке. Вы также можете добавить настраиваемые поля в класс BuildConfig из файла конфигурации сборки Gradle с помощью метода buildConfigField() и получить доступ к этим значениям в коде среды выполнения вашего приложения. Аналогичным образом вы можете добавить значения ресурсов приложения с помощью resValue() .

классный

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

В коде вашего приложения вы можете получить доступ к свойствам следующим образом:

Котлин

...
Log.i(TAG, BuildConfig.BUILD_TIME)
Log.i(TAG, getString(R.string.build_time))

Ява

...
Log.i(TAG, BuildConfig.BUILD_TIME);
Log.i(TAG, getString(R.string.build_time));

Делитесь свойствами с манифестом

В некоторых случаях вам может потребоваться объявить одно и то же свойство как в манифесте, так и в коде (например, при объявлении полномочий для FileProvider ). Вместо того чтобы обновлять одно и то же свойство в нескольких местах для отражения изменений, определите одно свойство в файле build.gradle вашего модуля, чтобы оно было доступно как для манифеста, так и для вашего кода, как показано в следующем примере. Чтобы узнать больше, прочтите «Внедрение переменных сборки в манифест» .

классный

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    def filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders =
      [filesAuthority: filesAuthorityValue]
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

Котлин

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    val filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders["filesAuthority"] = filesAuthorityValue
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

В вашем манифесте получите доступ к заполнителю следующим образом:

<manifest>
  ...
  <application>
    ...
    <provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="${filesAuthority}"
      android:exported="false"
      android:grantUriPermissions="true">
      ...
    </provider>
  </application>
</manifest>

Доступ к полю FILES_AUTHORITY в коде вашего приложения выглядит примерно так:

Котлин

...
val contentUri: Uri = FileProvider.getUriForFile(context, BuildConfig.FILES_AUTHORITY, myFile)

Ява

...
Uri contentUri = FileProvider.getUriForFile(getContext(),
  BuildConfig.FILES_AUTHORITY,
  myFile);
,

Gradle и плагин Android для Gradle предоставляют гибкий способ компиляции, сборки и упаковки вашего приложения или библиотеки Android. На этой странице собраны некоторые полезные советы и настройки, которые помогут вам максимально эффективно использовать каждую сборку. Если вы хотите узнать о том, как ускорить сборку, прочтите «Оптимизация скорости сборки» .

Если вы новичок в Gradle, изучите основы, прочитав «Настройка сборки» . Вы также можете просмотреть справочную документацию DSL плагина Android, чтобы узнать больше о свойствах, используемых на этой странице.

Управление проектами и источниками

Вот несколько конфигураций для управления модулями вашего проекта и их источниками. Чтобы узнать больше о создании проектов и модулей и управлении ими, прочтите Обзор проектов .

Изменение конфигураций исходного набора по умолчанию

Вы можете использовать блок sourceSets в файле build.gradle на уровне модуля, чтобы изменить, где Gradle ищет файлы для каждого компонента исходного набора .

классный

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.srcDirs = ['other/res1', 'other/res2']

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Котлин

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    getByName("main") {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.setSrcDirs("other/java")

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.setSrcDirs("other/res1", "other/res2")

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile("other/AndroidManifest.xml")
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
    }
  }
}
...

Управление библиотеками и зависимостями

Gradle предоставляет надежный механизм управления зависимостями , будь то удаленные библиотеки или модули локальных библиотек .

Целевые сборки с конфигурациями зависимостей

Если вам нужна зависимость только для определенного набора исходных кодов вариантов сборки или набора исходных кодов для тестирования , напишите имя конфигурации зависимостей с заглавной буквы и добавьте к нему префикс имени варианта сборки или набора исходных кодов для тестирования.

классный

android {...}

// Creates Gradle dependency configurations to use in the dependencies block.
configurations {
  // For variants that combine a product flavor and build type, you need to
  // intitialize a placeholder for its dependency configuration.
  freeDebugRuntimeOnly{}
  ...
}

dependencies {
    // Adds an implementation dependency only to the "free" product flavor.
    freeImplementation 'com.google.firebase:firebase-ads:21.5.1'
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

Котлин

android {...}

dependencies {
    // Use ""() notation for custom flavors and build types
    // Adds an implementation dependency only to the "free" product flavor.
    "freeImplementation"("com.google.firebase:firebase-ads:21.5.1")
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    "freeDebugRuntimeOnly"(fileTree("dir" to "libs", "include" to "*.jar"))
    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

Создавайте разные версии вашего приложения

Gradle и плагин Android позволяют создавать разные версии вашего приложения из одного модуля путем настройки вариантов сборки .

Настройка динамических кодов версий

По умолчанию, когда Gradle генерирует APK для вашего проекта, каждый APK имеет ту же информацию о версии, которая указана в файле build.gradle на уровне модуля. Поскольку в магазине Google Play не допускается использование нескольких APK для одного и того же приложения, имеющих одинаковую информацию о версии, перед загрузкой в ​​Play Store необходимо убедиться, что каждый APK имеет свой уникальный код версии .

Вы можете сделать это с помощью собственной логики сборки, которая назначает разный код версии каждому APK во время сборки. Например, при создании отдельных APK для каждого ABI автоматическое создание версий APK выглядит примерно так:

классный

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, mips:2, x86:3]

// For per-density APKs, create a similar map like this:
// ext.densityCodes = ['hdpi': 1, 'xhdpi': 2, 'xxhdpi': 3, 'xxxhdpi': 4]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code does not override the version code for universal APKs.
    // However, because we want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the version code
      // for only the output APK, not for the variant itself. Skipping this step simply
      // causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Котлин

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "mips" to 2, "x86" to 3)

// For per-density APKs, create a similar map like this:
// val densityCodes = mapOf("hdpi" to 1, "xhdpi" to 2, "xxhdpi" to 3, "xxxhdpi" to 4)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code does not override the version code for universal APKs.
            // However, because we want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

Комбинируйте несколько вкусов продуктов

В некоторых случаях вам может потребоваться объединить конфигурации из нескольких вариантов продукта . Для этого плагин Android для Gradle позволяет создавать группы вкусов продукта, называемые измерениями вкусов .

В следующем примере кода свойство flavorDimensions используется для создания измерения вкуса «mode» для группировки «полных» и «демо» вариантов продукта, а также измерения вкуса «api» для группировки конфигураций вариантов продукта на основе уровня API. Затем Gradle объединяет вкусы продукта из измерения «режим» с ароматами из измерения «api».

классный

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion '24'
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion '23'
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion '21'
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdkVersion(24)
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode = 30000 + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdkVersion(23)
      versionCode = 20000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdkVersion(21)
      versionCode = 10000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Фильтровать варианты

Вы можете фильтровать варианты сборки , которые вам не нужны, используя variantFilter в файле build.gradle модуля. Следующий пример кода сообщает Gradle не создавать варианты, сочетающие в себе версии продукта «minApi21» и «демо»:

классный

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

Котлин

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enabled = false
        }
    }
}
...

Проверьте свое приложение

Чтобы узнать больше о запуске локальных и интегрированных модульных тестов, прочтите статью «Протестируйте свое приложение» .

Настройка параметров ворса

Вы можете настроить определенные параметры lint, используя блок lintOptions в файле build.gradle уровня модуля. Чтобы узнать больше об использовании lint для вашего проекта Android, прочтите статью «Улучшите свой код с помощью Lint» .

классный

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable 'TypographyFractions','TypographyQuotes'
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly 'NewApi', 'InlinedApi'
        // If set to true, turns off analysis progress reporting by lint.
        quiet true
        // if set to true (default), stops the build if errors are found.
        abortOnError false
        // if true, only report errors.
        ignoreWarnings true
    }
}
...

Котлин

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable("TypographyFractions")
        disable("TypographyQuotes")
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable("RtlHardcoded")
        enable("RtlCompat")
        enable("RtlEnabled")
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly("NewApi", "InlinedApi")
        // If set to true, turns off analysis progress reporting by lint.
        quiet = true
        // if set to true (default), stops the build if errors are found.
        abortOnError = false
        // if true, only report errors.
        ignoreWarnings = true
    }
}
...

Настройка параметров манифеста инструментария

Когда Gradle собирает тестовый APK, он автоматически генерирует файл AndroidManifest.xml и настраивает его с помощью узла <instrumentation> . Вы можете изменить некоторые параметры этого узла, создав другой файл манифеста в наборе исходного кода теста или настроив файл build.gradle на уровне модуля, как показано в следующем примере кода.

классный

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest true
  }
}
...

Котлин

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId = "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner = "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling = true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest = true
  }
}
...

Изменение типа тестовой сборки

По умолчанию все тесты выполняются для типа сборки отладки. Вы можете изменить его на другой тип сборки, используя свойство testBuildType в файле build.gradle уровня модуля. Например, если вы хотите запустить тесты для «промежуточного» типа сборки, отредактируйте файл, как показано в следующем фрагменте.

классный

android {
    ...
    testBuildType "staging"
}

Котлин

android {
    ...
    testBuildType "staging"
}

Настройка параметров теста Gradle

Чтобы указать параметры, которые изменяют способ запуска всех ваших тестов Gradle, настройте блок testOptions в файле build.gradle на уровне модуля.

классный

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Котлин

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Чтобы указать параметры только для локальных модульных тестов, настройте блок testOptions.unitTests .

классный

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Котлин

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Оптимизируйте свою сборку

В этом разделе представлены некоторые конфигурации, которые помогут ускорить полную и инкрементную сборку. Чтобы узнать больше, прочтите «Оптимизируйте скорость сборки» .

Уменьшите свой код

Android Studio использует R8, который использует файлы правил ProGuard для сжатия вашего кода . Для новых проектов Android Studio использует файл настроек по умолчанию ( proguard-android.txt ) из tools/proguard/folder Android SDK. Для еще большего сокращения кода попробуйте файл proguard-android-optimize.txt , который находится в том же месте.

классный

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

Котлин

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

Чтобы добавить правила, специфичные для каждого варианта сборки, настройте дополнительное свойство proguardFiles для каждого варианта. Например, в следующем примере к «flavor2» добавляется flavor2-rules.pro . Теперь релизная версия «flavor2» использует все три файла правил, поскольку также применяются правила из блока релиза.

классный

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Опубликуйте свое приложение

Чтобы узнать больше о публикации вашего приложения в Google Play, прочтите статью «Опубликуйте свое приложение» .

Подпишите свое приложение

Хотя Android Studio предоставляет простой способ настройки подписи для выпускных сборок из пользовательского интерфейса, вы можете вручную настроить блок signingConfigs в файле build.gradle вашего модуля:

классный

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Котлин

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Удалите личную информацию для подписи из вашего проекта

По умолчанию конфигурации подписи записываются в виде обычного текста в файл build.gradle модуля. Если вы работаете с командой или проектом с открытым исходным кодом, вы можете удалить эту конфиденциальную информацию из файлов сборки, выполнив следующие действия.

  1. Создайте файл с именем keystore.properties в корневом каталоге вашего проекта и включите следующую информацию:
    storePassword=myStorePassword
    keyPassword=myKeyPassword
    keyAlias=myKeyAlias
    storeFile=myStoreFileLocation
    
  2. В файле build.gradle загрузите файл keystore.properties следующим образом (это должно быть до блока android):

    классный

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    

    Котлин

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    
  3. Введите информацию о подписи, хранящуюся в объекте keystoreProperties :

    классный

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    

    Котлин

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    
  4. Нажмите «Синхронизировать сейчас» на панели уведомлений.

Чтобы узнать больше о подписании приложений, прочтите «Подпишите свое приложение» .

Упрощение разработки приложений

Следующие советы помогут упростить разработку приложения для Android.

Делитесь настраиваемыми полями и значениями ресурсов с кодом вашего приложения.

Во время сборки Gradle генерирует класс BuildConfig , чтобы код вашего приложения мог проверять информацию о текущей сборке. Вы также можете добавить настраиваемые поля в класс BuildConfig из файла конфигурации сборки Gradle с помощью метода buildConfigField() и получить доступ к этим значениям в коде среды выполнения вашего приложения. Аналогичным образом вы можете добавить значения ресурсов приложения с помощью resValue() .

классный

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

Котлин

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

В коде вашего приложения вы можете получить доступ к свойствам следующим образом:

Котлин

...
Log.i(TAG, BuildConfig.BUILD_TIME)
Log.i(TAG, getString(R.string.build_time))

Ява

...
Log.i(TAG, BuildConfig.BUILD_TIME);
Log.i(TAG, getString(R.string.build_time));

Делитесь свойствами с манифестом

В некоторых случаях вам может потребоваться объявить одно и то же свойство как в манифесте, так и в коде (например, при объявлении полномочий для FileProvider ). Вместо того чтобы обновлять одно и то же свойство в нескольких местах для отражения изменений, определите одно свойство в файле build.gradle вашего модуля, чтобы оно было доступно как для манифеста, так и для вашего кода, как показано в следующем примере. Чтобы узнать больше, прочтите «Внедрение переменных сборки в манифест» .

классный

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    def filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders =
      [filesAuthority: filesAuthorityValue]
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

Котлин

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    val filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders["filesAuthority"] = filesAuthorityValue
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

В вашем манифесте получите доступ к заполнителю следующим образом:

<manifest>
  ...
  <application>
    ...
    <provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="${filesAuthority}"
      android:exported="false"
      android:grantUriPermissions="true">
      ...
    </provider>
  </application>
</manifest>

Доступ к полю FILES_AUTHORITY в коде вашего приложения выглядит примерно так:

Котлин

...
val contentUri: Uri = FileProvider.getUriForFile(context, BuildConfig.FILES_AUTHORITY, myFile)

Ява

...
Uri contentUri = FileProvider.getUriForFile(getContext(),
  BuildConfig.FILES_AUTHORITY,
  myFile);