Skonfiguruj warianty kompilacji

Na tej stronie pokazujemy, jak skonfigurować warianty kompilacji, aby tworzyć różne wersje aplikacji z jednego projektu, oraz jak poprawnie zarządzać zależnościami i konfiguracjami podpisywania.

Każdy wariant kompilacji reprezentuje inną wersję aplikacji, którą możesz skompilować. Możesz na przykład utworzyć jedną bezpłatną wersję aplikacji z ograniczonym zestawem treści i drugą płatną wersję z dodatkowymi treściami. Możesz też tworzyć różne wersje aplikacji kierowane na różne urządzenia, w zależności od poziomu interfejsu API lub innych wersji urządzeń.

Warianty kompilacji są wynikiem użycia przez Gradle określonego zestawu reguł do łączenia ustawień, kodu i zasobów skonfigurowanych w typach kompilacji i wersjach produktu. Chociaż nie konfigurujesz bezpośrednio wariantów kompilacji, konfigurujesz typy kompilacji i wersje produktów, które je tworzą.

Na przykład wersja demonstracyjna może zawierać określone funkcje i wymagania dotyczące urządzenia, takie jak niestandardowy kod źródłowy, zasoby i minimalne poziomy interfejsu API, podczas gdy typ kompilacji „debug” stosuje różne ustawienia kompilacji i pakowania, takie jak opcje debugowania i klucze podpisywania. Wersja kompilacji, która łączy te dwa warianty, to wersja „demoDebug” aplikacji. Zawiera ona kombinację konfiguracji i zasobów zawartych w wersji „demo” produktu, typie kompilacji „debug” oraz zbiorze main/.

Skonfiguruj typy kompilacji

Typy kompilacji możesz tworzyć i konfigurować w bloku android w pliku build.gradle.kts na poziomie modułu. Gdy utworzysz nowy moduł, Android Studio automatycznie utworzy kompilacje debugowania i wersji. Mimo że typ kompilacji do debugowania nie pojawia się w pliku konfiguracji kompilacji, Android Studio konfiguruje go za pomocą debuggable true. Umożliwia to debugowanie aplikacji na zabezpieczonych urządzeniach z Androidem i skonfigurowanie podpisywania aplikacji za pomocą ogólnego repozytorium kluczy debugowania.

Jeśli chcesz dodać lub zmienić określone ustawienia, możesz dodać do konfiguracji typ kompilacji do debugowania. W tym przykładzie dla typu kompilacji debugowania określono wartość applicationIdSuffix, a dla typu kompilacji „testowej” skonfigurowano ustawienia z kompilacji debugowej:

Kotlin

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
        ...
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }

        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        create("staging") {
            initWith(getByName("debug"))
            manifestPlaceholders["hostName"] = "internal.example.com"
            applicationIdSuffix = ".debugStaging"
        }
    }
}

Groovy

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
        ...
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            manifestPlaceholders = [hostName:"internal.example.com"]
            applicationIdSuffix ".debugStaging"
        }
    }
}

Uwaga: gdy wprowadzasz zmiany w pliku konfiguracji kompilacji, Android Studio wymaga zsynchronizowania projektu z nową konfiguracją. Aby zsynchronizować projekt, kliknij Synchronizuj teraz na pasku powiadomień, który pojawi się po wprowadzeniu zmiany, lub kliknij Synchronizuj projekt na pasku narzędzi. Jeśli Android Studio wykryje błędy w konfiguracji, pojawi się okno Wiadomości z opisem problemu.

Aby dowiedzieć się więcej o właściwościach, które możesz konfigurować za pomocą typów kompilacji, zapoznaj się z dokumentacją BuildType.

Konfigurowanie wersji produktu

Tworzenie wersji produktu przypomina tworzenie typów kompilacji. Dodaj wersje produktu do bloku productFlavors w konfiguracji kompilacji i uwzględnij odpowiednie ustawienia. Odmiany produktu obsługują te same właściwości co defaultConfig, ponieważ defaultConfig tak naprawdę należy do klasy ProductFlavor. Oznacza to, że możesz podać podstawową konfigurację dla wszystkich odmian w bloku defaultConfig, a każda odmiana może zmienić dowolną z tych wartości domyślnych, np. applicationId. Aby dowiedzieć się więcej o identyfikatorze aplikacji, przeczytaj artykuł Ustawianie identyfikatora aplikacji.

