我已经在安卓市场上发布了一款免费应用,但我想添加一个付费版,提供更好的功能。由于市场告诉我已经有一个使用相同包名的应用程序,我不能上传相同的应用程序并更改一些常量来解锁这些功能。
最清晰的做法是什么?
我已经在安卓市场上发布了一款免费应用,但我想添加一个付费版,提供更好的功能。由于市场告诉我已经有一个使用相同包名的应用程序,我不能上传相同的应用程序并更改一些常量来解锁这些功能。
最清晰的做法是什么?
Android SDK正式解决了共享或公共代码库的问题,称之为Library Project。
http://developer.android.com/tools/projects/index.html
基本上,共享的代码被定义为Library Project,然后付费版本和免费版本只是您Eclipse工作台中的两个不同项目,都引用了上述Library Project。
在构建时,Library Project将与您的任一发行版合并,这正是您想要的。
Android SDK示例源代码包含一个名为TicTacToe的项目,可以帮助您开始使用Library Projects。
祝你好运。
Daniel
有几种方法可以实现,但通常只有尝试之后才会看到缺点。这是我的经验:
解锁应用程序。这很容易实现,创建一个作为许可证的新应用程序:您的原始应用程序应检查解锁器应用程序的签名是否与您的应用程序匹配(如果是,则设备上可用解锁器,否则不可用);如果未安装,则您的解锁器应提示下载您的免费应用程序,否则启动它并删除其启动器图标。
优点:易于实施,并且只需要维护一个代码库。
缺点:据说用户有时会对此模型感到困惑,他们根本不明白为什么他们有两个启动器图标或者他们刚刚下载了什么。删除启动器图标很麻烦,更改只有在设备重新启动后才能看到。此外,似乎您将无法使用Google的许可API(LVL),因为您的免费应用无法代表您的付费解锁器应用程序发出许可请求。任何这后者的解决方法都会导致糟糕的用户体验。
应用内购买。如果您已经在代码中使用了IAP,则很容易实现,否则需要花费相当长的时间才能做到这一点。
优点:只需要维护一个代码库,用户购买流程相当方便。
缺点:据说用户担心他们的购买是否“持久”,也就是说,他们不确定如果他们以后将应用程序安装到另一个设备或重新安装它是否仍然可以使用专业版功能。
免费和付费版本的共享库项目。编码应该不难,看起来像是一个有逻辑的决定,包含大部分应用程序逻辑的单个代码库,并仅维护差异。
优点:用户对此模型的理解较少混淆,但他们可能担心如果开始使用付费版本,他们的首选项是否会丢失。
具有单独的代码库和源控件的免费版和付费版。乍一看,这也似乎是一个不错的主意,因为一个体面的源控制系统可以减轻你的负担,但......
优点:同第3点。
缺点:您将维护两个不同的代码库,而除了合并/分支地狱之外什么都没有。应用程序包名称应该不同,因此您可能需要区分每个单独的文件,以保持事情清晰。当然,您还需要为用户提供方便的方法,在首次启动时迁移其偏好设置到付费版本中。
具有脚本从其中一个派生另一个的免费版和付费版。听起来像是通过一个肮脏的小技巧来实现的,但你可以肯定它能够工作。
优点:与第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
中指定的包名中生成,所以当切换口味时,您不需要更改任何一行代码。
您可以从Android Studio左下角的Build Variants
面板中切换口味。此外,在您想要生成已签名的APK时,Android Studio会询问您要构建的APK的口味。
另外,您可以为每个口味使用不同的资源。例如,您可以在src
目录中创建一个pro
目录。目录结构应与main
目录类似。(例如:如果您想要为专业版本创建不同的启动器图标,则可以将其放置在src\pro\res\drawable
中。当您切换到pro
口味时,它将替换位于src\main\res\drawable
中的免费图标)。
如果您在上述pro
资源目录中创建了strings.xml
,则主要strings.xml
和pro
strings.xml
将合并以获得最终的strings.xml
。如果某个字符串键存在于免费和pro xml中,则将从pro xml中取字符串值。
如果您需要在代码中检查当前版本是专业版还是免费版,则可以使用以下方法。
public boolean isPro() {
return context.getPackageName().equals("com.example.my.app.pro");
}
获取更多信息,请参阅此处
同一份源代码生成两个应用程序(Eclipse)
市场上常常有只有一位差别的两种应用形式需要管理,也许只是一个变量不同(paidFor = true;),甚至图标可能也不同。
这种方法使用了Mihai在http://blog.javia.org/android-package-name/中解释的包名描述,强调了用于管理源代码和发布apk到Android市场所使用的包名之间的区别。在此示例中,两个发布的包名分别为com.acme.superprogram和com.acme.superprogrampaid,Java源代码包名为com.acme.superprogram。
在清单文件中,Activity被列出并命名为.ActivityName。Mike Wallace在他最近的演讲中指出,前面的点很重要,可以被一个完全限定的包替换。例如,“com.acme.superprogram.DialogManager”可以在manifest.xml文本中替换“.DialogManager”。
步骤1是使用Java源代码管理包名(com.acme.superprogram)将所有activity android:name条目替换为这些完全限定的包名。
然后可以更改清单包名称...
在Eclipse中,这会强制重新编译并创建一个新的R.java文件在gen文件夹中。这里有点棘手;有两个文件夹com.acme.superprogram和com.acme.superprogrampaid,只有一个文件夹有R.java。只需将R.java复制到另一个文件夹中,以便程序可以解析R.layout.xyz项。
当你在...
我已经在几个应用程序上尝试过了。我在模拟器上同时运行这两个应用程序,它们都在我的2.1手机上,并且它们都在Android市场上。
如果你也有一个穿戴模块,那么需要额外的工作。
以下是一些使用口味和构建类型打包穿戴模块的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 {
...
}
好处:升级到付费版本时,不会丢失用户数据/设置。
缺点:如果用户先安装付费版本,则必须指导他们安装免费版本,这很麻烦,为什么他们必须安装两个应用程序才能使用付费版本?
更改包名将有所帮助。
只需使用eclipse更改您的付费应用程序的包名,然后将其放入市场。
那将解决您的问题。
如果您不想使用库项目,并且对一些人在评论中提到的风险感到满意,那么@user426990在理论上提供了一个很好的答案。然而,当进行新的导出构建时,Eclipse似乎会清除gen目录的所有内容(我很难反驳这个作为一般原则的观点)。
基于同样原则的另一种解决方案如下,假设您编写了com.acme.superprogram并希望创建com.acme.superprogrampaid:
确保您的清单文件使用全名指向活动、服务等。根据@user426990的回答,".CoolActivity" 必须列为 com.acme.superprogram.CoolActivity。然后你就可以开始了。
附注:记得在最终导出对话框中更改文件名...