Skonfiguruj warianty kompilacji

Z tej strony dowiesz się, jak skonfigurować warianty kompilacji, aby tworzyć różne wersje aplikacji z jednego projektu, oraz jak prawidłowo zarządzać zależnościami i konfiguracjami podpisywania.

Każdy wariant kompilacji to inna wersja aplikacji, którą możesz skompilować. Możesz na przykład utworzyć jedną wersję aplikacji, która jest bezpłatna i zawiera ograniczony zestaw treści, oraz drugą, płatną, która zawiera więcej treści. Możesz też tworzyć różne wersje aplikacji przeznaczone na różne urządzenia na podstawie poziomu interfejsu API lub innych różnic między urządzeniami.

Warianty kompilacji to wynik zastosowania 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, możesz skonfigurować typy kompilacji i wersje produktu, które je tworzą.

Na przykład wersja produktu „demo” może określać pewne funkcje i wymagania dotyczące urządzenia, takie jak niestandardowy kod źródłowy, zasoby i minimalne poziomy interfejsu API, a typ kompilacji „debug” stosuje inne ustawienia kompilacji i pakowania, takie jak opcje debugowania i klucze podpisywania. Wariant kompilacji, który łączy te 2 elementy, to wersja „demoDebug” aplikacji. Zawiera ona kombinację konfiguracji i zasobów z wersji produktu „demo”, typu kompilacji „debug” i zestawu źródeł main/.

Konfigurowanie typów kompilacji

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

Jeśli chcesz dodać lub zmienić określone ustawienia, możesz dodać do konfiguracji typ kompilacji debugowania. Poniższy przykład określa applicationIdSuffix dla typu kompilacji debugowania i konfiguruje typ kompilacji „staging”, który jest inicjowany przy użyciu ustawień typu kompilacji debugowania:

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 wprowadzisz zmiany w pliku konfiguracyjnym 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 Messages z opisem problemu.

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

Konfigurowanie wersji produktu

Tworzenie wersji produktu jest podobne do tworzenia typów kompilacji. Dodaj wersje produktu do bloku productFlavors w konfiguracji kompilacji i uwzględnij wybrane ustawienia. Wersje produktu obsługują te same właściwości co defaultConfig, ponieważ defaultConfig należy do klasy ProductFlavor. Oznacza to, że możesz podać podstawową konfigurację wszystkich wersji w bloku defaultConfig, a każda wersja może zmienić dowolną z tych wartości domyślnych, np. applicationId. Więcej informacji o identyfikatorze aplikacji znajdziesz w artykule Ustawianie identyfikatora aplikacji.

Uwaga: nadal musisz podać nazwę pakietu za pomocą atrybutu package w pliku manifestu main/. Musisz też użyć tej nazwy pakietu w kodzie źródłowym, aby odwoływać się do klasy R lub rozwiązywać problemy z rejestracją względnej aktywności lub usługi. Dzięki temu możesz użyć applicationId, aby nadać każdemu wariantowi 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, czyli grupy smaków produktów. Musisz przypisać wszystkie wersje do wymiaru wersji, w przeciwnym razie pojawi się ten 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 jeden wymiar wersji, wtyczka Androida do Gradle automatycznie przypisuje wszystkie wersje modułu do tego wymiaru.

Poniższy przykładowy kod tworzy wymiar wersji o nazwie „version” i dodaje wersje produktu „demo” i „full”. Te wersje zapewniają własne applicationIdSuffixversionNameSuffix:

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 roku), którą rozpowszechniasz w Google Play za pomocą plików APK, aby rozpowszechniać ją w Google Play z obsługą wielu plików APK, przypisz tę samą wartość applicationId do wszystkich wersji i nadaj każdej z nich inną wartość versionCode. Aby rozpowszechniać różne warianty aplikacji jako osobne aplikacje w Google Play, musisz przypisać do każdego wariantu inny applicationId.

