Korzystanie z Build Flavours - Poprawna struktura folderów źródłowych i build.gradle


166

Uwaga: odpowiedź zredagowana po odpowiedzi Xaviera

Próbuję użyć różnych smaków kompilacji dla tego samego projektu aplikacji w Android Studio. Wydaje mi się jednak, że strasznie mi się nie udało konfigurować go do odpowiedniego działania.

Kroki:

  1. Utwórz nowy projekt Android Studio o nazwie „Test”.
  2. Otwórz plik build.gradle * i dodaj następujące wiersze:

    productFlavors {
    flavor1 {
        packageName 'com.android.studio.test.flavor1'
        }
    flavor2 {
        packageName 'com.android.studio.test.flavor2'
        }
    }
  3. Po ponownym uruchomieniu Android Studio widzę teraz 4 warianty kompilacji w sekcji Warianty kompilacji. Oznacza to, że do tej pory udało nam się stworzyć smaki produktów. **
  4. Utworzono nowy folder źródłowy dla flavour1 ; jednak nie jestem pewien, czy robię to we właściwy sposób. Oto jak to zrobiłem:

    • Pamiętaj, że nazwa mojego pakietu dla tego projektu to: com.foo.test
    • Kliknij srcfolder prawym przyciskiem myszy , w przypadku flavour1 faktycznie utworzyłem poszczególne foldery w eksploratorze, w sposób odpowiadający strukturze src/flavor1/java/com/foo/test/MainActivity.java.
    • Powyższe działało dobrze, ponieważ folder „java” jest zaznaczony na niebiesko , co oznacza, że ​​IDE zna swój aktywny katalog źródłowy. Pakiet został również utworzony automatycznie. Mimo to otrzymuję ostrzeżenie o znalezieniu zduplikowanej klasy. Zobacz zrzut ekranu tutaj.
    • W przypadku flavour2 próbowałem stworzyć pakiet ręcznie, ale folder „src” dla flavour2 nie wydaje się być niebieski, dlatego opcje są inne po kliknięciu prawym przyciskiem myszy, a „Nowy pakiet” nie jest dostępny do użycia. Zobacz zdjęcie tutaj.
    • Zauważ, że dla flavour1 utworzyłem również katalog „res”, który zmienia kolor na niebieski, ale mimo to nie oferuje możliwości tworzenia ani pliku zasobów systemu Android, ani katalogu zasobów Andorid, na wypadek, gdyby chciał użyć innego Resoruces dla różnych smaków.

czy robię coś źle? A może coś mi brakuje? Powiadom mnie, jeśli będziesz potrzebować więcej informacji.

* Mój projekt wydaje się mieć dwa pliki build.gradle. Jeden znajduje się w katalogu głównym folderu projektu (\ GradleTest), ten jest pusty. Drugi znajdujący się w katalogu głównym podfolderu \ GradleTest, również oznaczony jako „GradleTest” (GradleTest-GradleTest), to ten, który miał już kod po otwarciu; dlatego to jest ten, który redagowałem.

** Sprawdziłem ustawienia gradle i najwyraźniej opcja Użyj automatycznego importu była już włączona. Mimo to wprowadzenie zmian w pliku build.gradle nie powoduje automatycznej aktualizacji wariantów kompilacji. Uwaga: próbowałem również użyć opcji Build - Rebuild Project i / lub Build - Make Project, no-go. Nadal muszę zamknąć projekt i otworzyć go ponownie, aby zmiany odniosły skutek.


Zauważ, że applicationIdjest teraz obsługiwany zamiast packageName.
Hamzeh Soboh

Odpowiedzi:


220

Jeśli masz w preferencjach Studio, w sekcji Gradle, możesz włączyć automatyczny import dla swojego projektu (włączymy to domyślnie później). Dzięki temu Studio będzie ponownie importować plik build.gradle po każdej edycji.

Tworzenie smaków nie oznacza, że ​​będziesz używać do nich niestandardowego kodu, więc nie tworzymy folderów. Musisz je stworzyć samodzielnie.

