Лучший способ получить платную и бесплатную версию приложения для Android

У меня уже есть бесплатное приложение в Android Market, но я хочу добавить платную версию с улучшенными функциями. Я не могу загрузить то же самое с некоторыми измененными константами, чтобы разблокировать эти функции, поскольку Маркет говорит мне, что у меня уже есть приложение с таким названием пакета на рынке.

Какой самый чистый способ сделать это?

9 ответов

Решение

Android SDK формально решает проблему общей или общей кодовой базы с помощью проекта библиотеки.

http://developer.android.com/tools/projects/index.html

По сути, общий код определяется как проект библиотеки, затем платная и бесплатная версии - это просто два разных проекта в вашей рабочей среде eclipse, которые ссылаются на вышеупомянутый проект библиотеки.

Во время сборки проект библиотеки объединяется с любым из ваших выпусков, что вам и нужно.

Пример исходного кода Android SDK содержит проект под названием TicTacToe, который поможет вам начать работу с библиотечными проектами.

Удачи.

Даниил

Существует несколько подходов, но вы обычно не видите недостатков, пока не попробуете их сами. Вот мой опыт:

  1. Приложение для разблокировки. Это очень легко реализовать, создайте новое приложение, которое действует как лицензия: ваше оригинальное приложение должно проверить, совпадает ли подпись приложения разблокировки с подписью вашего приложения (если да, разблокировщик доступен на устройстве, в противном случае нет); Ваш разблокировщик должен запросить загрузку бесплатного приложения, если оно не установлено, в противном случае запустите его и удалите его значок запуска.

    Плюсы: проста в реализации и поддерживается только одна кодовая база.
    Минусы: говорят, что пользователи иногда смущаются этой моделью, они просто не понимают, почему у них две иконки запуска, или что они только что загрузили. Удаление значка запуска громоздко, изменения видны только после перезагрузки устройства. Кроме того, кажется, что вы не сможете использовать API лицензирования Google (LVL), поскольку ваше бесплатное приложение не может отправлять запросы на лицензирование от имени вашего платного приложения для разблокировки. Любой обходной путь для этого последнего ведет к плохому пользовательскому опыту.

  2. Покупки в приложении. Это легко реализовать, если в любом случае в вашем коде есть IAP, иначе потребуется некоторое время, чтобы все исправить.

    Плюсы: есть только одна кодовая база для поддержки, и процесс покупки для пользователя довольно удобен.
    Минусы: говорят, что пользователи обеспокоены тем, является ли их покупка "устойчивой", что означает, что они не уверены, могут ли они по-прежнему использовать профессиональные функции, если позднее они установили приложение на другое устройство или переустановили его.

  3. Бесплатные и платные версии с общим библиотечным проектом. Это не должно быть трудным для программирования и кажется логичным решением иметь единую кодовую базу, содержащую большую часть логики вашего приложения и поддерживающую только различия.

    Плюсы: пользователи меньше путаются с этой моделью, но они, вероятно, обеспокоены тем, будут ли потеряны их предпочтения, если они начнут использовать платную версию.
    Минусы: Java / Eclipse по моему опыту не очень дружелюбен в отношении библиотек. Для правильной настройки проектов потребуется некоторое время, но все равно будет непонятно, как все это работает, что помещать в какой манифест, что происходит с ресурсами и т. Д. Вы столкнетесь с проблемами сборки с неправильно настроенными проектами, проблемами сборки с ресурсы не найдены (проект библиотеки не будет "видеть" уже упоминавшиеся ресурсы, поэтому вам придется редактировать ссылки по одной). Более того, файлы ресурсов не поддерживаются в этой модели, то есть вам придется проделать некоторые трюки с символическими ссылками, копированием или другой магией, которые просто добавят к ужасному беспорядку, которым уже стал ваш проект "единой кодовой базы". И когда ваш проект наконец-то создается, вам нужно просто скрестить пальцы, чтобы все работало должным образом, будьте готовы к отсутствующим изображениям и поиску скрытых ошибок. И, конечно же, вам нужно будет предоставить своим пользователям удобный способ перенести их настройки в платную версию при первом запуске.

  4. Бесплатные и платные версии с отдельными кодовыми базами и контролем версий. На первый взгляд это также кажется хорошей идеей, так как приличная система контроля исходного кода может снять вес с ваших плеч, но...

    Плюсы: так же, как 3.
    Минусы: вы будете обслуживать две разные кодовые базы и ничего, кроме адского слияния / ветвления, которого здесь и следовало ожидать. Имена пакетов приложений должны отличаться, поэтому вам, вероятно, нужно будет различать каждый отдельный файл, чтобы сохранить чистоту. И, конечно же, вам нужно будет предоставить своим пользователям удобный способ перенести их настройки в платную версию при первом запуске.

  5. Бесплатные и платные версии со скриптом, который наследует одно от другого. Это звучит как грязный хак, но вы точно знаете, что это работает.

    Плюсы: так же, как 3 плюс, вы поддерживаете только одну кодовую базу.
    Минусы: создание сценариев занимает немного времени (убедитесь, что вы переименовываете папки, заменяете имена пакетов, приложений и проектов), и вам нужно быть осторожным, чтобы время от времени обновлять свои сценарии в случае необходимости (этого не произойдет если между бесплатной и платной версиями не так много различий). И, конечно же, вам нужно будет предоставить своим пользователям удобный способ перенести их настройки в платную версию при первом запуске.