Uwaga: nadal musisz określić nazwę pakietu za pomocą atrybutu package w pliku manifestu main/. Musisz też użyć tej nazwy pakietu w kodze źródłowym, aby odwoływać się do klasy R lub rozwiązywać wszelkie problemy związane z aktywizacją lub rejestracją usługi. Dzięki temu możesz używać applicationId, aby nadać każdemu smakowi produktu unikalny identyfikator na potrzeby pakowania i dystrybucji bez konieczności zmiany kodu źródłowego.

Wszystkie smaki muszą należeć do nazwanego wymiaru smaku, który jest grupą smaków produktu. Musisz przypisać wszystkie smaki do wymiaru smaku. W przeciwnym razie pojawi się taki błąd kompilacji.

  Error: All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

Jeśli dany moduł określa tylko 1 wymiar wersji, wtyczka Gradle dla Androida automatycznie przypisuje wszystkie wersje tego modułu do tego wymiaru.

Podany niżej przykład kodu tworzy wymiar wersji o nazwie „version” i dodaje wersje produktów „demo” i „full”. Te smaki mają własne applicationIdSuffix i versionNameSuffix:

Kotlin

android {
    ...
    defaultConfig {...}
    buildTypes {
        getByName("debug"){...}
        getByName("release"){...}
    }
    // Specifies one flavor dimension.
    flavorDimensions += "version"
    productFlavors {
        create("demo") {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension = "version"
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
        }
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}

Groovy

android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

Uwaga: jeśli masz starszą aplikację (utworzoną przed sierpniem 2021 r.), którą rozpowszechniasz za pomocą plików APK w Google Play, aby dystrybuować aplikację z wykorzystaniem obsługi wielu plików APK w Google Play, przypisz tę samą wartość applicationId do wszystkich wariantów, a każdym z nich przypisz inny versionCode. Aby dystrybuować różne warianty aplikacji jako osobne aplikacje w Google Play, musisz przypisać do każdego z nich inny applicationId.

Po utworzeniu i skonfigurowaniu wersji produktu kliknij Synchronizuj na pasku powiadomień. Po zakończeniu synchronizacji Gradle automatycznie tworzy warianty kompilacji na podstawie typów kompilacji i wersji produktu oraz nadaje im nazwy zgodnie z <product-flavor><Build-Type>. Jeśli na przykład utworzysz wersje „demograficzne” i „pełne” oraz zachowasz domyślne typy kompilacji „debugowanie” i „wersja”, Gradle utworzy te wersje kompilacji:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Aby wybrać wersję kompilacji do utworzenia i uruchomienia, kliknij Kompiluj > Wybierz wersję kompilacji i w menu wybierz wersję kompilacji. Aby zacząć dostosowywać poszczególne wersje kompilacji za pomocą własnych funkcji i zasobów, musisz utworzyć zestawy źródeł i nimi zarządzać zgodnie z opisem na tej stronie.

Zmienianie identyfikatora aplikacji dla wariantów kompilacji

Gdy tworzysz plik APK lub AAB, narzędzia do kompilacji oznaczają aplikację identyfikatorem aplikacji zdefiniowanym w bloku defaultConfig w pliku build.gradle.kts, jak pokazano w tym przykładzie. Jeśli jednak chcesz utworzyć różne wersje aplikacji, które będą widoczne jako osobne strony w Google Play, np. wersja „bezpłatna” i „pro”, musisz utworzyć osobne warianty kompilacji, z których każdy będzie miał inny identyfikator aplikacji.

W takim przypadku zdefiniuj każdą wersję kompilacji jako osobny rodzaj produktów. W przypadku każdego wariantu w bloku productFlavors możesz zdefiniować ponownie właściwość applicationId lub dołączyć segment do domyślnego identyfikatora aplikacji za pomocą parametru applicationIdSuffix, jak pokazano poniżej:

Kotlin

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
        }
    }
}

Odlotowe

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

W ten sposób identyfikator aplikacji dla wersji „free” to „com.example.myapp.free”.

Możesz też użyć polecenia applicationIdSuffix, by dołączyć segment na podstawie typu kompilacji, jak w tym przykładzie:

Kotlin

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}

Groovy

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

Gradle stosuje konfigurację typu kompilacji po wersji produktu, więc identyfikator aplikacji dla wariantu kompilacji „bezpłatny debug” to „com.example.myapp.free.debug”. Jest to przydatne, gdy chcesz mieć na tym samym urządzeniu zarówno wersję debugującą, jak i wersję produkcyjną, ponieważ żadne 2 aplikacje nie mogą mieć tego samego identyfikatora aplikacji.