Po utworzeniu i skonfigurowaniu wersji produktów kliknij Synchronizuj teraz 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 zasadą <product-flavor><Build-Type>. Jeśli na przykład utworzysz wersje produktu „demo” i „full” oraz zachowasz domyślne typy kompilacji „debug” i „release”, Gradle utworzy te warianty kompilacji:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Aby wybrać wariant kompilacji do skompilowania i uruchomienia, kliknij Build (Kompilacja) > Select Build Variant (Wybierz wariant kompilacji) i wybierz wariant kompilacji z menu. Aby rozpocząć dostosowywanie każdej wersji kompilacji za pomocą własnych funkcji i zasobów, musisz utworzyć zestawy źródeł i nimi zarządzać, jak opisano na tej stronie.

Zmiana identyfikatora aplikacji dla wariantów kompilacji

Gdy tworzysz plik APK lub AAB aplikacji, 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ą wyświetlane jako osobne strony w Google Play, np. wersję „bezpłatną” 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ę jako osobny smak produktu. W przypadku każdego wariantu w bloku productFlavors możesz ponownie zdefiniować właściwość applicationId lub zamiast tego dołączyć segment do domyślnego identyfikatora aplikacji za pomocą applicationIdSuffix, jak pokazano poniżej:

Kotlin

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

Groovy

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

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

Możesz też użyć symbolu applicationIdSuffix, aby dołączyć segment na podstawie typu kompilacji, jak pokazano tutaj:

Kotlin

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

Groovy

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

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

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 karty aplikacji do rozpowszechniania wielu plików APK, z których każdy jest przeznaczony na inną konfigurację urządzenia, np. poziom interfejsu API, musisz użyć tego samego identyfikatora aplikacji dla każdego wariantu kompilacji, ale nadać każdemu plikowi APK inny versionCode. Więcej informacji znajdziesz w artykule Obsługa wielu plików APK. Publikowanie za pomocą pakietów AAB nie ulega zmianie, ponieważ domyślnie używa jednego artefaktu z jednym kodem wersji i identyfikatorem aplikacji.

Wskazówka: jeśli musisz odwołać się do identyfikatora aplikacji w pliku manifestu, możesz użyć symbolu zastępczego ${applicationId} w dowolnym atrybucie 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 z wymiarami smaku

W niektórych przypadkach możesz chcieć połączyć konfiguracje z kilku wersji produktu. Możesz na przykład utworzyć różne konfiguracje dla wersji produktu „pełna” i „demo” na podstawie poziomu interfejsu API. W tym celu wtyczka Androida do Gradle umożliwia tworzenie wielu grup wersji produktu jako wymiarów wersji.

Podczas tworzenia aplikacji Gradle łączy konfigurację wersji produktu z każdego zdefiniowanego przez Ciebie wymiaru wersji z konfiguracją typu kompilacji, aby utworzyć ostateczną odmianę kompilacji. Gradle nie łączy wersji produktu należących do tego samego wymiaru wersji.

W tym przykładowym kodzie użyto właściwości flavorDimensions do utworzenia wymiaru „mode” (tryb), który grupuje wersje produktu „full” (pełna) i „demo” (wersja demonstracyjna), oraz wymiaru „api” (interfejs API), który grupuje konfiguracje 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 tworzonych przez Gradle jest równa iloczynowi liczby wersji w każdym wymiarze wersji i liczby skonfigurowanych typów kompilacji. Gdy Gradle nadaje nazwy poszczególnym wariantom kompilacji lub powiązanym artefaktom, najpierw pojawiają się wersje produktu należące do wymiaru wersji o wyższym priorytecie, a potem te z wymiarów o niższym priorytecie i na końcu typ kompilacji.

Na podstawie poprzedniej konfiguracji kompilacji Gradle tworzy łącznie 12 wariantów kompilacji o tym schemacie nazewnictwa:

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

Oprócz katalogów zestawów źródeł, które możesz utworzyć dla każdej wersji produktu i odmiany kompilacji, możesz też tworzyć katalogi zestawów źródeł dla każdej kombinacji wersji produktu. Możesz na przykład utworzyć i dodać źródła Java do katalogu src/demoMinApi24/java/, a Gradle będzie ich używać tylko podczas tworzenia wariantu, który łączy te 2 wersje produktu.