Ни одно решение не является идеальным, но, как мы надеемся, вышеприведенное может направить вас в правильном направлении, если вы только собираетесь начать реализацию одной из возможностей.

С системой сборки Gradle теперь вы можете иметь разные варианты продуктов, каждый из которых имеет свое собственное имя пакета. Ниже приведен пример скрипта gradle, имеющего бесплатные и профессиональные версии одного и того же приложения.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 19
    buildToolsVersion "19.1"

    defaultConfig {
        applicationId "com.example.my.app"
    }

    productFlavors {
        free {
            applicationId "com.example.my.app"
            minSdkVersion 15
            targetSdkVersion 23
            versionCode 12
            versionName '12'
        }
        pro {
            applicationId "com.example.my.app.pro"
            minSdkVersion 15
            targetSdkVersion 23
            versionCode 4
            versionName '4'
        }
    }

R класс будет по-прежнему генерироваться в имени пакета, указанном в AndroidManifest.xml так что вам не нужно менять одну строку кода при переключении вкусов.

Вы можете переключить вкус с Build Variants панель, которая доступна из левого нижнего угла Android Studio. Кроме того, когда вы хотите создать подписанный APK, Android Studio спросит вас, какой вкус вы хотите создать APK.

Кроме того, вы можете иметь разные ресурсы для каждого аромата. Например, вы можете создать каталог pro в вашем src каталог. Структура каталогов должна быть похожа на main каталог. (Например: если вы хотите иметь другую иконку запуска для профессиональной версии, вы можете поместить ее в src\pro\res\drawable, Это заменит бесплатный значок, расположенный в src\main\res\drawableкогда вы перешли на pro вкус).

Если вы создаете strings.xml в pro каталог ресурсов, описанный выше, основной strings.xml и про strings.xml будет объединен, чтобы получить финал strings.xml при строительстве в pro аромат. Если определенный ключ строки существует как в свободном, так и в pro xml, значение строки будет взято из pro xml.

Если вам нужно проверить, является ли текущая версия про или бесплатной в коде, вы можете использовать метод, подобный следующему.

public boolean isPro() {
    return context.getPackageName().equals("com.example.my.app.pro");
}

Для получения дополнительной информации, обратитесь к этому

Один исходный код двух приложений (Eclipse)

Существует постоянная проблема управления приложением с двумя формами представления на рынке, возможно, только с разницей в один бит между ними ( paidFor = true;) Возможно, значок также отличается.