Jeśli masz starszą aplikację (utworzoną przed sierpniem 2021 r.), którą rozpowszechniasz w Google Play za pomocą plików APK, i chcesz używać tej samej strony aplikacji do rozpowszechniania wielu plików APK, z których każdy jest przeznaczony do innej konfiguracji urządzenia (np. z uwzględnieniem poziomu interfejsu API), musisz użyć tego samego identyfikatora aplikacji dla każdej wersji, ale nadać każdemu plikowi APK inny versionCode. Więcej informacji znajdziesz w artykule o obsłudze wielu plików APK. Publikowanie za pomocą pakietów APK nie jest w żaden sposób ograniczone, ponieważ używa on jednego artefaktu, który domyślnie korzysta z jednego kodu wersji i identyfikatora aplikacji.

Wskazówka: jeśli w pliku manifestu musisz odwoływać się do identyfikatora aplikacji, możesz użyć zastępnika ${applicationId} w dowolnym atrybucie pliku manifestu. Podczas kompilacji Gradle zastępuje ten tag rzeczywistym identyfikatorem aplikacji. Więcej informacji znajdziesz w artykule Wstrzykiwanie zmiennych kompilacji do pliku manifestu.

Łączenie wielu smaków produktów za pomocą wymiarów smaku

W niektórych przypadkach warto połączyć konfiguracje z różnych wersji produktu. Możesz na przykład utworzyć różne konfiguracje wersji „pełnej” i „demonstracyjnej” usługi na podstawie poziomu interfejsu API. W tym celu w pluginie Gradle na Androida możesz utworzyć wiele grup wersji produktu jako wymiarów wersji.

Podczas kompilowania aplikacji Gradle łączy konfigurację wersji produktu z każdej zdefiniowanej przez Ciebie wersji oraz konfigurację typu kompilacji, aby utworzyć ostateczną wersję kompilacji. Gradle nie łączy wariantów produktów należących do tego samego wymiaru.

Ten przykładowy kod używa właściwości flavorDimensions do utworzenia wymiaru „tryb” w celu pogrupowania wersji produktu „pełna” i „demo” oraz wymiaru „api” w celu pogrupowania konfiguracji wersji produktu na podstawie poziomu interfejsu API:

Kotlin

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

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their 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, with the first dimension having a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdk = 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.
      versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdk = 23
      versionCode = 20000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdk = 21
      versionCode = 10000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Groovy

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

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their 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, with the first dimension having 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.

      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"
      ...
    }
  }
}
...

Liczba wariantów kompilacji utworzonych przez Gradle jest równa iloczynowi liczby wersji w każdym wymiarze wersji oraz liczby typów kompilacji skonfigurowanych przez Ciebie. Gdy Gradle nazywa każdą wersję kompilacji lub odpowiednie artefakty, najpierw pojawiają się wersje produktu należące do wymiaru o wyższym priorytecie, a potem te z wymiarów o niższym priorytecie, a na końcu typ kompilacji.

Na przykład na podstawie poprzedniej konfiguracji kompilacji Gradle tworzy łącznie 12 wariantów kompilacji za pomocą tego schematu nazewnictwa:

  • Wariant kompilacji: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • Powiązany plik APK: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • Na przykład
    Wariant kompilacji: minApi24DemoDebug
    Odpowiadający plik APK: app-minApi24-demo-debug.apk

Oprócz katalogów zbiorów źródeł, które możesz utworzyć dla poszczególnych wersji produktu i wersji kompilacji, możesz też utworzyć katalogi zbiorów źródeł dla każdej kombinacji wersji produktu. Możesz na przykład utworzyć i dodać źródła kodu Java do katalogu src/demoMinApi24/java/, a Gradle używa tych źródeł tylko podczas kompilowania wariantu, który łączy te 2 wersje produktu.

Zestawy źródeł utworzone dla kombinacji wersji produktu mają wyższy priorytet niż zestawy źródeł należące do poszczególnych wersji produktu. Więcej informacji o zbiorach źródeł i o tym, jak Gradle scala zasoby, znajdziesz w sekcji Tworzenie zbiorów źródeł.

Filtrowanie wariantów

Gradle tworzy wariant kompilacji dla każdej możliwej kombinacji skonfigurowanych typów usług i typów kompilacji. Mogą jednak występować pewne warianty kompilacji, których nie potrzebujesz lub które nie mają sensu w kontekście projektu. Aby usunąć określone konfiguracje wariantów kompilacji, utwórz filtr wariantów w pliku build.gradle.kts na poziomie modułu.

