当前位置: 首页 > news >正文

Google Play 下架 App 之后的替身制作

前段时间升级 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"
    }
}
复制代码

好了,sync之后,现在我们就可以在 Gradle->:app->Tasks->build 中编译两个不同的 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
    }
}
复制代码

这里有几点需要注意

  1. signingConfigs 声明需要放在 productFlavors 之前,否则找不到对应的自定义签名
  2. productFlavors 中已指定签名,需要把 buildTypes -> release 下面指定的签名删除,否则在编译 Release 版本时会覆盖掉 productFlavors 中指定的签名。

微信支付相关报错

我们替换掉 applicationId 之后,其实 Context.getPackageName() 方法返回的值也跟着变化了,那么,微信支付根据这个包名去查找相关的配置肯定是找不到的,所以,我们需要将 WXEntryActivityWXPayEntryActivity 拷贝一份到 com.yun.reader.wxapi 包名下面。记得在 manifests 中用绝对路径声明这两个 Activity。

有的同学就算这样配置之后,微信支付仍然会报错,因为我们改了 applicationId 导致以前默认是 news 的相关配置对 reader 是不适用的。所以我们需要为以前的代码背锅,将所有写死的地方改为配置型。

例如: 在微信支付时,需要传递 app_code 字段,让服务器在生成订单时,知晓当前的应用是哪一个,方便后续的校对。如果服务器直接写死了是 news, 那么在后续匹配中,news 申请的 支付码和 packageName 不能匹配,那么支付失败是必然的。

类似的问题还有 cookie , 与Web端交互的时候,对方会根据cookie来判别应用,所以Web端也需要做兼容。RN 端类似。

AndroidManifest 中根据 productFlavors 指定特定变量

例如:QQ 登录,每个 App 有不同的 scheme,本文中用 applicationId 来伪装成两个不同的 App, scheme 需要配置不同的 tencent_id。这里可以利用manifestPlaceholders来实现这个需求。

首先需要在 gradle 中声明:

defaultConfig {
    manifestPlaceholders = [tencent_id: "1000000000", package_name: "com.yun.news"]
}
复制代码

如果 manifestPlaceholders 包含多个值,需要用 , 隔开。

productFlavors中配置很简单

productFlavors {
    google {
        applicationId "com.yun.reader"
        signingConfig signingConfigs.google
        manifestPlaceholders.tencent_id = "1000000000"
        manifestPlaceholders.package_name = "com.yun.reader"
    }

    yun {
        applicationId "com.yun.news"
        signingConfig signingConfigs.yun
        manifestPlaceholders.tencent_id = "1000000001"
        manifestPlaceholders.package_name = "com.yun.news"
    }
}
复制代码

manifest 中使用方式如下:

<activity
    android:name="com.tencent.tauth.AuthActivity"
    ...>
    <intent-filter>
        ...
        <data android:scheme="tencent${tencent_id}" />
    </intent-filter>
</activity>

复制代码

这样我们就可以根据不同的 productFlavors 来配置特性字段了。 类似的有:MIPush(如果写死包名,手机不能同时安装两个变体),

参考:developer.android.com/studio/buil…

相关文章:

  • 安卓应用性能调试和优化经验分享
  • Redis 懒删除(lazy free)简史
  • vue全家桶+Koa2开发笔记(8)--开发网页
  • Mycat - 实现数据库的读写分离与高可用
  • 理解 JavaScript 中的 this
  • 块级、内联、内联块级
  • Spring Boot 运作原理
  • 虚拟机网络不通故障解决
  • 原型模式深入探讨-【设计模式4】
  • 撕掉普通程序员的标签,这才是真正的大数据工程师!
  • Spring基于XML方式配置事务
  • 程序员最讨厌的9句话,你可有补充?
  • 一些关于Rust在2019年的思考
  • 通信类
  • 力扣(LeetCode)56
  • Akka系列(七):Actor持久化之Akka persistence
  • Android 架构优化~MVP 架构改造
  • Asm.js的简单介绍
  • java多线程
  • leetcode46 Permutation 排列组合
  • Redis学习笔记 - pipline(流水线、管道)
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • SQLServer插入数据
  • Vultr 教程目录
  • Windows Containers 大冒险: 容器网络
  • windows-nginx-https-本地配置
  • 闭包--闭包作用之保存(一)
  • 翻译--Thinking in React
  • 构建工具 - 收藏集 - 掘金
  • 前端面试之闭包
  • 人脸识别最新开发经验demo
  • 我有几个粽子,和一个故事
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 在Mac OS X上安装 Ruby运行环境
  • 找一份好的前端工作,起点很重要
  • ​​​​​​​​​​​​​​Γ函数
  • ​iOS安全加固方法及实现
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • # 透过事物看本质的能力怎么培养?
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • (+4)2.2UML建模图
  • (175)FPGA门控时钟技术
  • (31)对象的克隆
  • (c语言)strcpy函数用法
  • (Ruby)Ubuntu12.04安装Rails环境
  • (二)斐波那契Fabonacci函数
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (译)2019年前端性能优化清单 — 下篇
  • (转)程序员疫苗:代码注入
  • (转载)hibernate缓存
  • ***通过什么方式***网吧
  • .Net Core与存储过程(一)
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