Jeśli spojrzysz na moje wystąpienie o zamówieniach publicznych , zobaczysz, jak łączymy ze sobą wartości ze smaków i typów konstrukcji, aby stworzyć wariant.

Dla źródła Java:

src/main/java
src/flavor1/java
src/debug/java

wszystkie 3 są używane do tworzenia jednego wyjścia. Oznacza to, że nie mogą zdefiniować tej samej klasy.

Jeśli chcesz mieć inną wersję tej samej klasy w dwóch smakach, musisz stworzyć ją w obu smakach.

src/flavor1/java/com/foo/A.java
src/flavor2/java/com/foo/A.java

A potem twój kod w src / main / java może to zrobić

import com.foo.A

w zależności od wybranego smaku używana jest właściwa wersja com.foo.A.

Oznacza to również, że obie wersje A muszą mieć to samo API (przynajmniej jeśli chodzi o API używane przez klasy w src / main / java / ...

Edytuj, aby dopasować poprawione pytanie

Ponadto ważne jest, aby umieścić tę samą klasę A tylko w folderach źródłowych, które się wzajemnie wykluczają. W tym przypadku src / flavour1 / java i src / flava2 / java nigdy nie są wybierane razem, ale main i flava1 są.

Jeśli chcesz udostępnić inną wersję działania w innym stylu, nie umieszczaj jej w katalogu src / main / java.

Zwróć uwagę, że jeśli masz 3 smaki i chciałbyś mieć tylko niestandardowy dla flavour1, podczas gdy flavour2 i flavour3 mają to samo działanie, możesz utworzyć wspólne foldery źródłowe dla tych dwóch innych działań. Masz całkowitą elastyczność w tworzeniu nowych folderów źródłowych i konfigurowaniu zestawu źródłowego, aby z nich korzystać.

Przejdźmy do innych punktów:

To normalne, że drugi folder źródłowy smaku nie jest niebieski. Musisz przełączyć się na drugi smak, aby go włączyć, a następnie będziesz mógł tworzyć pakiety i klasy wewnątrz. Do tego czasu Studio nie uważa go za folder źródłowy. Mamy nadzieję, że poprawimy to w przyszłości, aby środowisko IDE było świadome tych nieaktywnych folderów źródłowych.

Myślę, że to również normalne, że nie można tworzyć plików zasobów w folderze res. System menu nie został zaktualizowany, aby poradzić sobie z tymi wszystkimi dodatkowymi folderami zasobów. To przyjdzie później.


1
Dodałem kilka nowych elementów na końcu mojej odpowiedzi, ale duplikat ma sens. Nie możesz mieć tej samej klasy zarówno w src / main / java, jak i src / flavour1 / java, ponieważ obie są używane podczas wybierania flavour1. W mojej odpowiedzi zwróć uwagę, że umieściłem tę samą klasę tylko w flavour1 / java i flava2 / java, ponieważ są one ekskluzywne i nigdy nie są włączone razem.
Xavier Ducrohet

Hej Xavier, czy możesz mi bardziej szczegółowo opisać, jak mogę użyć innej wersji działania w moich smakach? Mam projekt testowy, w którym chcę używać różnych wersji mojej MainActivity, ale w obu apkach (flavour1 i flava2) jest tylko wersja main / java. Kiedy nie umieszczam MainActivity w main / java, aplikacja ulega awarii po uruchomieniu.
JensJensen

@XavierDucrohet co powiesz na posiadanie różnych zasobów, a także różnych kodów opartych na smakach, ale mieć je w różnych modułach, abyśmy mogli dołączyć jeden lub drugi moduł w oparciu o smak, bez konieczności mieszania kodu i zasobów w tym samym projekcie głównym? Czy to jest obsługiwane?
Valerio Santinelli

3
@ValerioSantinelli Możesz zrobić zależności według smaku. WykorzystanieflavorCompile ...
Xavier Ducrohet

@XavierDucrohet Wypróbowałem to, co zasugerowałeś, ale nie działa tak, jak się spodziewałem. Możesz tam zobaczyć, jak zbudowany jest mój projekt: stackoverflow.com/q/24410995/443136
Valerio Santinelli

19

„Smaki produktów” na Androida

Czasami pytano mnie, jak pracować z różnymi hostami, ikonami, a nawet nazwami pakietów, w zależności od różnych wersji tej samej aplikacji.

Jest wiele powodów, aby to zrobić i jedna prosta droga: Smaki produktów.

Możesz zdefiniować w swoim skrypcie build.gradle takie rzeczy, które opisałem wcześniej.

Smaki produktów Część tego artykułu jest napisana z myślą o smakach produktów, więc czym one są? Odnośnie dokumentacji Androida:

Smak produktu definiuje dostosowaną wersję aplikacji utworzonej przez projekt. Pojedynczy projekt może mieć różne smaki, które zmieniają wygenerowaną aplikację.

Jak możesz je zdefiniować? Musisz napisać w pliku build.gradle, które smaki chcesz zdefiniować:

productFlavors {  
        ...
        devel {
            ...
        }

        prod {
            ...
        }
    }

Teraz będziemy mieć dwie różne wersje naszej aplikacji. Możesz to sprawdzić również w Android Studio na karcie Warianty kompilacji

Buduj warianty

Wiele nazw pakietów

A co, jeśli chcesz mieć zainstalowaną na telefonie jedną aplikację ze stanem rozwoju i jedną dla stanu produkcyjnego. Jak być może wiesz, możesz zainstalować tylko jedną aplikację o tej samej nazwie pakietu (jeśli spróbujesz zainstalować nowy plik APK z tym samym, co zainstalowany w telefonie, spróbuje go zaktualizować).

Jedyne, co musisz zrobić, to zdefiniować to na każdym ze smaków produktu:

android {  
    productFlavors {
        devel {
            applicationId "zuul.com.android.devel"
        }
        prod {
            applicationId "zuul.com.android"
        }
    }
}

Wysyłaj żądania do wielu hostów w zależności od smaku Tak jak poprzednio, musisz dołączyć niektóre parametry w polu konfiguracji smaku produktu.

android {  
    productFlavors {
        devel {
            applicationId "zuul.com.android.devel"
            buildConfigField 'String', 'HOST', '"http://192.168.1.34:3000"'

        }

        prod {
            applicationId "zuul.com.android"
               buildConfigField 'String', 'HOST', '"http://api.zuul.com"'

        }
    }
}

Jako przykład postaramy się pokazać, jak można to zintegrować z Retrofitem, aby wysłać żądanie do odpowiedniego serwera bez obsługi serwera wskazanego i na podstawie smaku. W tym przypadku jest to fragment aplikacji Zuul na Androida:

public class RetrofitModule {

    public ZuulService getRestAdapter() {
        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(BuildConfig.HOST)
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .build();
        return restAdapter.create(ZuulService.class);
    }

}

Jak widać, wystarczy użyć klasy BuildConfigclass, aby uzyskać dostęp do właśnie zdefiniowanej zmiennej.

Dowolna zmienna dostępna za pośrednictwem Twojego kodu Zmienna HOST nie jest jedyną, którą możesz ujawnić w swoim kodzie. Możesz to zrobić z czymkolwiek chcesz:

prod {  
    applicationId "zuul.com.android"
    buildConfigField 'String', 'HOST', '"http://api.zuul.com"'
    buildConfigField 'String', 'FLAVOR', '"prod"'
    buildConfigField "boolean", "REPORT_CRASHES", "true"
}

Możesz uzyskać do nich dostęp w następujący sposób:

BuildConfig.HOST  
BuildConfig.FLAVOR  
BuildConfig.REPORT_CRASHES  

Różne ikony dla każdego smaku Jeśli chcesz mieć różne ikony dla każdego smaku, dzięki czemu możesz wizualnie wykryć, którą otwierasz (możesz to również zrobić za pomocą nazwy ... Ale to nie mieści się w przestrzeni!), Po prostu masz aby zdefiniować nowe struktury katalogów dla każdego ze smaków.

W przykładzie, którego właśnie użyłem, są dwa smaki: devel i prod. Następnie moglibyśmy zdefiniować dwie nowe struktury katalogów, abyśmy mogli zdefiniować potrzebne zasoby:

Struktura

Działa to z innymi typami zasobów, takimi jak strings.xml, integers.xml, arrays.xmlitp.

Skonfiguruj ustawienia podpisywania

Aby ręcznie skonfigurować konfiguracje podpisywania dla typu kompilacji wydania przy użyciu konfiguracji kompilacji Gradle:

1 Utwórz magazyn kluczy. Magazyn kluczy to plik binarny zawierający zestaw kluczy prywatnych. Musisz przechowywać swój magazyn kluczy w bezpiecznym miejscu. 2. Utwórz klucz prywatny. Klucz prywatny reprezentuje jednostkę, która ma być identyfikowana z aplikacją, na przykład osobę lub firmę. 3. Dodaj konfigurację podpisywania do pliku build.gradle na poziomie modułu:

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

}