Korzystając z konfiguracji kompilacji z poprzedniego rozdziału, załóżmy, że planujesz obsługiwać tylko poziomy interfejsu API 23 i nowsze w przypadku wersji demonstracyjnej aplikacji. Możesz użyć bloku variantFilter, aby odfiltrować wszystkie konfiguracje wariantów kompilacji, które łączą wersje produktu „minApi21” i „demo”:

Kotlin

android {
  ...
  buildTypes {...}

  flavorDimensions += listOf("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.enable = false
        }
    }
}
...

Groovy

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)
      }
  }
}
...

Gdy dodasz do konfiguracji kompilacji filtr wariantu i klikniesz Synchronizuj na pasku powiadomień, Gradle zignoruje wszystkie warianty kompilacji, które spełniają określone przez Ciebie warunki. Wersje kompilacji nie są już widoczne w menu, gdy na pasku menu klikasz Kompiluj > Wybierz wersję kompilacji lub Wersje kompilacji na pasku narzędzi.

Tworzenie zbiorów źródłowych

Domyślnie Android Studio tworzy main/ zbiór źródeł i katalogi dla wszystkich elementów, które chcesz udostępniać we wszystkich wariantach kompilacji. Możesz jednak tworzyć nowe zbiory źródłowe, aby kontrolować, które pliki Gradle skompiluje i pakiety pod kątem określonych typów kompilacji, rodzajów usług, kombinacji smaków produktów (w przypadku wymiarów rodzajów) i wariantów kompilacji.

Możesz na przykład zdefiniować podstawowe funkcje w zestawie main/ źródeł i użyć zestawów źródeł wersji produktu, aby zmienić branding aplikacji dla różnych klientów, lub uwzględnić specjalne uprawnienia i funkcję rejestrowania tylko w przypadku wariantów wersji, które korzystają z typu wersji debugowania.

Gradle oczekuje, że pliki i katalogi zbioru źródłowego będą uporządkowane w określony sposób, podobnie jak w zbiorze źródłowym main/. Na przykład Gradle oczekuje, że pliki klasy Kotlin lub Java związane z typem kompilacji „debug” będą znajdować się w katalogach src/debug/kotlin/ lub src/debug/java/.

Wtyczka Androida do obsługi Gradle udostępnia przydatne zadanie Gradle, które pokazuje, jak porządkować pliki pod kątem poszczególnych typów kompilacji, wersji produktu i wariantów kompilacji. Na przykład ten przykład z wyjścia zadania opisuje, gdzie Gradle ma znaleźć określone pliki dla typu kompilacji „debug”:

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

...

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

Aby wyświetlić dane wyjściowe, wykonaj te czynności:

  1. Na pasku narzędzi kliknij Gradle.
  2. Wybierz MyApplication > Tasks > android i kliknij dwukrotnie sourceSets.

    Aby zobaczyć folder Tasks, musisz pozwolić Gradle na utworzenie listy zadań podczas synchronizacji. Aby to zrobić:

    1. Kliknij Plik > Ustawienia > Eksperymentalne (Android Studio > Ustawienia > Eksperymentalny w systemie macOS).
    2. Odznacz opcję Nie twórz listy zadań Gradle podczas synchronizacji Gradle.
  3. Po wykonaniu zadania Gradle otworzy się okno Uruchom z wynikiem.

Uwaga: w danych wyjściowych zadania znajdziesz też informacje o tym, jak uporządkować zestawy źródłowe dla plików, których chcesz użyć do testowania aplikacji, np. testowe zestawy źródeł test/ i androidTest/.

Podczas tworzenia nowego wariantu kompilacji Android Studio nie tworzy katalogów zestawu źródłowego, ale udostępnia kilka opcji, które mogą Ci pomóc. Na przykład, aby utworzyć tylko katalog java/ dla typu kompilacji „debug”:

  1. Otwórz panel Projekt i w menu u góry panelu wybierz widok Projekt.
  2. Wejdź na MyProject/app/src/.
  3. Kliknij prawym przyciskiem myszy katalog src i wybierz Nowy > Katalog.
  4. W menu Zestawy źródeł Gradle wybierz full/java.
  5. Naciśnij Enter.

Android Studio tworzy katalog zestawu źródłowego dla typu kompilacji debugowania, a potem tworzy w nim katalog java/. Android Studio może też utworzyć katalogi za Ciebie, gdy dodasz do projektu nowy plik na potrzeby określonego wariantu kompilacji.

