前言
本文最主要的是講解下自己是如何在在項(xiàng)目中更恰當(dāng)合理的運(yùn)用SopHix涛菠,當(dāng)然這只是我團(tuán)隊(duì)兩個(gè)人的做法而已。經(jīng)驗(yàn)總是要踩出來的疙渣,對(duì)同學(xué)有幫助就最好了匙奴。
以下分為六部分
-
引言
-
官方使用步驟
-
測試開發(fā)中熱更新修復(fù)操作五部曲
-
下發(fā)補(bǔ)丁步驟
-
如何處理加載補(bǔ)丁成功后的彈窗強(qiáng)制更新提醒
-
如何爬坑
一、引言
熱修復(fù)技術(shù)可謂是百花齊放妄荔,許多產(chǎn)品都聲稱自己可以做到全方位全功能的熱修復(fù)泼菌。不過他們各自有自身的局限性谍肤,或者不夠穩(wěn)定,或者補(bǔ)丁過大哗伯,或者效率低下荒揣,或者使用起來過于繁瑣,大部分技術(shù)上看起來似乎可行焊刹,但實(shí)際體驗(yàn)并不好系任。而在我們看來,有很多技術(shù)細(xì)節(jié)能夠做得更加完美伴澄。
終于在2017年6月11日赋除,手淘技術(shù)團(tuán)隊(duì)聯(lián)合阿里云正式發(fā)布了史上首個(gè)非侵入式移動(dòng)熱更新解決方案——Sophix。
Sophix熱修復(fù)方案主要開發(fā)者非凌,萬壑 就職于手機(jī)淘寶举农。
Sophix的橫空出世,將會(huì)打破各家熱修復(fù)技術(shù)紛爭的局面敞嗡。我們可以滿懷信心地說颁糟,在Android熱修復(fù)的三大領(lǐng)域:代碼修復(fù)、資源修復(fù)喉悴、so修復(fù)方面棱貌,以及方案的安全性和易用性方面,Sophix都做到了業(yè)界領(lǐng)先箕肃。
文章介紹 https://yq.aliyun.com/articles/103527?spm=5176.8091938.0.0.l6pz1T
??下面的這張表格引用自[ 作者博文]婚脱,從幾個(gè)熱修復(fù)最重要的維度,把Sophix和另外兩個(gè)主要商業(yè)化熱修復(fù)方案進(jìn)行了比較勺像。
而其中唯一不支持的地方就是四大組件的修復(fù)障贸,這是因?yàn)槿绻迯?fù)四大組件,必須在AndroidManifest里面預(yù)先插入代理組件吟宦,并且盡可能聲明所有權(quán)限篮洁,而這么做就會(huì)給原先的app添加很多臃腫的代碼,對(duì)app運(yùn)行流程的侵入性很強(qiáng)殃姓≡ǎ可以插入幾個(gè)代理文件即可。我在項(xiàng)目中就這么做蜗侈。
二篷牌、官方使用步驟
Android 接入流程請(qǐng)開發(fā)者務(wù)必仔細(xì)閱讀一遍官方文檔。文檔是最好的老師
先去創(chuàng)建應(yīng)用
然后快速接入流程
三踏幻、測試開發(fā)中熱更新修復(fù)操作五部曲
打包區(qū)分的三個(gè)包
Base_1.5.30 (基礎(chǔ)包用于對(duì)比)
Safe_1.5.30(加固過后的包枷颊,發(fā)布給線上使用)
Test_1.5.31(在打完Base包之后修改版本號(hào),再打一個(gè)測試包,確保代碼一致偷卧,用于安裝豺瘤,下發(fā)補(bǔ)丁首先下發(fā)到該版本,我是這么做的)Safe_1.5.30發(fā)布完上線之后 听诸,在你的Android Studio中修改版本號(hào)為1.5.33 繼續(xù)開發(fā)或者修復(fù)BUG坐求,反正需要大于1.5.31(因?yàn)槟阕鰷y試需要先在Sophix后臺(tái)下發(fā)補(bǔ)丁到1.5.31,你本地代碼沒有修改的話晌梨, build 運(yùn)行時(shí)會(huì)收到Sophix服務(wù)器的下發(fā)的補(bǔ)丁桥嗤,此時(shí)代碼覆蓋會(huì)讓你一臉懵逼)
這時(shí)你已經(jīng)在 Dev_1.5.33 版本中修復(fù)了BUG,打包1.5.33之后仔蝌,需要通過 Sophix工具上傳 基礎(chǔ)包 Base_1.5.30 進(jìn)行比對(duì)生成 Sophix-Patch.jar 補(bǔ)丁包 泛领,(生成的補(bǔ)丁與版本號(hào)無關(guān),工具只會(huì)對(duì)比代碼結(jié)構(gòu), so敛惊,資源等) 完成后將其上傳至Sophix后臺(tái)
指定下發(fā)對(duì)應(yīng)版本號(hào)渊鞋,先下發(fā)到Test_1.5.31 測試版本,補(bǔ)丁下發(fā)成功瞧挤! 手機(jī)安裝 先測試驗(yàn)收锡宋,驗(yàn)收成功之后再指定下發(fā)到 正式的版本 1.5.30 即可
Tips: 在Android Studio中 開發(fā)中的版本需要比Test1.5.31版本大,便于在 Build 運(yùn)行項(xiàng)目時(shí)特恬,不會(huì)因聯(lián)網(wǎng)下發(fā)到針對(duì)1.5.31的補(bǔ)丁 指示代碼收到下發(fā)的補(bǔ)丁 受影響
注意事項(xiàng):
下發(fā)補(bǔ)丁修復(fù)bug不要去修改 Manifest 的文件执俩,發(fā)布補(bǔ)丁時(shí)的版本也最好不要去修改一些依賴庫的版本,會(huì)出現(xiàn)某些問題癌刽!
還有打base包的 那份代碼需要備份這個(gè)分支役首,然后copy一個(gè)分支進(jìn)行下一步的開發(fā),以防萬一合成補(bǔ)丁出錯(cuò)显拜,進(jìn)行代碼比對(duì)
舊包內(nèi)部版本號(hào)不能 比新包內(nèi)部版本號(hào)大衡奥,不然會(huì)生成補(bǔ)丁出錯(cuò)
??這里我并沒有使用官方的調(diào)試工具進(jìn)行調(diào)試。而是直接在項(xiàng)目中查看日志補(bǔ)丁加載狀態(tài)讼油。要多注意每個(gè)Code代表的意思 杰赛,這里文檔也有描述到呢簸,詳情查看SDK中PatchStatus類的代碼矮台,有具體說明
四、下發(fā)補(bǔ)丁步驟
生成補(bǔ)丁工具
??假設(shè)當(dāng)前 版本為 1.5.30 為發(fā)布的包根时,而你要先進(jìn)行更新測試包更新包版本為 1.5.31 ,這個(gè)時(shí)候你就要將最新代碼生成的apk文件包與base基線版本1.5.30做比對(duì)瘦赫。此時(shí)生成的補(bǔ)丁文件 sophix-patch.jar 上傳至 Sophix 后臺(tái)。
??補(bǔ)丁版本為后臺(tái)自控的蛤迎。會(huì)進(jìn)行疊加确虱。繼續(xù)上傳一個(gè)補(bǔ)丁則上一個(gè)版本的補(bǔ)丁自動(dòng)停用.
- 添加版本號(hào)要注意格式1.X.X 或者 1.XXX
![~J$E2{$(6OFRBSI]WA7Y0MR.png](http://upload-images.jianshu.io/upload_images/956862-2a3012b0a3c770ab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -
上傳補(bǔ)丁文件,并且加點(diǎn)描述
五替裆、如何處理加載補(bǔ)丁成功后的彈窗強(qiáng)制更新提醒
- 強(qiáng)制更新補(bǔ)丁狀態(tài)說明:
一般都會(huì)把查詢補(bǔ)丁的操作放在Application中去操作校辩,Sophix啟動(dòng)APP時(shí)會(huì)調(diào)用方法請(qǐng)求查詢最新的補(bǔ)丁窘问,這句話一般在Application中的onCreate()方法的Super之前去進(jìn)行調(diào)用,這里貼一下自己項(xiàng)目中的代碼
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
initHotfix(base);
}
public void onCreate() {
SophixManager.getInstance().queryAndLoadNewPatch();
super.onCreate();
...
}
// 阿里熱修復(fù)
private void initHotfix(Context context) {
SophixManager.getInstance().setContext(this)
.setAppVersion(VersionUtils.getVersionName(this))
.setAesKey(null)
.setEnableDebug(false)
.setPatchLoadStatusStub((mode, code, info, handlePatchVersion) -> {
// 補(bǔ)丁加載回調(diào)通知
String s = "mode:" + mode + " code:" + code + "\npatchVersion:" + handlePatchVersion + "\ninfo:" + info;
hotfixController(context, code, handlePatchVersion, s);//這里是處理相對(duì)應(yīng)的主要Code的操作宜咒,代碼就不貼完整了惠赫,你可以根據(jù)官方的來進(jìn)行操作,建議多加一個(gè)對(duì)CODE_LOAD_NOPATCH 狀態(tài)碼14故黑,沒有補(bǔ)丁的情況針對(duì)性的操作
}).initialize();
}
```
Q:收到Code 12 之后儿咱,有的包是需要提示用戶強(qiáng)制冷啟動(dòng)的,而有的包是不用提示场晶,等下次自己更新就好了混埠。這樣的情況怎么判斷當(dāng)前的包是處于哪種情況呢?
需要提示 & 不需要再提示诗轻?
A:官方給我的提示钳宪,需要自己加個(gè)推送什么的,回調(diào)中沒有這個(gè)標(biāo)識(shí)扳炬。((__) 嘻嘻…… 如果Shophix后臺(tái)有支持就好了)這里主要講解下發(fā)成功 狀態(tài)為 1 "load new patch success."時(shí)使套,也就是補(bǔ)丁下發(fā)成功了,之后提醒用戶的操作鞠柄。
這里需要判斷兩個(gè)前提條件侦高,都要提示用戶去重啟應(yīng)用, 要注意回調(diào)狀態(tài)碼Code 等于12 “please relaunch app to load new patch” 的時(shí)候 就是冷啟動(dòng)的提示厌杜。 這里我并沒有根據(jù)12去做判斷進(jìn)行重啟奉呛。
下面說下我自己項(xiàng)目中的兩個(gè)必要重啟判斷條件,僅供參考夯尽。
* 1瞧壮、取得SopHix SDK啟動(dòng)時(shí)返回的最新的補(bǔ)丁版本號(hào)(hotfixPatchVersion)和服務(wù)器返回的補(bǔ)丁版本號(hào)(hotfixMustUpdateVersion)相同,且大于當(dāng)前最后一次強(qiáng)制重啟的補(bǔ)丁版本號(hào)(hotfixLastMustVersion 將每次返回的補(bǔ)丁版本寫入SP記錄)
* 2匙握、或者第一次下載咆槽、數(shù)據(jù)被清除后,如果有新強(qiáng)制更新版本圈纺,則強(qiáng)制更新
建議定義三個(gè)字段進(jìn)行比較
public static final String hotfixPatchVersion = "hotfixPatchVersion"; // 阿里返回的補(bǔ)丁版本號(hào)
public static final String hotfixMustUpdateVersion = "hotfixMustUpdateVersion"; // 服務(wù)器返回的版本號(hào)
public static final String hotfixLastMustVersion = "hotfixLastMustVersion"; // 最后一次強(qiáng)制重啟的版本號(hào)
注意:
以上所說的這種方式秦忿,實(shí)際項(xiàng)目中加載了最新的補(bǔ)丁,需要把這個(gè)Sophix中最新的補(bǔ)丁版本號(hào)蛾娶,告知后臺(tái)進(jìn)行修改為最新更新的補(bǔ)丁號(hào)灯谣。一般在版本更新的接口中多加入一個(gè)字段(downloadPatchCode ),請(qǐng)求時(shí)用于進(jìn)行比對(duì)蛔琅。
提示用戶關(guān)閉時(shí)的處理胎许,可以在PatchLoadStatusListener監(jiān)聽到CODE_LOAD_RELAUNCH后在合適的時(shí)機(jī),調(diào)用此方法殺死進(jìn)程。注意辜窑,不可以直接Process.killProcess(Process.myPid())來殺進(jìn)程钩述,這樣會(huì)擾亂Sophix的內(nèi)部狀態(tài)。因此如果需要?dú)⑺肋M(jìn)程穆碎,建議使用這個(gè)方法切距,它在內(nèi)部做一些適當(dāng)處理后才殺死本進(jìn)程。本人是直接在一個(gè)透明的 彈窗提示判斷中關(guān)閉時(shí)調(diào)用的此方法進(jìn)行關(guān)閉惨远。
SophixManager.getInstance().killProcessSafely();
六谜悟、如何爬坑
以下是我所遇到過的兩個(gè)問題,遇到問題心態(tài)不要蹦北秽,先去查看日志葡幸,再去查看官方文檔,或者QA贺氓,百分之七八十都可以解決蔚叨,若你還不能解決你就去官方釘釘群交流:11734260 進(jìn)行交流。
問題1
熱修復(fù)Android SDK:在4.x的手機(jī)系統(tǒng)上崩潰
在4.x的系統(tǒng)上出現(xiàn)IllegalAccessException:class ref in pre-verified異常辙培,應(yīng)用崩潰
問題原因
??這個(gè)是由于我們是完整dex修復(fù)蔑水,所以會(huì)出現(xiàn)新dex中的類和老dex中的類沖突,所以要在Sophix初始化前避免加載原有apk中的類扬蕊,而對(duì)于4.x版本的系統(tǒng)搀别,如果在Sophix初始化之前有加載原有apk中的類,則會(huì)影響修復(fù)的過程尾抑,造成崩潰
解決方案
https://help.aliyun.com/knowledge_detail/55414.html?spm=5176.7851422.2.4.SyAVhp
??Sophix初始化在Application最前面歇父,同時(shí)盡量用系統(tǒng)類及盡量不使用log等,或者把initialize寫到attachBaseContext里面再愈,但query還是在onCreat的最前面榜苫;如果有用到MultiDex,直接繼承Application翎冲,在attachBaseContext里寫MultiDex.install(base)垂睬,然后在onCreat的最前面initialize和query,如果還會(huì)出現(xiàn)這個(gè)崩潰抗悍,把initialize寫到attachBaseContext的MultiDex.install(base)后面驹饺,但query還是在onCreat的最前面
問題2
這個(gè)問題很奇怪,項(xiàng)目老大解決的檐春。我現(xiàn)在只知道不能這么做而已逻淌。
??在4.X的設(shè)備上么伯,用OkHttp作為Fresco的網(wǎng)絡(luò)加載器的時(shí)候疟暖,如果發(fā)布新的包(比如只修改一個(gè)Toast),
在下載完重啟后會(huì)直接崩潰報(bào)如下錯(cuò)誤:
Process: com.newtest.myapplication, PID: java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
而其他機(jī)型則不會(huì)。 很明顯和第一個(gè)錯(cuò)誤是一樣的俐巴。
Sophix對(duì)4.X的機(jī)型有Bug: 在一個(gè)外部引用的包里骨望,自帶的實(shí)例,需要去調(diào)用這個(gè)包里的另一個(gè)實(shí)例方法時(shí)欣舵,會(huì)報(bào)上面這個(gè)錯(cuò)擎鸠; 但把這個(gè)包里的實(shí)例移到App里創(chuàng)建,再去調(diào)用相同的那個(gè)方法里就不會(huì)了缘圈。 就是說4.X的引用包里的方法不能再次調(diào)用引用包里的別的方法劣光。 現(xiàn)在已經(jīng)解決了,但5.0及以上的機(jī)型卻不會(huì)有這個(gè)Bug糟把,這只是很奇葩的問題绢涡,如果沒遇到過同學(xué)略過吧~
以上兩個(gè)問題也屬于同一個(gè)問題,詳細(xì)閱讀作者這邊文章會(huì)有些啟示遣疯。
- 由于是直接在項(xiàng)目分支中引入的Sophix 所以沒有寫個(gè)Demo雄可。
有空補(bǔ)上(這是我聽過過最大的笑話)
貼一個(gè)官方Demo
以上僅是自己的處理方式,寫的不好的地方還望指正缠犀,有問題還望同學(xué)們指點(diǎn)一二数苫,O(∩_∩)O謝謝!
學(xué)會(huì)分享~