Utworzone przez Ciebie zestawy źródeł dla kombinacji smaków produktów mają wyższy priorytet niż zestawy źródeł należące do poszczególnych smaków produktów. Więcej informacji o zbiorach źródeł i sposobie scalania zasobów przez Gradle znajdziesz w sekcji dotyczącej tworzenia zbiorów źródeł.

Filtrowanie wariantów

Gradle tworzy wariant kompilacji dla każdej możliwej kombinacji wersji produktu i skonfigurowanych typów kompilacji. Mogą jednak istnieć pewne warianty kompilacji, które nie są potrzebne lub nie mają sensu w kontekście Twojego projektu. Aby usunąć niektóre konfiguracje wariantów kompilacji, utwórz filtr wariantów w pliku build.gradle.kts na poziomie modułu.

Na przykładzie konfiguracji kompilacji z poprzedniej sekcji załóżmy, że w wersji demonstracyjnej aplikacji planujesz obsługiwać tylko poziomy interfejsu API 23 i wyższe. 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 filtr wariantu do konfiguracji kompilacji i na pasku powiadomień klikniesz Synchronizuj teraz, Gradle zignoruje wszystkie warianty kompilacji, które spełniają określone przez Ciebie warunki. Warianty kompilacji nie będą już wyświetlane w menu, gdy na pasku menu klikniesz Build > Select Build Variant lub na pasku okna narzędziowego klikniesz Build Variants .

Tworzenie zbiorów źródeł

Domyślnie Android Studio tworzy main/ zestaw źródeł i katalogi dla wszystkich elementów, które chcesz udostępniać między wszystkimi wariantami kompilacji. Możesz jednak tworzyć nowe zestawy źródeł, aby dokładnie określać, które pliki Gradle ma kompilować i pakować w przypadku określonych typów kompilacji, wersji produktu, kombinacji wersji produktu (gdy używasz wymiarów wersji) i wariantów kompilacji.

Możesz na przykład zdefiniować podstawową funkcjonalność w main/zestawie ź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 funkcje rejestrowania tylko w przypadku wariantów kompilacji, które używają typu kompilacji debugowania.

Gradle oczekuje, że pliki i katalogi zestawu źródeł będą uporządkowane w określony sposób, podobnie jak main/zestaw źródeł. Na przykład Gradle oczekuje, że pliki klas Kotlin lub Java, które są specyficzne dla typu 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 organizować pliki dla każdego typu kompilacji, wersji produktu i wariantów kompilacji. Na przykład poniższy fragment danych wyjściowych zadania opisuje, gdzie Gradle oczekuje znalezienia określonych plików w przypadku 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ć te dane wyjściowe, wykonaj te czynności:

  1. Na pasku okna narzędzi kliknij Gradle.
  2. Otwórz MyApplication > Tasks > android i dwukrotnie kliknij sourceSets.

    Aby wyświetlić folder Tasks (Zadania), musisz zezwolić Gradle na utworzenie listy zadań podczas synchronizacji. Aby to zrobić:

    1. Kliknij Plik > Ustawienia > Eksperymentalne (Android Studio > Ustawienia > Eksperymentalne w systemie macOS).
    2. Odznacz opcję Nie twórz listy zadań Gradle podczas synchronizacji Gradle.
  3. Po wykonaniu zadania przez Gradle otworzy się okno Uruchom, w którym będą wyświetlane dane wyjściowe.

Uwaga: w wyniku zadania zobaczysz też, jak uporządkować zbiory źródeł plików, których chcesz użyć do przeprowadzenia testów aplikacji, np. test/androidTest/ zbiory źródeł testowych.

Gdy tworzysz nowy wariant kompilacji, Android Studio nie tworzy za Ciebie katalogów zestawów źródeł, 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 w sekcji Gradle Source Sets (Zestawy źródeł Gradle) wybierz full/java.
  5. Naciśnij Enter.

