前段时间升级 qq sdk
实现第三方登录功能。在用户手机未安装QQ时,会引导用户下载安装。Google Play 商店以它的规则为由下架了我们的App。这个问题很多开发者都遇到过,毕竟我们都得听 Google 的,规则制定者不是开玩笑的。经过沟通,坚决不让重新上架,提议换个包名拷贝代码,重新上传一个新的应用。我们当时按照这个简单的方法上架了紧急修复的App,但是考虑到后续功能迭代以及两个项目的维护成本,需要寻求一个新的方式来完成这些操作。
背景
公司有个老版本的读书软件一直在线上,但是很久没维护了。这次事故正好可以把老的软件作为壳子,替换成目前维护的新的App。这样可以避免用户流失(Google Play 渠道用户量本身就不是很大)。
修改应用ID
每个 Android 应用均有一个唯一的应用 ID,像 Java 软件包名称一样,如 com.example.myapp。 此 ID 可以在设备上和 Google Play 商店中对您的应用进行唯一标识。 如果您想要上传新版本的应用,应用 ID(以及使用它签署的证书)必须与原始 APK 相同 - 如果您更改应用 ID,Google Play 商店会将 APK 视为完全不同的应用。所以您发布应用后,绝不应更改应用 ID。
基于这个原则,我们实现的方式很简单,配置编译变体,指定特定的 applicationId
应用是基于 Walle 进行多渠道打包的,那么我们需要配置两个 productFlavors
,其中一个是对 Google Play 商店的定制版,另一个是使用 [Wally] 进行多渠道打包的基础apk。当然如果你没有做多渠道打包,那么也至少需要配置两个,毕竟你需要一个 未变体的 Apk 供其他渠道使用。
flavorDimensions "default" // 当只有一个时,不需要在 Flavors 中重复指定
productFlavors {
google { // Google Play 商店的定制版
applicationId "com.yun.reader"
}
yun { // 基础版本 apk
applicationId "com.yun.news"
}
}
好了,现在我们可以编译两个不同的 apk 了。
指定不同的签名
老版本的 Apk 签名和新开发 Apk 并不是用的同一个签名文件,所以,在productFlavors
中需要指定签名文件。
首先我们需要定义签名文件:
android {
signingConfigs {
yun {
if (System.getenv("YUN_NEWS_KEYSTORE_FILE") != null) {
storeFile file(System.getenv("YUN_NEWS_KEYSTORE_FILE"))
storePassword System.getenv("YUN_NEWS_KEYSTORE_PASSWORD")
keyAlias System.getenv("YUN_NEWS_KEY_ALIAS")
keyPassword System.getenv("YUN_NEWS_KEY_PASSWORD")
}
}
google {
if (System.getenv("YUN_READER_KEYSTORE_FILE") != null) {
storeFile file(System.getenv("YUN_READER_KEYSTORE_FILE"))
storePassword System.getenv("YUN_READER_KEYSTORE_PASSWORD")
keyAlias System.getenv("YUN_READER_KEY_ALIAS")
keyPassword System.getenv("YUN_READER_KEY_PASSWORD")
}
}
}
}
看上面的代码你会发现,我们的路径都是放在远程编译服务器的环境变量中的,你也可以存放在本地的 local.properties
中,具体的操作可以自行Google。
两个签名定义完成,使用起来很简单:
flavorDimensions "default" // 当只有一个时,不需要在 Flavors 中重复指定
productFlavors {
google { // Google Play 商店的定制版
applicationId "com.yun.reader"
signingConfig signingConfigs.google
}
yun { // 基础版本 apk
applicationId "com.yun.news"
signingConfig signingConfigs.yun
}
}
这里有几点需要注意
-
signingConfigs
声明需要放在productFlavors
之前,否则找不到对应的自定义签名 -
productFlavors
中已指定签名,需要把buildTypes -> release
下面指定的签名删除,否则在编译 Release 版本时会覆盖掉productFlavors
中指定的签名。
微信支付相关报错
我们替换掉 applicationId
之后,其实 Context.getPackageName()
方法返回的值也跟着变化了,那么,微信支付根据这个包名去查找相关的配置肯定是找不到的,所以,我们需要将 WXEntryActivity
和 WXPayEntryActivity
拷贝一份到 com.yun.reader.wxapi
包名下面。记得在 manifests 中用绝对路径声明这两个 Activity。
有的同学就算这样配置之后,微信支付仍然会报错,因为我们改了 applicationId
导致以前默认是 news
的相关配置对 reader
是不适用的。所以我们需要为以前的代码背锅,将所有写死的地方改为配置型。
例如:
在微信支付时,需要传递 app_code 字段,让服务器在生成订单时,知晓当前的应用是哪一个,方便后续的校对。如果服务器直接写死了是 news, 那么在后续匹配中,news 申请的 支付码和 packageName 不能匹配,那么支付失败是必然的。
类似的问题还有 cookie
, 与Web端交互的时候,对方会根据cookie来判别应用,所以Web端也需要做兼容。RN 端类似。
参考:https://developer.android.com/studio/build/application-id?hl=zh-cn