Wygeneruj podpisany plik APK:

Aby wygenerować podpisany plik APK, wybierz opcję Kompiluj> Generuj podpisany plik APK z menu głównego. Pakiet w app / build / apk / app-release.apk jest teraz podpisany kluczem do wydania.

ref: https://developer.android.com/studio/build/build-variants.html#signing,http://blog.brainattica.com/how-to-work-with-flavours-on-android/



7

Wygląda na to, że po dodaniu nowych smaków musisz ponownie załadować projektbuild.gradle . Następnie zobaczysz 4 warianty budowy w widoku wariantów budowy (dostęp do nich można uzyskać z lewej krawędzi okna).

Jeśli chodzi o dodatkowe katalogi źródłowe, wydaje się, że musisz je utworzyć ręcznie: src/flavor1/javai src/flavor2/java. Zobaczysz, że zmiana stylu w widoku „Warianty kompilacji” zmieni aktualnie aktywne katalogi źródłowe (katalog jest niebieski, gdy jest katalogiem aktywnego źródła )

Wreszcie „Gradle stworzy nowe sourceSets dla nowych smaków” oznacza, że Gradle będą tworzyć obiekty android.sourceSets.flavor1i android.sourceSets.flavor2można z nich korzystać w skrypcie build.gradle. Ale te obiekty są tworzone dynamicznie, dlatego nie widzisz ich w build.gradle(proponuję przeczytać to: http://www.gradle.org/docs/current/userguide/tutorial_using_tasks.html). Szczególnie w 6.6: wyjaśnia tworzenie zadania dynamicznego. Skrypt gradle to groovy, więc proponuję również zapoznać się z groovy)


2
Myślę, że uwaga dotycząca importu to Build VariantsWidok, nie zauważyłem tego.
Chris.Jenkins,

2

Miałem ten sam problem podczas migracji projektu do Gradle. Problem polegał na tym, że kompilacja nie znalazła odpowiedniego folderu zasobów. Naprawiłem to, dodając to pod elementem androida w build.gradle:

sourceSets {
        main {
            res.srcDirs = ['myProject/res']
        }
    }

0

Coś, co jest ważne i blokowało mnie od dłuższego czasu, to nazwa smaku, który musi pasować do opakowania, w przeciwieństwie do opakowania zdefiniowanego w definicji smaku w gradle. Na przykład:

src/flavor1/java/com/foo/A.java

będzie pasować

productFlavors {
  flavor1 {
    packageName 'com.android.studio.test.foobar'
  }
}

ale

src/foobar/java/com/foo/A.java nie będzie używany w kompilacji o smaku 1.


0

W gradle:

W przypadku typów kompilacji potrzebujesz tylko:

buildTypes {
   release{
    //proguard, signing etc.
   }
   debug {
    //development
   }
  }
}

A potem do smaków dodajesz te, których potrzebujesz

productFlavors {
    pro {
        applicationIdSuffix '.paid'
        buildConfigField 'boolean', 'PRO', 'true'
    }
    free {
        applicationIdSuffix '.free'
        buildConfigField 'boolean', 'PRO', 'false'
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.