Android Studio tworzy katalog zestawu źródeł dla typu kompilacji debugowania, a następnie tworzy w nim katalog java/. Możesz też dodać nowy plik do projektu dla określonego wariantu kompilacji, a Android Studio utworzy za Ciebie katalogi.

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

  1. W panelu Project (Projekt) kliknij prawym przyciskiem myszy katalog src i wybierz New (Nowy) > XML > Values XML File (Plik XML wartości).
  2. Wpisz nazwę pliku XML lub pozostaw nazwę domyślną.
  3. W menu obok opcji Target Source Set (Docelowy zestaw źródeł) wybierz debug (debugowanie).
  4. Kliknij Zakończ.

Ponieważ typ kompilacji „debug” został określony jako docelowy zestaw źródeł, Android Studio automatycznie tworzy niezbędne katalogi podczas tworzenia pliku XML. Wynikowa struktura katalogów wygląda jak na rysunku 1.

Rysunek 1. Nowe katalogi zestawu źródeł dla typu kompilacji „debug”.

Aktywne zestawy źródeł mają na ikonie zielony wskaźnik, który informuje, że są aktywne. Zbiór źródeł debug ma sufiks [main], co oznacza, że zostanie on scalony ze zbiorem źródeł main.

W ten sam sposób możesz też tworzyć katalogi zestawów źródeł dla wersji produktów, np. src/demo/, i tworzyć warianty, np. src/demoDebug/. Możesz też tworzyć testowe zestawy źródeł, które są kierowane na konkretne warianty kompilacji, np. src/androidTestDemoDebug/. Więcej informacji znajdziesz w artykule o testowaniu zestawów źródeł.

Zmiana domyślnych konfiguracji zestawu źródeł

Jeśli masz źródła, które nie są zorganizowane w domyślny plik zestawu źródeł, którego oczekuje Gradle (zgodnie z opisem w poprzedniej sekcji dotyczącej tworzenia zestawów źródeł), możesz użyć bloku sourceSets, aby zmienić miejsce, w którym Gradle szuka plików dla każdego komponentu zestawu ź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órych Gradle może znaleźć pliki dla każdego komponentu zestawu źródeł. Aby dowiedzieć się, które komponenty możesz skonfigurować i czy możesz przypisać je do wielu plików lub katalogów, zapoznaj się z dokumentacją interfejsu API wtyczki Android Gradle.

Ten przykładowy kod mapuje źródła z katalogu app/other/ na określone komponenty zestawu źródeł main i zmienia katalog główny zestawu źródeł 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ódłowy może należeć tylko do jednego zbioru źródeł. Nie możesz na przykład udostępniać tych samych źródeł testowych obu zestawom źródeł testandroidTest. Dzieje się tak, ponieważ Android Studio tworzy osobne moduły IntelliJ dla każdego zbioru źródeł i nie obsługuje zduplikowanych katalogów głównych treści w zbiorach źródeł.

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

Możesz używać katalogów zestawów źródeł do przechowywania kodu i zasobów, które mają być pakowane tylko w przypadku określonych konfiguracji. Jeśli na przykład tworzysz wariant kompilacji „demoDebug”, który jest połączeniem wersji produktu „demo” i typu kompilacji „debug”, Gradle sprawdza te katalogi i nadaje im następujące priorytety:

  1. src/demoDebug/ (zestaw źródeł wariantu kompilacji)
  2. src/debug/ (zestaw źródeł typu kompilacji)
  3. src/demo/ (zestaw źródeł wariantów usługi)
  4. src/main/ (główny zestaw źródeł)

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

Jeśli połączysz kilka smaków produktu, priorytet między nimi będzie określany przez wymiar smaku, do którego należą. Podczas podawania wymiarów smaku za pomocą właściwości android.flavorDimensions smaki produktów należące do pierwszego wymienionego wymiaru smaku mają wyższy priorytet niż smaki należące do drugiego wymiaru smaku itd. Dodatkowo zestawy źródeł tworzone dla kombinacji smaków produktów mają wyższy priorytet niż zestawy źródeł należące do poszczególnych smaków produktów.

