Как у вас есть два аромата сборки, которые наследуются от корневого вкуса в Android Studio?

У меня были следующие ароматы проекта:

  • Apple,
  • Orange

Первоначально единственным отличием был applicationId/packageName. Теперь есть один java файл, который отличается. Конкретный ArrayAdapter, если быть точным. Решение заключалось в создании src/Apple и src/Orange и оба наследуемых от src/main. Я удалил java файл из src/main и поместил копию в src/Apple и src/Orange и соответствующим образом изменил его. Все было хорошо в мире.

Ускорьте вперед несколько недель, теперь есть около 10 java файлов, которые отличаются между Apple и Orange. Опять... нет ничего страшного. Прост в обращении. Разделите java файлы в src/Apple и src/Orange.

Ускоренная перемотка вперед. Мне нужно немного изменить ситуацию, потому что я хочу иметь бесплатную и премиальную версию каждого. Свободные и премиальные версии отличаются только URL-адресом. Я собирался просто создавать новые типы:

  • AppleFree
  • ApplePremium
  • OrangeFree
  • OrangePremium

У меня есть дилемма. Поскольку теперь src/Apple и src/Orange имеют 10 разных файлов, которые были изменены... если я изменю любой java файл в AppleFree, я должен убедиться, что я делаю то же самое в ApplePremium. Я нахожусь на перекрестке и надеюсь, что мой вопрос будет иметь смысл в этот момент. Я придумал три возможных решения, но я не уверен, как я их реализую/что будет правильным подходом/решение не то, что я хочу.

Решение 1:

Используйте оператор if if (BuildConfig.FLAVOR==appleFree) {//use free Url} else {// use premium url}

Проблема: Оба Urls технически скомпилированы в apk. Я не хочу этого.

Решение 2:

У вас есть src/AppleFree и src/ApplePremium от родительского каталога src/Apple.

Проблема: Не знаю, как я это сделаю.

Решение 3:

Добавьте бесплатный и премиальный URL прямо в build.gradle, как это?

    productFlavors {
            appleFree {
                applicationId "com.example.apple.free"
                versionName "1.0"
                url "http://freeurl.com"
                versionCode 1
            }
            applePremium {
                applicationId "com.example.apple.premium"
                versionName "1.0"
                url "http://premiumurl.com"
                versionCode 1
            }
            orangeFree {
                applicationId "com.example.orange.free"
                versionName "1.0"
                versionCode 1
                url "http://freeurl.com"


            }
            orangePremium {
                applicationId "com.example.orange.premium"
                url "http://premiumurl.com"
                versionName "1.0"
                versionCode 1

            }
        } 

Проблема: Не знаете, как это сделать.

Полезны любые советы.

EDIT:

Окончательное решение?

flavorGroups 'fruit', 'paid'

productFlavors {
    apple {
        flavorGroup 'fruit'
    }
    orange {
        flavorGroup 'fruit'
    }
    free {
        flavorGroup 'paid'
    }
    premium {
        flavorGroup 'paid'
    }
    appleFree {
        applicationId "com.example.apple.free"
        versionName "1.0"
        buildConfigField 'String', 'BASE_URL', 'http://freeurl.com'
        versionCode 1
    }
    applePremium {
        applicationId "com.example.apple.premium"
        versionName "1.0"
        buildConfigField 'String', 'BASE_URL', 'http://premiumurl.com'
        versionCode 1
    }
    orangeFree {
        applicationId "com.example.orange.free"
        versionName "1.0"
        versionCode 1
        buildConfigField 'String', 'BASE_URL', 'http://freeurl.com'
    }
    orangePremium {
        applicationId "com.example.orange.premium"
        buildConfigField 'String', 'BASE_URL', 'http://premiumurl.com' 
        versionName "1.0"
        versionCode 1
    }
    }

Ответы

Ответ 1

Существует много возможных решений вашей проблемы. Самым естественным решением для Gradle должно быть использование Flavor Dimensions, как описано в http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Multi-flavor-variants

Это также похоже на то, о чем вы думали в решении 2.

Это будет работать примерно так:

flavorDimensions 'fruit', 'paid'

productFlavors {
    apple {
        dimension 'fruit'
    }
    orange {
        dimension 'fruit'
    }
    free {
        dimension 'paid'
    }
    premium {
        dimension 'paid'
    }
}

Это даст вам варианты сборки (и исходные папки), где она сочетает в себе все возможности из каждого размера вкуса, сохраняя тот же порядок, что и группы в вашем заявлении flavorDimensions (т.е. appleFree, а не freeApple), таким образом:

* appleFree
* applePremium
* orangeFree
* orangePremium

в папке src/, вы можете иметь следующие возможности:

* src/main
* src/apple
* src/orange
* src/free
* src/premium
* src/appleFree
* src/applePremium
* src/orangeFree
* src/orangePremium

Решение 3

Вы можете использовать buildConfigField для указания констант, которые входят в класс BuildConfig по принципу "за вкусом":

productFlavors {
    appleFree {
        buildConfigField 'String', 'MY_URL', 'value1'
    }
    applePremium {
        buildConfigField 'String', 'MY_URL', 'value2'
    }
    orangeFree {
        buildConfigField 'String', 'MY_URL', 'value3'
    }
    orangePremium {
        buildConfigField 'String', 'MY_URL', 'value4'
    }

Решение 1

Я пытался что-то решить в соответствии с решением 1, но это не сработает для вашего конкретного случая использования. Если у вас есть условие if в Java, которое проверяет логическое значение, объявленное static final, тогда компилятор может определить статически, доступен ли код или нет, и он разделит его, если он не будет. Таким образом:

static final boolean DEBUG = false;

...

if (DEBUG) {
    // do something
}

Код в // do something не будет компилироваться вообще. Это преднамеренное и документированное поведение со стороны компилятора Java и позволяет писать дорогой код отладки, который не будет скомпилирован в ваш двоичный файл release. BuildConfig.DEBUG для этой точной причины объявляется static final.

Там a BuildConfig.FLAVOR, но он определен как String, и вы не получите того же преимущества:

static final String FLAVOR = "orange";

...

if (FLAVOR.equals("apple")) {
    // do something
}

Компилятор недостаточно умен, чтобы делать статический анализ, см., что // do something недоступен, а не компилирует его. Обратите внимание, что он будет работать нормально во время выполнения, но этот мертвый код будет включен в ваш двоичный файл.

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