Этот подход использует описание имен пакетов, объясненное Михаем по адресу http://blog.javia.org/android-package-name/ котором подчеркивается различие между именем пакета, используемым для управления исходным кодом, и именем пакета, использованным для публикации apk на Android Маркете. В этом примере два опубликованных имени пакета: com.acme.superprogram и com.acme.superprogrampaid, имя пакета исходного кода Java - com.acme.superprogram.

В манифесте Действия перечислены и названы как.ActivityName. Майк Уоллес отметил в своей недавней презентации, что предыдущая точка важна и может быть заменена полностью квалифицированным пакетом. Например, "com.acme.superprogram.DialogManager" может заменить ".DialogManager" в тексте manifest.xml.

Шаг 1 состоит в том, чтобы заменить все записи об активности android: name этими полностью определенными именами пакетов, используя имя пакета управления исходным кодом java (com.acme.superprogram).

Тогда имя Манифест-пакета можно изменить...

В Eclipse это вызывает перекомпиляцию, и в папке gen создается новая R.java. Вот где это становится немного сложнее; Есть две папки com.acme.superprogram и com.acme.superprogrampaid, только в одной есть R.java. Просто скопируйте R.java в другую папку, чтобы программа могла разрешать элементы R.layout.xyz.

Когда вы меняете пакет в

Я попробовал это на нескольких приложениях. У меня оба работают вместе на эмуляторе, на моем телефоне 2.1, и они оба на Android Market.

На всякий случай, если у вас есть модуль износа, вам необходимо выполнить дополнительную работу.

Вот несколько примеров файлов Gradle для упаковки модуля износа со вкусами и типами сборки.

Модуль мобильный build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "com.example.app"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 85
        versionName "2.5.2"
    }
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
            embedMicroApp = true
            minifyEnabled false
        }
        release {
            embedMicroApp = true
            shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            zipAlignEnabled true
        }
    }
    productFlavors {
        free{
            applicationId "com.example.app"
        }
        pro{
            applicationId "com.example.app.pro"
        }
    }
}

configurations {
    freeDebugWearApp
    proDebugWearApp
    freeReleaseWearApp
    proReleaseWearApp
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'

    freeDebugWearApp project(path: ':wear', configuration: 'freeDebug')
    proDebugWearApp project(path: ':wear', configuration: 'proDebug')

    freeReleaseWearApp project(path: ':wear', configuration: 'freeRelease')
    proReleaseWearApp project(path: ':wear', configuration: 'proRelease')
}

Износ модуля build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"
    publishNonDefault true

    defaultConfig {
        applicationId "com.example.app"
        minSdkVersion 20
        targetSdkVersion 23
        versionCode 85
        versionName "2.5.2"
    }
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
            minifyEnabled false
        }
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            zipAlignEnabled true
        }
    }
    productFlavors {
        free {
            applicationId "com.example.app"
        }
        pro {
            applicationId "com.example.app.pro"
        }
    }
}

dependencies {

    ...

}

У меня была та же проблема: я решил создать два приложения для Android, одно из которых называется my_epic_app_free, а другое my_epic_app_paid. Что я хотел бы сделать, это только внести изменения в платную версию, тогда у меня есть отдельная консольная программа java, все, что она делает, - это получает доступ ко всем файлам.java непосредственно с диска, копирует их в память, манипулирует именами пакетов и строками манифеста, затем вставьте их прямо в бесплатное приложение. У меня это происходит нажатием кнопки на рабочем столе, поэтому, когда я заканчиваю разработку на платной версии, я нажимаю кнопку, а затем компилирую бесплатную версию. У меня есть платное и бесплатное приложение, связывающиеся друг с другом, пользователь может даже установить их оба, а бесплатная версия видит платную и открывает платную версию.

Другая идея состоит в том, чтобы сделать платное приложение просто голой программой, которая содержит ключевой файл. Платное приложение, если оно будет запущено, автоматически запустит бесплатную версию, а бесплатная версия будет вести себя как платная версия.