Kolejność priorytetów określa, który zestaw źródeł ma wyższy priorytet, gdy Gradle łączy kod i zasoby. Ponieważ katalog zestawu źródeł demoDebug/ zawiera prawdopodobnie pliki specyficzne dla tego wariantu kompilacji, jeśli demoDebug/ zawiera plik, który jest również zdefiniowany w debug/, Gradle używa pliku z zestawu źródeł demoDebug/. Podobnie Gradle nadaje plikom w zestawach źródeł typu kompilacji i wersji produktu wyższy priorytet niż tym samym plikom w main/. Gradle uwzględnia tę kolejność priorytetów podczas stosowania tych reguł kompilacji:

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

    Uwaga: w przypadku danej odmiany kompilacji Gradle zgłasza błąd kompilacji, jeśli napotka co najmniej 2 katalogi zestawu źródeł, w których zdefiniowano tę samą klasę Kotlin lub Java. Na przykład podczas tworzenia aplikacji do debugowania nie możesz zdefiniować jednocześnie katalogów src/debug/Utility.ktsrc/main/Utility.kt, ponieważ Gradle sprawdza oba te katalogi podczas procesu kompilacji i zgłasza błąd „duplicate class” (zduplikowana klasa). Jeśli chcesz mieć różne wersje Utility.kt dla różnych typów kompilacji, każdy typ kompilacji musi definiować własną wersję pliku i nie może zawierać jej w zestawie źródeł main/.

  • Manifesty są łączone w jeden manifest. Priorytet jest przyznawany w tej samej kolejności co na liście w poprzednim przykładzie. Oznacza to, że ustawienia pliku manifestu dla typu kompilacji zastępują ustawienia pliku manifestu dla wersji produktu itd. Więcej informacji znajdziesz w artykule o scalaniu plików manifestu.
  • Pliki w katalogach values/ są łączone. Jeśli 2 pliki mają taką samą nazwę, np. 2 pliki strings.xml, priorytet jest przyznawany w tej samej kolejności co na liście w poprzednim przykładzie. Oznacza to, że wartości zdefiniowane w pliku w zestawie źródeł typu kompilacji zastępują wartości zdefiniowane w tym samym pliku w wersji produktu itd.
  • Zasoby w katalogach res/asset/ są pakowane razem. Jeśli w co najmniej 2 zestawach źródeł zdefiniowano zasoby o tej samej nazwie, priorytet jest przyznawany w tej samej kolejności co na liście w poprzednim przykładzie.
  • Gradle nadaje zasobom 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 konkretnego wariantu kompilacji lub zestawu źródeł testowych, przed słowem kluczowym Implementation dodaj nazwę wariantu kompilacji lub zestawu źródeł testowych, jak pokazano 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.

Korzystanie z zarządzania 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 debugwariantdebug aplikacji automatycznie korzysta z debugwariantudebug biblioteki i tak dalej. Działa to też w przypadku wersji: wariant freeDebug aplikacji będzie korzystać z wersji freeDebug biblioteki.

Aby wtyczka mogła dokładnie dopasowywać warianty, musisz udostępnić pasujące rezerwowe wartości zgodnie z opisem w następnej sekcji w przypadkach, gdy bezpośrednie dopasowanie nie jest możliwe.

Załóżmy na przykład, że Twoja aplikacja konfiguruje typ kompilacji o nazwie „staging”, ale jedna z jej zależności bibliotecznych nie. Gdy wtyczka spróbuje utworzyć wersję „stagingową” aplikacji, nie będzie wiedzieć, której wersji biblioteki użyć, i wyświetli 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, w których bezpośrednie dopasowanie wariantu między aplikacją a zależnością nie jest możliwe.