Aby na przykład utworzyć plik XML z wartościami dla typu kompilacji „debugowanie”:

  1. W panelu Projekt kliknij prawym przyciskiem myszy katalog src i wybierz Nowy > XML > Plik XML z wartościami.
  2. Wpisz nazwę pliku XML lub pozostaw nazwę domyślną.
  3. W menu obok pozycji Docelowy zestaw źródeł wybierz debuguj.
  4. Kliknij Zakończ.

Ponieważ jako docelowy zestaw źródeł został określony typ kompilacji „debug”, Android Studio automatycznie utworzy odpowiednie katalogi podczas tworzenia pliku XML. Powstała struktura katalogów wygląda jak na ilustracji 1.

Rysunek 1. Nowe katalogi zbiorów źródłowych dla kompilacji „debug”.

Aktywna grupa źródeł ma na ikonie zielony wskaźnik, który informuje o jej aktywności. Zbiór źródeł debug ma przypisany sufiks [main], aby wskazać, że zostanie scalony z zbiorem źródeł main.

Za pomocą tej samej procedury możesz też tworzyć katalogi zbiorów źródłowych dla wersji produktu, takich jak src/demo/, i wariantów kompilacji, takich jak src/demoDebug/. Możesz też tworzyć zestawy źródeł testów, które będą kierować na konkretne warianty kompilacji, takie jak src/androidTestDemoDebug/. Więcej informacji znajdziesz w artykule o testowaniu zbiorów źródłowych.

Zmień konfiguracje domyślnego zestawu źródeł

Jeśli masz źródła, które nie są uporządkowane w domyślnej strukturze pliku zbioru źródeł, której oczekuje Gradle (jak opisano w poprzedniej sekcji dotyczącej tworzenia zbiorów źródeł), możesz użyć bloku sourceSets, aby zmienić miejsce, w którym Gradle będzie szukać plików poszczególnych komponentów zbioru źródeł.

Blok sourceSets musi znajdować się w bloku android. Nie musisz przenosić plików źródłowych. Wystarczy, że podasz Gradle ścieżki względne do pliku build.gradle.kts na poziomie modułu, w którym Gradle może znaleźć pliki poszczególnych komponentów zestawu źródeł. Aby dowiedzieć się, które komponenty możesz konfigurować i czy możesz je mapować na wiele plików lub katalogów, zapoznaj się z dokumentacją interfejsu API wtyczki Gradle na Androida.

Poniższy przykładowy kod mapuje źródła z katalogu app/other/ na określone komponenty zbioru źródłowego main i zmienia katalog główny zbioru źródłowego androidTest:

Kotlin

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

    // If you list multiple directories, Gradle uses all of them to collect
    // sources. Because Gradle gives these directories equal priority, if
    // you define the same resource in more than one directory, you receive an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs(listOf("other/res1", "other/res2"))

    // Note: Avoid specifying a directory that is a parent to one
    // or more other directories you specify. For example, avoid the following:
    // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
    // Specify either only the root 'other/res1' directory or only the
    // nested 'other/res1/layouts' and 'other/res1/strings' directories.

    // For each source set, you can specify only one Android manifest.
    // By default, Android Studio creates a manifest for your main source
    // set in the src/main/ directory.
    manifest.srcFile("other/AndroidManifest.xml")
    ...
  }

  // Create additional blocks to configure other source sets.
  sourceSets.getByName("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")
      ...
  }
}
...

Groovy

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']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you receive an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: Avoid specifying a directory that is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // Specify either only the root 'other/res1' directory or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      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'
      ...
    }
  }
}
...

Pamiętaj, że katalog źródeł może należeć tylko do jednego zbioru źródeł. Nie możesz na przykład udostępniać tych samych źródeł testowych w przypadku zestawów testandroidTest. Dzieje się tak, ponieważ Android Studio tworzy osobne moduły IntelliJ dla każdego zestawu źródeł i nie może obsługiwać zduplikowanych katalogów źródeł w różnych zestawach źródeł.

Kompilowanie za pomocą zbiorów źródeł

Możesz użyć katalogów zbioru źródłowego, w którym znajdziesz kod i zasoby, które chcesz spakować tylko z określonymi konfiguracjami. Jeśli na przykład tworzysz wariant kompilacji „demoDebug”, który jest krzyżowaniem wersji produktu „demo” i typu kompilacji „debug”, Gradle sprawdza te katalogi i przypisuje im te priorytety:

  1. src/demoDebug/ (zbiór źródłowy wariantu kompilacji)
  2. src/debug/ (zbiór źródeł typu kompilacji)
  3. src/demo/ (zestaw źródeł smaku produktu)
  4. src/main/ (główny zbiór źródeł)

