Создание бесплатных / платных версий приложения из одного кода
Так я иду вниз, чтобы освободить время для моего приложения. Мы планируем выпустить две версии, бесплатную версию play-to-unlock на основе рекламы и платную полностью разблокированную версию. У меня есть настроенный код, который я могу просто установить флаг при запуске, чтобы включить/отключить рекламу и заблокировать/разблокировать все функции. Так буквально только одна строка кода будет выполняться по-разному между этими версиями.
чтобы выпустить два отдельных приложения, они требуют разных имен пакетов, поэтому мой вопрос есть ли простой способ рефакторинга имени пакета моего приложения? Инструмент рефакторинга Eclipse не разрешает сгенерированный файл R или любые ссылки XML в файлах макета и манифеста. Я пытался сделать новый проект, используя оригинал в качестве источника, но я не могу ссылаться на активы и ресурсы, и я ищу, чтобы избежать дублирования кода и активов. Это не огромная боль, чтобы рефакторинг это вручную, но я чувствую, что там должен быть лучший способ, чтобы сделать это. У кого-нибудь есть элегантное решение это?
Редактировать/Ответил:
для моей ситуации я считаю вполне приемлемым просто использовать Project - > Android Tools - > Rename Application Package. Я не знал, что это существует, и я чувствую себя идиотом, чтобы опубликовать это сейчас. Спасибо за ответы и комментарии каждого, не стесняйтесь голосовать за это закрыто.
7 ответов:
возможно, дубликат массовая публикация приложений для Android.
проекты библиотеки Android сделают это для вас красиво. Вы получите 1 проект библиотеки, а затем проект для каждого издания (бесплатный/полный) с теми, кто действительно содержит разные ресурсы, такие как значки приложений и разные манифесты, где имя пакета будет изменяться.
надеюсь, что это поможет. Это хорошо сработало для меня.
Это очень просто с помощью строить.gradle в Android Studio. Читайте о productFlavors. Это очень полезная функция. Просто добавьте следующие строки в build.Gradle в:
productFlavors { lite { packageName = 'com.project.test.app' versionCode 1 versionName '1.0.0' } pro { packageName = 'com.project.testpro.app' versionCode 1 versionName '1.0.0' } }в этом примере я добавляю два вкуса продукта: первый для облегченной версии и второй для полной версии. Каждая версия имеет свой собственный код версии и имя версии (для публикации Google Play).
в коде просто проверьте BuildConfig.Вкус:
if (BuildConfig.FLAVOR == "lite") { // add some ads or restrict functionallity }для работы и тестирование на устройстве используйте вкладку "варианты сборки" в Android Studio для переключения между версиями:
лучший способ-использовать "Android Studio" - > gradle.build - > [productFlavors + создать файл манифеста из шаблона]. Эта комбинация позволяет создавать бесплатные / платные версии и кучу выпусков для разных рынков приложений из одного источника.
это часть шаблонного файла манифеста:
<manifest android:versionCode="1" android:versionName="1" package="com.example.product" xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/{f:FREE}app_name_free{/f}{f:PAID}app_name_paid{/f}" android:name=".ApplicationMain" android:theme="@style/AppTheme"> <activity android:label="@string/{f:FREE}app_name_free{/f}{f:PAID}app_name_paid{/f}" android:name=".ActivityMain"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application>
это шаблон "ProductInfo.шаблон " для java-файла: ProductInfo.java
package com.packagename.generated; import com.packagename.R; public class ProductInfo { public static final boolean mIsPaidVersion = {f:PAID}true{/f}{f:FREE}false{/f}; public static final int mAppNameId = R.string.app_name_{f:PAID}paid{/f}{f:FREE}free{/f}; public static final boolean mIsDebug = {$DEBUG}; }
этот манифест обрабатывается градля.построить скрипт с помощью productFlavors и processManifest задач крюк:
import java.util.regex.Matcher; import java.util.regex.Pattern; import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction ... android { ... productFlavors { free { packageName 'com.example.product.free' } paid { packageName 'com.example.product.paid' } } ... } afterEvaluate { project -> android.applicationVariants.each { variant -> def flavor = variant.productFlavors[0].name tasks['prepare' + variant.name + 'Dependencies'].doLast { println "Generate java files..." //Copy templated and processed by build system manifest file to filtered_manifests forder def productInfoPath = "${projectDir}/some_sourcs_path/generated/" copy { from(productInfoPath) into(productInfoPath) include('ProductInfo.template') rename('ProductInfo.template', 'ProductInfo.java') } tasks.create(name: variant.name + 'ProcessProductInfoJavaFile', type: processTemplateFile) { templateFilePath = productInfoPath + "ProductInfo.java" flavorName = flavor buildTypeName = variant.buildType.name } tasks[variant.name + 'ProcessProductInfoJavaFile'].execute() } variant.processManifest.doLast { println "Customization manifest file..." // Copy templated and processed by build system manifest file to filtered_manifests forder copy { from("${buildDir}/manifests") { include "${variant.dirName}/AndroidManifest.xml" } into("${buildDir}/filtered_manifests") } tasks.create(name: variant.name + 'ProcessManifestFile', type: processTemplateFile) { templateFilePath = "${buildDir}/filtered_manifests/${variant.dirName}/AndroidManifest.xml" flavorName = flavor buildTypeName = variant.buildType.name } tasks[variant.name + 'ProcessManifestFile'].execute() } variant.processResources.manifestFile = file("${buildDir}/filtered_manifests/${variant.dirName}/AndroidManifest.xml") } }
это отдельная задача для обработки файла
class processTemplateFile extends DefaultTask { def String templateFilePath = "" def String flavorName = "" def String buildTypeName = "" @TaskAction void run() { println templateFilePath // Load file to memory def fileObj = project.file(templateFilePath) def content = fileObj.getText() // Flavor. Find "{f:<flavor_name>}...{/f}" pattern and leave only "<flavor_name>==flavor" def patternAttribute = Pattern.compile("\{f:((?!${flavorName.toUpperCase()})).*?\{/f\}",Pattern.DOTALL); content = patternAttribute.matcher(content).replaceAll(""); def pattern = Pattern.compile("\{f:.*?\}"); content = pattern.matcher(content).replaceAll(""); pattern = Pattern.compile("\{/f\}"); content = pattern.matcher(content).replaceAll(""); // Build. Find "{$DEBUG}" pattern and replace with "true"/"false" pattern = Pattern.compile("\{\$DEBUG\}", Pattern.DOTALL); if (buildTypeName == "debug"){ content = pattern.matcher(content).replaceAll("true"); } else{ content = pattern.matcher(content).replaceAll("false"); } // Save processed manifest file fileObj.write(content) } }обновление: processTemplateFile создан для повторного использования кода.
Gradle позволяет использовать сгенерированный BuildConfig.java для передачи некоторых данных в код.
productFlavors { paid { packageName "com.simple.paid" buildConfigField 'boolean', 'PAID', 'true' buildConfigField "int", "THING_ONE", "1" } free { packageName "com.simple.free" buildConfigField 'boolean', 'PAID', 'false' buildConfigField "int", "THING_ONE", "0" }
для всех, кто хочет использовать решение Дениса:
В новой версии gradlepackageNameтеперьapplicationIdи не забудьте поставитьproductFlavors { ... }наandroid { ... }productFlavors { lite { applicationId = 'com.project.test.app' versionCode 1 versionName '1.0.0' } pro { applicationId = 'com.project.testpro.app' versionCode 1 versionName '1.0.0' } }
один из подходов, с которым я экспериментирую,-это использование полных имен для действий и просто изменение атрибута пакета. Это позволяет избежать реального рефакторинга (1 копия файла, 1 текстовый суб).
Это почти работает, но сгенерированный класс R не подобран, так как пакет для этого вытащен из AndroidManifest.xml, так что заканчивается в новом пакете.
Я думаю, что это должно быть довольно прямо вперед, чтобы построить AndroidManifest.XML с помощью правил Муравей (в -предварительно построить), что вставляет имя пакета распространения, а затем (в процессе предварительной компиляции) созданные ресурсы в пакет по умолчанию (Java).
надеюсь, это поможет,
Фил Лелло
Если вы хотите другое имя приложения, в зависимости от вкуса, вы можете также добавить это:
productFlavors { lite { applicationId = 'com.project.test.app' resValue "string", "app_name", "test lite" versionCode 1 versionName '1.0.0' } pro { applicationId = 'com.project.testpro.app' resValue "string", "app_name", "test pro" versionCode 1 versionName '1.0.0' } }

Comments