Poniżej znajdziesz listę problemów związanych z dopasowywaniem zależności z uwzględnieniem wersji i sposoby ich rozwiązywania za pomocą właściwości DSL:

  • Twoja aplikacja zawiera typ kompilacji, którego nie ma w zależności biblioteki.

    Na przykład Twoja aplikacja zawiera typ kompilacji „staging”, ale zależność zawiera tylko typy kompilacji „debug” i „release”.

    Pamiętaj, że nie ma problemu, gdy zależność biblioteki zawiera typ kompilacji, którego Twoja aplikacja nie ma. Dzieje się tak, ponieważ wtyczka nigdy nie żąda tego typu kompilacji od zależności.

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

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

    Groovy

    // 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 wersji, który występuje zarówno w aplikacji, jak i w zależności biblioteki, aplikacja zawiera wersje, których nie ma biblioteka.

    Na przykład zarówno aplikacja, jak i jej zależności dotyczące bibliotek zawierają wymiar wersji „tier”. Wymiar „poziom” w aplikacji obejmuje jednak wersje „bezpłatna” i „płatna”, ale zależność obejmuje tylko wersje „demo” i „płatna” dla tego samego wymiaru.

    Pamiętaj, że w przypadku danego wymiaru wersji, który występuje zarówno w aplikacji, jak i w zależnościach biblioteki, nie ma problemu, gdy biblioteka zawiera wersję produktu, której nie ma w aplikacji. Dzieje się tak, ponieważ wtyczka nigdy nie żąda tej wersji od zależności.

    Użyj znaku matchingFallbacks, aby określić alternatywne dopasowania do wersji produktu „bezpłatnej” aplikacji, jak pokazano 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")
            }
        }
    }

    Groovy

    // 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 wersji, którego nie ma w aplikacji.

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

    Pamiętaj, że nie ma problemu, gdy aplikacja zawiera wymiar wersji, którego nie ma zależność biblioteki. Wynika to z faktu, ż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 interfejsów ABI, wersja „freeX86Debug” Twojej aplikacji będzie używać wersji „freeDebug” zależności.

    Użyj znaku missingDimensionStrategy w bloku defaultConfig, aby określić domyślną odmianę, którą wtyczka ma wybierać w przypadku każdego brakującego wymiaru, jak pokazano w poniższym 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 sekcjach matchingFallbacksmissingDimensionStrategy w dokumentacji DSL wtyczki Androida do obsługi Gradle.

Konfigurowanie ustawień podpisywania

Gradle nie podpisuje pliku APK ani pakietu AAB wersji produkcyjnej, chyba że jawnie zdefiniujesz konfigurację podpisywania dla tej wersji. Jeśli nie masz jeszcze klucza podpisywania, wygeneruj klucz przesyłania i magazyn kluczy w Androidzie Studio.

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

  1. Utwórz magazyn kluczy. Magazyn kluczy to plik binarny zawierający zestaw kluczy prywatnych. Musisz przechowywać magazyn kluczy w bezpiecznym miejscu.
  2. Utwórz klucz prywatny. Klucz prywatny służy do podpisywania aplikacji na potrzeby dystrybucji i nigdy nie jest dołączany do aplikacji ani udostępniany 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 dobrym rozwiązaniem z punktu widzenia bezpieczeństwa. Zamiast tego skonfiguruj plik kompilacji tak, aby pobierał te hasła ze zmiennych środowiskowych lub aby proces kompilacji wyświetlał prośbę o podanie tych haseł.

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

Kotlin

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

Groovy

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

Możesz też wczytać magazyn kluczy z lokalnego pliku właściwości. Ze względów bezpieczeństwa nie dodawaj tego pliku do kontroli źródła. 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 publikować ją w Google Play.

Ostrzeżenie: przechowuj magazyn kluczy i klucz prywatny w bezpiecznym miejscu i zadbaj o bezpieczne kopie zapasowe. Jeśli korzystasz z podpisywania aplikacji w Google Play i utracisz klucz przesyłania, możesz poprosić o jego zresetowanie w Konsoli Play. Jeśli publikujesz aplikację bez podpisywania aplikacji w Google Play (w przypadku aplikacji utworzonych przed sierpniem 2021 roku) i utracisz klucz podpisywania aplikacji, nie będziesz mieć możliwości publikowania aktualizacji aplikacji, ponieważ wszystkie jej wersje musisz podpisywać przy użyciu tego samego klucza.

Podpisywanie aplikacji na Wear OS

Podczas publikowania aplikacji na Wear OS zarówno plik APK zegarka, jak i opcjonalny plik APK telefonu 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.