Zestawy źródeł utworzone dla kombinacji smaków produktu muszą zawierać wszystkie wymiary smaków. Na przykład zbiór źródeł wersji kompilacji musi być kombinacją typu kompilacji i wszystkich wymiarów rodzaju. Scalanie kodu i zasobów obejmujących foldery, które obejmują wiele wymiarów typów, nie jest obsługiwane.

Jeśli połączysz kilka smaków produktu, priorytet każdego z nich będzie określany na podstawie wymiaru smaku, do którego należą. Gdy podajesz wymiary smaku za pomocą właściwości android.flavorDimensions, smaki produktów należące do pierwszego wymiaru smaku mają wyższy priorytet niż te należące do drugiego wymiaru smaku itd. Ponadto zestawy źródeł utworzone dla kombinacji wersji produktu mają wyższy priorytet niż zestawy źródeł należące do poszczególnych wersji produktu.

Kolejność priorytetów określa, który zestaw źródłowy ma wyższy priorytet, gdy Gradle łączy kod i zasoby. Katalog zbioru źródłowego demoDebug/ prawdopodobnie zawiera pliki, które są specyficzne dla danego wariantu kompilacji, więc jeśli demoDebug/ zawiera plik, który jest również zdefiniowany w debug/, Gradle używa pliku ze zbioru źródłowego demoDebug/. Podobnie Gradle przypisuje plikom w typie kompilacji i wersji produktu źródłowych zbiorów wyższy priorytet niż tym samym plikom w pliku main/. Gradle stosuje te reguły kompilacji w kolejności priorytetów:

  • Cały kod źródłowy w katalogach kotlin/ lub java/ jest kompilowany, aby wygenerować pojedynczy wynik.

    Uwaga: w przypadku danego wariantu kompilacji Gradle zgłasza błąd kompilacji, gdy natrafi na co najmniej 2 katalogi zbioru źródłowego, które mają zdefiniowaną tę samą klasę Kotlin lub Java. Na przykład podczas kompilowania aplikacji debugowania nie możesz zdefiniować ani src/debug/Utility.kt, ani src/main/Utility.kt, ponieważ Gradle sprawdza oba te katalogi podczas procesu kompilacji i wyrzuca błąd „duplikat klasy”. Jeśli chcesz używać różnych wersji pliku Utility.kt w przypadku różnych typów kompilacji, każdy typ kompilacji musi mieć własną wersję pliku i nie może być uwzględniony w zbiorze źródeł main/.

  • Pliki manifestu są scalone w jeden plik manifestu. Priorytet jest nadawany w tej samej kolejności co lista w poprzednim przykładzie. Oznacza to, że ustawienia pliku manifestu dla danego typu kompilacji zastępują ustawienia pliku manifestu dla określonego rodzaju produktów i tak dalej. Więcej informacji znajdziesz w artykule o łączeniu plików manifestu.
  • Pliki w katalogach values/ są scalane ze sobą. Jeśli 2 pliki mają tę samą nazwę, np. 2 pliki strings.xml, priorytet jest przyznawany w takiej samej kolejności jak lista w poprzednim przykładzie. Oznacza to, że wartości zdefiniowane w pliku w zbiorze źródłowym typu kompilacji zastępują wartości zdefiniowane w tym samym pliku w rodzaju produktu itd.
  • Zasoby w katalogach res/asset/ są pakowane razem. Jeśli w co najmniej 2 zbiorach źródłowych istnieją zasoby o tej samej nazwie, priorytet jest przyznawany w takiej samej kolejności jak lista w poprzednim przykładzie.
  • Gradle nadaje aplikacjom i plikom manifestu dołączonym do zależności modułu biblioteki najniższy priorytet podczas tworzenia aplikacji.

Deklarowanie zależności

Aby skonfigurować zależność dla konkretnej wersji lub zestawu źródeł testów, przed słowem kluczowym Implementation umieść nazwę wersji lub zestawu źródeł testów, jak w tym przykładzie:

Kotlin

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    "freeImplementation"(project(":mylibrary"))

    // 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")
}

Groovy

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation project(":mylibrary")

    // 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'
}

Więcej informacji o konfigurowaniu zależności znajdziesz w artykule Dodawanie zależności kompilacji.

Zarządzanie zależnościami z uwzględnieniem wariantów

