前段時(shí)間升級(jí) qq sdk 實(shí)現(xiàn)第三方登錄功能。在用戶(hù)手機(jī)未安裝QQ時(shí),會(huì)引導(dǎo)用戶(hù)下載安裝。Google Play 商店以它的規(guī)則為由下架了我們的App。這個(gè)問(wèn)題很多開(kāi)發(fā)者都遇到過(guò),畢竟我們都得聽(tīng) Google 的,規(guī)則制定者不是開(kāi)玩笑的。經(jīng)過(guò)溝通,堅(jiān)決不讓重新上架,提議換個(gè)包名拷貝代碼,重新上傳一個(gè)新的應(yīng)用。我們當(dāng)時(shí)按照這個(gè)簡(jiǎn)單的方法上架了緊急修復(fù)的App,但是考慮到后續(xù)功能迭代以及兩個(gè)項(xiàng)目的維護(hù)成本,需要尋求一個(gè)新的方式來(lái)完成這些操作。
背景
公司有個(gè)老版本的讀書(shū)軟件一直在線(xiàn)上,但是很久沒(méi)維護(hù)了。這次事故正好可以把老的軟件作為殼子,替換成目前維護(hù)的新的App。這樣可以避免用戶(hù)流失(Google Play 渠道用戶(hù)量本身就不是很大)。
修改應(yīng)用ID
每個(gè) Android 應(yīng)用均有一個(gè)唯一的應(yīng)用 ID,像 Java 軟件包名稱(chēng)一樣,如 com.example.myapp。 此 ID 可以在設(shè)備上和 Google Play 商店中對(duì)您的應(yīng)用進(jìn)行唯一標(biāo)識(shí)。 如果您想要上傳新版本的應(yīng)用,應(yīng)用 ID(以及使用它簽署的證書(shū))必須與原始 APK 相同 - 如果您更改應(yīng)用 ID,Google Play 商店會(huì)將 APK 視為完全不同的應(yīng)用。所以您發(fā)布應(yīng)用后,絕不應(yīng)更改應(yīng)用 ID。
基于這個(gè)原則,我們實(shí)現(xiàn)的方式很簡(jiǎn)單,配置編譯變體,指定特定的 applicationId
應(yīng)用是基于 Walle 進(jìn)行多渠道打包的,那么我們需要配置兩個(gè) productFlavors ,其中一個(gè)是對(duì) Google Play 商店的定制版,另一個(gè)是使用 [Wally] 進(jìn)行多渠道打包的基礎(chǔ)apk。當(dāng)然如果你沒(méi)有做多渠道打包,那么也至少需要配置兩個(gè),畢竟你需要一個(gè) 未變體的 Apk 供其他渠道使用。
flavorDimensions "default" // 當(dāng)只有一個(gè)時(shí),不需要在 Flavors 中重復(fù)指定
productFlavors {
google { // Google Play 商店的定制版
applicationId "com.yun.reader"
}
yun { // 基礎(chǔ)版本 apk
applicationId "com.yun.news"
}
}
好了,現(xiàn)在我們可以編譯兩個(gè)不同的 apk 了。
指定不同的簽名
老版本的 Apk 簽名和新開(kāi)發(fā) Apk 并不是用的同一個(gè)簽名文件,所以,在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")
}
}
}
}
看上面的代碼你會(huì)發(fā)現(xiàn),我們的路徑都是放在遠(yuǎn)程編譯服務(wù)器的環(huán)境變量中的,你也可以存放在本地的 local.properties 中,具體的操作可以自行Google。
兩個(gè)簽名定義完成,使用起來(lái)很簡(jiǎn)單:
flavorDimensions "default" // 當(dāng)只有一個(gè)時(shí),不需要在 Flavors 中重復(fù)指定
productFlavors {
google { // Google Play 商店的定制版
applicationId "com.yun.reader"
signingConfig signingConfigs.google
}
yun { // 基礎(chǔ)版本 apk
applicationId "com.yun.news"
signingConfig signingConfigs.yun
}
}
這里有幾點(diǎn)需要注意
-
signingConfigs聲明需要放在productFlavors之前,否則找不到對(duì)應(yīng)的自定義簽名 -
productFlavors中已指定簽名,需要把buildTypes -> release下面指定的簽名刪除,否則在編譯 Release 版本時(shí)會(huì)覆蓋掉productFlavors中指定的簽名。
微信支付相關(guān)報(bào)錯(cuò)
我們替換掉 applicationId 之后,其實(shí) Context.getPackageName() 方法返回的值也跟著變化了,那么,微信支付根據(jù)這個(gè)包名去查找相關(guān)的配置肯定是找不到的,所以,我們需要將 WXEntryActivity 和 WXPayEntryActivity 拷貝一份到 com.yun.reader.wxapi 包名下面。記得在 manifests 中用絕對(duì)路徑聲明這兩個(gè) Activity。
有的同學(xué)就算這樣配置之后,微信支付仍然會(huì)報(bào)錯(cuò),因?yàn)槲覀兏牧?applicationId 導(dǎo)致以前默認(rèn)是 news 的相關(guān)配置對(duì) reader 是不適用的。所以我們需要為以前的代碼背鍋,將所有寫(xiě)死的地方改為配置型。
例如:
在微信支付時(shí),需要傳遞 app_code 字段,讓服務(wù)器在生成訂單時(shí),知曉當(dāng)前的應(yīng)用是哪一個(gè),方便后續(xù)的校對(duì)。如果服務(wù)器直接寫(xiě)死了是 news, 那么在后續(xù)匹配中,news 申請(qǐng)的 支付碼和 packageName 不能匹配,那么支付失敗是必然的。
類(lèi)似的問(wèn)題還有 cookie , 與Web端交互的時(shí)候,對(duì)方會(huì)根據(jù)cookie來(lái)判別應(yīng)用,所以Web端也需要做兼容。RN 端類(lèi)似。
參考:https://developer.android.com/studio/build/application-id?hl=zh-cn