Бесплатное приложение проверяет наличие файла ключа платного приложения, и если оно существует, оно разблокирует бесплатное приложение. Это предотвращает дублирование кода. Имена пакетов по-прежнему должны отличаться, но в идеале платное приложение не нужно менять часто.

Преимущество: никакие пользовательские данные / настройки не теряются при "обновлении" до платной версии.

Недостаток: если пользователь сначала устанавливает платную версию, ему нужно будет установить бесплатную версию, что создает трудности, зачем ему устанавливать два приложения для платной версии?

Изменение имени пакета поможет.
Просто измените название пакета вашего платного приложения, используя eclipse, и разместите его на рынке.

Это решит вашу проблему.

Если вы не хотите использовать библиотечные проекты и довольны рисками, о которых пару человек упомянули в комментариях, то @user426990 теоретически дал отличный ответ. Однако eclipse, по-видимому, стирает все содержимое каталога gen при создании новой сборки для экспорта (с которой мне трудно поспорить в качестве общего принципа).

Альтернативное решение, основанное на том же принципе, заключается в следующем, при условии, что вы написали com.acme.superprogram и хотите создать com.acme.superprogrampaid

  1. Убедитесь, что ваш манифест указывает на деятельность, услуги и так далее по полному имени. Согласно ответу @user426990, ".CoolActivity" должен быть указан как com.acme.superprogram.CoolActivity.

  2. Создайте новый класс MyR в своем коде (com.activity.superprogram, с остальным кодом) следующим образом:

    package com.acme.superprogram;
    import com.acme.superprogram.R;
    
    public final class MyR {
        public final static R.attr     attr     = new R.attr();
        public final static R.color    color    = new R.color();
        public final static R.dimen    dimen    = new R.dimen();
        public final static R.layout   layout   = new R.layout();
        public final static R.id       id       = new R.id();
        public final static R.string   string   = new R.string();
        public final static R.drawable drawable = new R.drawable();
        public final static R.raw      raw      = new R.raw();
        public final static R.style    style    = new R.style();
        public final static R.xml      xml      = new R.xml();
    }
    

    Вам нужно будет изменить точное содержание, чтобы отразить ресурсы, которые вы используете! Например, вам может не понадобиться строка xml, а может понадобиться еще одна. Посмотрите на настоящий файл R.java в gen / com / acme / superprogram, и вам понадобится одна строка на класс. Вам может понадобиться подкласс.

  3. Теперь (а) удалите все строки "import com.acme.superprogram.R" из своего кода и (б) замените все "R." ссылки на "MyR.". Таким образом, все ваши ссылки на R перенаправляются через одно место. Недостатком является то, что все они получают предупреждения о том, что они недостаточно статичны. У вас есть три варианта с этими предупреждениями: вы можете подавить их, вы можете их игнорировать или можете сделать более полную версию MyR со строкой для каждой записи в R, которую вы используете:

    package com.acme.superprogram;
    import com.acme.superprogram.R;
    
    public final class MyR {
        final static class attr {
            final static int XXXX = R.attr.XXXX
    // And so on ...
    
  4. Согласно @user426990, теперь вы можете изменить пакет в манифесте с "com.acme.superprogram" на "com.acme.superprogrampaid"; Вы, вероятно, также хотите изменить имя, значок запуска и ключевые переменные на этом этапе.

  5. Измените строку импорта в MyR для импорта com.acme.superprogrampaid.R

И понеслось.

пс. Не забудьте изменить имя файла в окончательном диалоговом окне экспорта...

Почему бы не сделать два разных проекта Eclipse? Это не так сложно исправить вручную несколько вещей, относящихся к названию пакета. Будьте внимательны, чтобы изменить операторы импорта и файл манифеста. Вам все равно нужно дважды написать код. Конечно, исправлять код в обоих местах неудобно.

Правильное решение будет на рынке. Он должен разрешать два приложения с одинаковыми именами пакетов и запрашивать замену одного, когда другое должно быть установлено. После загрузки он должен проверить, действительно ли имя пакета от того же поставщика программного обеспечения.

Rene

Другие вопросы по тегам