Wtyczka Androida do obsługi Gradle w wersji 3.0.0 lub nowszej zawiera nowy mechanizm zależności, który automatycznie dopasowuje warianty podczas korzystania z biblioteki. Oznacza to, że wariant debug aplikacji automatycznie korzysta z wariantu debug biblioteki itd. Działa to też w przypadku wersji: wersja freeDebug aplikacji będzie używać wersji freeDebug biblioteki.

Aby wtyczka mogła dokładnie dopasowywać warianty, musisz podać pasujące wartości zastępcze zgodnie z opisem w następnym rozdziale w przypadku sytuacji, gdy dopasowanie bezpośrednie jest niemożliwe.

Załóżmy na przykład, że Twoja aplikacja konfiguruje typ kompilacji o nazwie „staging”, ale jedna z jej zależności biblioteki tego nie robi. Gdy wtyczka spróbuje utworzyć wersję „testową” aplikacji, nie będzie wiedzieć, której wersji biblioteki użyć, i wyświetli się komunikat o błędzie podobny do tego:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

Rozwiązywanie błędów kompilacji związanych z dopasowywaniem wariantów

Wtyczka zawiera elementy DSL, które pomagają kontrolować sposób, w jaki Gradle rozwiązuje sytuacje, gdy bezpośrednie dopasowanie wariantu aplikacji do zależności nie jest możliwe.

Poniżej znajdziesz listę problemów związanych z dopasowywaniem zależności uwzględniających warianty oraz sposoby ich rozwiązywania przy użyciu właściwości DSL:

  • Twoja aplikacja zawiera typ kompilacji, którego nie ma w bibliotece zależnej.

    Na przykład aplikacja zawiera typ kompilacji „testowej”, ale zależność obejmuje tylko typy kompilacji „debug” i „wydana”.

    Pamiętaj, że nie ma problemu, gdy zależność biblioteki zawiera typ kompilacji, którego nie ma w Twojej aplikacji. Dzieje się tak, ponieważ wtyczka nigdy nie prosi o ten typ kompilacji w zależności.

    Użyj matchingFallbacks, aby określić alternatywne dopasowania dla danego typu kompilacji, jak pokazano tutaj:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        buildTypes {
            getByName("debug") {}
            getByName("release") {}
            create("staging") {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks += listOf("debug", "qa", "release")
            }
        }
    }

    Odlotowe

    // In the app's build.gradle file.
    android {
        buildTypes {
            debug {}
            release {}
            staging {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks = ['debug', 'qa', 'release']
            }
        }
    }
  • W przypadku danego wymiaru rodzaju, który istnieje zarówno w zależności od aplikacji, jak i jej biblioteki, aplikacja zawiera smaki, których nie ma biblioteka.

    Zarówno aplikacja, jak i jej zależności od biblioteki zawierają wymiar „poziom” (ang. „tier”). Wymiar „poziom” w aplikacji obejmuje jednak wersje „bezpłatna” i „płatna”, ale zależność obejmuje tylko wersje „demo” i „płatna” tego samego wymiaru.

    Pamiętaj, że w przypadku danej odmiany, która występuje zarówno w aplikacji, jak i w jej zależnościach od biblioteki, nie ma problemu, gdy biblioteka zawiera odmianę produktu, której nie ma w aplikacji. Dzieje się tak, ponieważ wtyczka nigdy nie prosi o tę wersję w zależnej.

    Użyj atrybutu matchingFallbacks, aby określić alternatywne odpowiedniki dla „bezpłatnego” rodzaju produktów, jak tutaj:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions += "tier"
        productFlavors {
            create("paid") {
                dimension = "tier"
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            create("free") {
                dimension = "tier"
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks += listOf("demo", "trial")
            }
        }
    }

    Odlotowe

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions 'tier'
        productFlavors {
            paid {
                dimension 'tier'
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            free {
                dimension 'tier'
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks = ['demo', 'trial']
            }
        }
    }
  • Zależność biblioteki zawiera wymiar spersonalizowany, którego nie ma Twoja aplikacja.

    Na przykład zależność biblioteki zawiera wersje dla wymiaru „minApi”, ale Twoja aplikacja zawiera wersje tylko dla wymiaru „poziom”. Jeśli chcesz utworzyć wersję aplikacji „freeDebug”, wtyczka nie wie, czy użyć wersji „minApi23Debug” czy „minApi18Debug”.

    Pamiętaj, że nie ma problemu, gdy Twoja aplikacja zawiera wymiar smaku, którego nie ma w bibliotece zależnej. Wynika to z tego, że wtyczka dopasowuje wersje tylko tych wymiarów, które występują w zależności. Jeśli na przykład zależność nie zawiera wymiaru dla ABI, wersja „freeX86Debug” aplikacji będzie używać wersji „freeDebug” tej zależności.

    Użyj w bloku defaultConfig wartości missingDimensionStrategy, aby określić domyślny wariant, z którego ma korzystać wtyczka do wyboru każdego brakującego wymiaru, jak pokazano w tym przykładzie. Możesz też zastąpić swoje wybory w bloku productFlavors, aby każdy wariant mógł określić inną strategię dopasowywania w przypadku brakującego wymiaru.

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy("minApi", "minApi18", "minApi23")
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy("abi", "x86", "arm64")
        }
        flavorDimensions += "tier"
        productFlavors {
            create("free") {
                dimension = "tier"
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the "minApi" dimension.
                missingDimensionStrategy("minApi", "minApi23", "minApi18")
            }
            create("paid") {}
        }
    }

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy 'abi', 'x86', 'arm64'
        }
        flavorDimensions 'tier'
        productFlavors {
            free {
                dimension 'tier'
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the 'minApi' dimension.
                missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
            }
            paid {}
        }
    }

