我已经在Android电子市场上有一个免费的应用程序,但我想添加一个具有更好功能的付费版本。我不能上传相同的一些更改的常量来解锁这些功能,因为市场告诉我,我已经在市场上有一个带有该包名称的应用程序。
最干净的方法是什么?
答案 0 :(得分:49)
Android SDK正式解决了共享或公共代码库的问题,称为库项目。
http://developer.android.com/tools/projects/index.html
基本上,共享代码被定义为库项目,然后付费和免费版本只是eclipse工作台中的两个不同项目,两者都参考上述库项目。
在构建时,库项目会与您的任一版本合并,这就是您想要的。
Android SDK示例源代码包含一个名为TicTacToe的项目,它将帮助您开始使用库项目。
祝你好运。丹尼尔
答案 1 :(得分:28)
存在几种方法,但在您自己尝试之前,通常不会看到缺点。以下是我的经历:
解锁应用。这很容易实现,创建一个充当许可证的新应用程序:您的原始应用程序应该检查解锁器应用程序的签名是否与您的应用程序的签名相匹配(如果是,解锁器在设备上可用,否则为否);如果没有安装,解锁器应提示您下载免费应用程序,否则启动它并删除其启动器图标。
优点:易于实施,只需维护一个代码库
缺点:据说用户有时会对此模型感到困惑,他们根本不明白为什么他们有两个启动器图标,或者他们刚刚下载了什么。删除启动器图标很麻烦,只有在重启设备时才能看到更改。此外,您似乎无法使用Google的许可API(LVL),因为您的免费应用无法代表您的付费解锁应用发出许可请求。对后者的任何解决方法都会导致糟糕的用户体验。
应用内购买。如果你的代码中有IAP,这很容易实现,否则需要相当长的时间才能把事情做好。
优点:维护和购买流程只有一个代码库非常方便
缺点:据说用户担心他们的购买是否“持久”,这意味着如果他们以后将应用程序安装到其他设备或重新安装它们,他们是否仍然可以使用专业功能时会感到困惑。 / p>
包含共享库项目的免费和付费版本。这不应该是一个难以编码的事情,并且似乎是一个逻辑决定,让单个代码库包含大部分应用程序逻辑并仅保持差异。
优点:用户不会对此模型感到困惑,但他们可能会担心如果他们开始使用付费版本,他们的偏好是否会丢失。
缺点:根据我的经验,Java / Eclipse在图书馆方面并不是很友好。正确设置项目需要一些时间,但是仍然会混淆整个工作如何工作,在哪个清单中放置什么,在资源中发生什么等等。您将面临错误配置项目的构建问题,构建问题找不到资源(库项目不会“看到”已经引用的资源,因此您必须逐个编辑引用)。此外,此模型不支持资产文件,这意味着您必须使用符号链接,复制或其他魔法来做一些技巧,这些技巧只会增加您的“单个代码库”项目已经变成的可怕混乱。当你的项目最终构建时,你必须保持手指交叉以使整个事情按预期工作,为缺少的图像和隐藏的错误做好准备。当然,您需要为用户提供一种方便的方式,以便在首次启动时将其偏好设置迁移到付费版本。
具有单独代码库和源代码管理的免费和付费版本。乍一看这似乎也是一个好主意,因为一个合适的源控制系统可以减轻肩膀的重量,但是...... 优点:与3.相同 缺点:你将保留两个不同的代码库,除了合并/分支地狱之外别无其他。应用程序包名称应该不同,因此您可能需要区分每个文件,以保持清洁。当然,您需要为用户提供一种方便的方式,以便在首次启动时将其偏好设置迁移到付费版本。
免费版和付费版的脚本可以从另一个版本派生出来。这听起来像是一个肮脏的黑客,但你肯定知道它有效。
优点:与3相同,只保留一个代码库
缺点:创建脚本需要一些时间(确保重命名文件夹,替换包,应用程序和项目名称),如果不时更新脚本,则必须小心必要的(如果免费和付费版本之间没有太多差异,则不会发生这种情况)。当然,您需要为用户提供一种方便的方式,以便在首次启动时将其偏好设置迁移到付费版本。
没有任何解决方案是完美的,但如果您即将开始实施其中一种可能性,上述内容有望为您指明方向。
答案 2 :(得分:22)
使用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
中指定的包名称中生成,因此您在切换口味时无需更改单行代码。
您可以从Android Studio左下角可以访问的Build Variants
窗格切换风味。此外,当你想生成一个签名的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
味道建造。如果free和pro xml中都存在某个字符串键,则字符串值将取自pro xml。
如果您需要在代码中检查当前版本是专业版还是免费版,您可以使用以下方法。
public boolean isPro() {
return context.getPackageName().equals("com.example.my.app.pro");
}
有关详细信息,请参阅this
答案 3 :(得分:15)
一个源代码两个应用程序(Eclipse)
在市场上管理具有两种表现形式的应用程序时经常出现问题,两者之间只有一点差异(paidFor = true;)也许图标也不同。
此方法使用Mihai在http://blog.javia.org/android-package-name/解释的包名称描述,其中强调了用于管理源代码的包名称与用于在Android Market上发布apk的包名称之间的区别。在此示例中,两个已发布的包名称为com.acme.superprogram和com.acme.superprogrampaid,Java源代码包名称为com.acme.superprogram。
在清单中列出了活动并命名为.ActivityName。 Mike Wallace在他最近的演讲中指出,前面的点很重要,可以用完全合格的包替换。例如,“com.acme.superprogram.DialogManager”可以替换manifest.xml文本中的“.DialogManager”。
步骤1是使用java源代码管理包名称(com.acme.superprogram)替换所有具有这些完全限定包名称的活动android:name条目。
然后可以更改Manifest Package名称......
在Eclipse中,这会强制重新编译,并在gen文件夹中创建新的R.java。这是有点棘手的地方;有两个文件夹com.acme.superprogram和com.acme.superprogrampaid,只有一个文件夹有R.java。只需将R.java复制到另一个文件夹中,以便程序可以解析R.layout.xyz项目。
更改包中的时间
我在几个应用程序上尝试过它。我在模拟器和2.1手机上一起跑,他们都在Android Market上。
答案 4 :(得分:1)
我遇到了同样的问题,我决定制作两个Android应用程序,一个名为my_epic_app_free,另一个名为my_epic_app_paid。我要做的只是更改付费版本,然后我有一个单独的java控制台程序,它所做的就是直接从磁盘访问所有.java文件,将它们复制到内存中,摆弄软件包名称和清单行,然后将它们直接粘贴到免费的应用程序中我在桌面上的按钮上有这个,所以当我完成付费版本的开发时,我按下按钮,然后编译免费版本。我有付费应用和免费应用程序相互通信,用户甚至可以安装它们,免费版本看到付费,并解锁到付费版本。
另一个想法是使付费应用程序只是一个包含密钥文件的裸骨程序。付费应用程序(如果运行)将自动运行免费版本,免费版本将表现为付费版本。
免费应用程序检查付费应用程序的密钥文件是否存在,如果存在,则会解锁免费应用程序。这可以防止代码重复。包名称仍然必须不同,但理想情况下付费应用程序不需要经常更改。
好处:“升级”到付费版本时,不会丢失任何用户数据/设置。
缺点:如果用户首先安装付费版本,则必须指示他们安装免费版本,这是一个麻烦,为什么他们必须为付费版本安装两个应用程序?
答案 5 :(得分:1)
如果你还有一个磨损模块,还需要做额外的工作。
以下是一些示例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 {
...
}
答案 6 :(得分:0)
如果您不想使用图书馆项目,并且对评论中提到的几个人提到的风险感到满意,那么@ user426990在理论上提供了一个很好的答案。然而,在进行新的构建导出时,eclipse似乎擦除了gen目录的全部内容(我觉得很难说这是一般原则)。
基于相同原理的替代解决方案如下,假设您已编写com.acme.superprogram并且您希望创建com.acme.superprogrampaid
确保您的清单以全名指向活动,服务等。根据@ user426990的回答“.CoolActivity”必须列为com.acme.superprogram.CoolActivity
在您的代码中创建一个新类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行,可能需要另一行。查看gen / com / acme / superprogram中的真实R.java文件,每个类需要一行。您可能需要子类化。
现在(a)从您的代码中删除所有“import com.acme.superprogram.R”行,并(b)替换所有“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 ...
根据@ user426990,您现在可以将清单中的包从“com.acme.superprogram”更改为“com.acme.superprogrampaid”;您可能还想在此时更改名称,启动图标和关键变量。
更改MyR中的导入行以导入com.acme.superprogrampaid.R
离开你。
PS。请记住在最终导出对话框中更改文件名...
答案 7 :(得分:0)
更改包名称会有所帮助 只需使用eclipse更改付费应用的包名称,然后将其投放市场。
这将解决您的问题。
答案 8 :(得分:-5)
为什么不制作两个不同的Eclipse项目?手动修复涉及包名称的一些事情并不困难。注意更改import语句和清单文件。无论如何你需要编码两次。当然,在两个地方修复代码都很麻烦。
适当的解决方案将在市场上。它应该允许两个具有相同软件包名称的应用程序,并要求在另一个应用程序安装时替换该软件包。上传后,应检查包名是否真的来自同一软件供应商。
雷