Więcej informacji znajdziesz w sekcji matchingFallbacksmissingDimensionStrategy w pliku referencyjnym wtyczki DSL dla Androida.

Skonfiguruj ustawienia podpisywania

Gradle nie podpisuje pliku APK ani pakietu AAB Twojej wersji, chyba że wyraźnie zdefiniujesz konfigurację podpisywania tej kompilacji. Jeśli nie masz jeszcze klucza podpisywania, wygeneruj klucz przesyłania i sklep kluczy za pomocą Androida Studio.

Aby ręcznie skonfigurować konfiguracje podpisywania typu kompilacji wersji za pomocą konfiguracji kompilacji Gradle:

  1. Utwórz magazyn kluczy. Magazyn kluczy to plik binarny zawierający zestaw kluczy prywatnych. Magazyn kluczy musisz przechowywać w bezpiecznym miejscu.
  2. Utwórz klucz prywatny. Klucz prywatny służy do podpisywania aplikacji na potrzeby dystrybucji. Nie jest nigdy dołączany do aplikacji ani ujawniany nieupoważnionym osobom trzecim.
  3. Dodaj konfigurację podpisywania do pliku build.gradle.kts na poziomie modułu:

    Kotlin

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

    Groovy

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

Uwaga: umieszczanie haseł do klucza wersji i magazynu kluczy w pliku kompilacji nie jest dobrą praktyką w zakresie bezpieczeństwa. Zamiast tego skonfiguruj plik kompilacji tak, aby pobierał te hasła ze zmiennych środowiskowych, lub poproś proces kompilacji o ich podanie.

Aby uzyskać te hasła z zmiennych środowiskowych:

Kotlin

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

Odlotowe

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

Możesz też załadować magazyn kluczy z lokalnego pliku właściwości. Ze względów bezpieczeństwa nie dodawaj tego pliku do kontroli wersji. Zamiast tego skonfiguruj go lokalnie dla każdego dewelopera. Więcej informacji znajdziesz w artykule Usuwanie informacji o podpisywaniu z plików kompilacji.

Po zakończeniu tego procesu możesz rozpowszechniać aplikację i opublikować ją w Google Play.

Ostrzeżenie: przechowuj magazyn kluczy i klucz prywatny w bezpiecznym miejscu i zapewnij sobie dostęp do ich bezpiecznych kopii zapasowych. Jeśli korzystasz z podpisywania aplikacji przez Google Play i stracisz klucz przesyłania, w Konsoli Play możesz poprosić o zresetowanie urządzenia. Jeśli publikujesz aplikację bez usługi podpisywania aplikacji Google Play (w przypadku aplikacji utworzonych przed sierpniem 2021 r.) i utracisz klucz podpisywania aplikacji, nie będziesz mieć możliwości publikowania jej aktualizacji, ponieważ wszystkie wersje aplikacji musisz podpisywać tym samym kluczem.

Podpisywanie aplikacji na Wear OS

Gdy publikujesz aplikacje na Wear OS, zarówno plik APK na zegarek, jak i opcjonalny plik APK na telefon muszą być podpisane tym samym kluczem. Więcej informacji o pakowaniu i podpisywaniu aplikacji na Wear OS znajdziesz w artykule Pakowanie i dystrybucja aplikacji na Wear OS.