阿里雲地址:https://help.aliyun.com/document_detail/51416.html?spm=5176.doc53238.6.540.rbsAxH
熱修復技術,已經是目前主流APP廣泛使用的技術,也是android平臺發展成熟一個階段的必然產物。
隨着移動端複雜程度的慢慢增加,一旦我們的應用被髮布到各大平臺上面之後修復bug是一件很麻煩的事情,
如果要重新發布審覈週期之長,用戶肯定不接受,雖然也可以在應用中自檢更新,但是一個小小的bug動輒
就更新應用實在是大材小用,但是不更新用戶怎麼辦?這時熱修復技術的推出很大程度上改善了這一局面。
熱修復在近年來飛速發展,尤其在Instant Run方案推出後,各種熱修復技術百花盛開,國內大部
份成熟的APP都擁有自己的熱修復技術,example:微信,QQ,美團,餓了麼,支付寶,淘寶等。一個好的熱
修復,將使我們的APP助力百倍。
1.2.1 android studio集成方式
gradle遠程倉庫依賴, 打開項目找到app的build.gradle文件,添加如下配置:
添加maven倉庫地址:
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
}
添加gradle座標版本依賴:
compile 'com.aliyun.ams:alicloud-android-hotfix:3.0.5'
傳遞性依賴utdid這個sdk, 所以不需要重複依賴utdid.但是另一方面其它阿里系SDK也可能依賴了utdid這個SDK, 如果編譯期間報utdid重複, 所以此時進行如下處理即可, 關閉傳遞性依賴:
compile ('com.aliyun.ams:alicloud-android-hotfix:3.0.5') {
exclude(module:'alicloud-android-utdid')
}
1.2.3 權限說明
Sophix SDK使用到以下權限
<! -- 網絡權限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<! -- 外部存儲讀權限,調試工具加載本地補丁需要 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
READ_EXTERNAL_STORAGE/ACCESS_WIFI_STATE
權限屬於Dangerous
Permissions,自行做好android6.0以上的運行時權限獲取
1.2.4 配置AndroidManifest文件
在AndroidManifest.xml
中間的application
節點下添加如下配置:
<meta-data
android:name="com.taobao.android.hotfix.IDSECRET"
android:value="App ID" />
<meta-data
android:name="com.taobao.android.hotfix.APPSECRET"
android:value="App Secret" />
<meta-data
android:name="com.taobao.android.hotfix.RSASECRET"
android:value="RSA密鑰" />
將上述value中的值分別改爲通過平臺HotFix服務申請得到的App Secret和RSA密鑰。
1.2.5 混淆配置
#基線包使用,生成mapping.txt
-printmapping mapping.txt
#生成的mapping.txt在app/buidl/outputs/mapping/release路徑下,移動到/app路徑下
#修復後的項目使用,保證混淆結果一致
#-applymapping mapping.txt
#hotfix
-keep class com.taobao.sophix.**{*;}
-keep class com.ta.utdid2.device.**{*;}
#防止inline
-dontoptimize
1.3 SDK接口使用說明
1.3.1 接入範例
initialize的調用應該儘可能的早,必須在Application.attachBaseContext()
或者Application.onCreate()
的最開始進行SDK初始化操作,否則極有可能導致崩潰。而查詢服務器是否有可用補丁的操作可以在後面的任意地方。
// initialize最好放在attachBaseContext最前面
SophixManager.getInstance().setContext(this)
.setAppVersion(appVersion)
.setAesKey(null)
.setEnableDebug(true)
.setPatchLoadStatusStub(new PatchLoadStatusListener() {
@Override
public void onLoad(final int mode, final int code, final String info, final int handlePatchVersion) {
// 補丁加載回調通知
if (code == PatchStatus.CODE_LOAD_SUCCESS) {
// 表明補丁加載成功
} else if (code == PatchStatus.CODE_LOAD_RELAUNCH) {
// 表明新補丁生效需要重啓. 開發者可提示用戶或者強制重啓;
// 建議: 用戶可以監聽進入後臺事件, 然後應用自殺
} else if (code == PatchStatus.CODE_LOAD_FAIL) {
// 內部引擎異常, 推薦此時清空本地補丁, 防止失敗補丁重複加載
// SophixManager.getInstance().cleanPatches();
} else {
// 其它錯誤信息, 查看PatchStatus類說明
}
}
}).initialize();
// queryAndLoadNewPatch不可放在attachBaseContext 中,否則無網絡權限,建議放在後面任意時刻,如onCreate中
SophixManager.getInstance().queryAndLoadNewPatch();
1.3.2 接口說明
1.3.2.1 initialize方法
-
initialize(): <必選>
該方法主要做些必要的初始化工作以及如果本地有補丁的話會加載補丁, 但不會自動請求補丁。因此需要自行調用queryAndLoadNewPatch方法拉取補丁。這個方法調用需要儘可能的早, 推薦在Application的onCreate方法中調用, initialize()方法調用之前你需要先調用如下幾個方法, 方法調用說明如下:
-
setContext(this): <必選> Application上下文context
-
setAppVersion(appVersion): <必選> 應用的版本號
-
setEnableDebug(true/false): <可選> 默認爲false, 是否調試模式, 調試模式下會輸出日誌以及不進行補丁簽名校驗. 線下調試此參數可以設置爲true, 查看日誌過濾TAG:Sophix, 同時強制不對補丁進行簽名校驗, 所有就算補丁未簽名或者簽名失敗也發現可以加載成功. 但是正式發佈該參數必須爲false, false會對補丁做簽名校驗, 否則就可能存在安全漏洞風險
-
setEnableFixWhenJit(): <可選> 默認情況下會在Android N以後的版本發現jit後跳過,這會使得部分7.0以上設備不進行修復。而如果想要此時不跳過,需要打開這個選項進行配置。打開後,需要做對Application進行改造。要儘可能避免Application類與和它同包名的類互相訪問,如果確實需要訪問,接口應設爲public權限,詳見常見問題文檔,也可尋求羣裏技術支持解決。Android 7.0後帶來的jit問題很隱蔽,只有頻繁使用的app會由系統進行jit,該接口可以徹底解決Android N帶來的jit問題。
-
setAesKey(aesKey): <可選> 用戶自定義aes祕鑰, 會對補丁包採用對稱加密。這個參數值必須是16位數字或字母的組合,是和補丁工具設置裏面AES Key保持完全一致, 補丁才能正確被解密進而加載。此時平臺無感知這個祕鑰, 所以不用擔心阿里雲移動平臺會利用你們的補丁做一些非法的事情。
-
setPatchLoadStatusStub(new PatchLoadStatusListener()): <可選> 設置patch加載狀態監聽器, 該方法參數需要實現PatchLoadStatusListener接口, 接口說明見1.3.2.2說明
-
setUnsupportedModel(modelName, sdkVersionInt):<可選> 把不支持的設備加入黑名單,加入後不會進行熱修復。modelName爲該機型上Build.MODEL的值,這個值也可以通過adb shell getprop | grep ro.product.model取得。sdkVersionInt就是該機型的Android版本,也就是Build.VERSION.SDK_INT,若設爲0,則對應該機型所有安卓版本。
1.3.2.2 queryAndLoadNewPatch方法
該方法主要用於查詢服務器是否有新的可用補丁. SDK內部限制連續兩次queryAndLoadNewPatch()方法調用不能短於3s, 否則的話就會報code:19的錯誤碼. 如果查詢到可用的話, 首先下載補丁到本地, 然後
- 應用原本沒有補丁, 那麼如果當前應用的補丁是熱補丁, 那麼會立刻加載(不管是冷補丁還是熱補丁). 如果當前應用的補丁是冷補丁, 那麼需要重啓生效.
-
應用已經存在一個補丁, 請求發現有新補丁後,本次不受影響。並且在下次啓動時補丁文件刪除, 下載並預加載新補丁。在下下次啓動時應用新補丁。
補丁在後臺發佈之後, 並不會主動下行推送到客戶端, 需要手動調用queryAndLoadNewPatch方法查詢後臺補丁是否可用.
- 只會下載補丁版本號比當前應用存在的補丁版本號高的補丁, 比如當前應用已經下載了補丁版本號爲5的補丁, 那麼只有後臺發佈的補丁版本號>5纔會重新下載.
同時1.4.0以上版本服務後臺上線了“一鍵清除”補丁的功能, 所以如果後臺點擊了“一鍵清除”那麼這個方法將會返回code:18的狀態碼. 此時本地補丁將會被強制清除, 同時不清除本地補丁版本號
1.3.2.3 killProcessSafely方法
可以在PatchLoadStatusListener監聽到CODE_LOAD_RELAUNCH後在合適的時機,調用此方法殺死進程。注意,不可以直接Process.killProcess(Process.myPid())來殺進程,這樣會擾亂Sophix的內部狀態。因此如果需要殺死進程,建議使用這個方法,它在內部做一些適當處理後才殺死本進程。
1.3.2.4 cleanPatches()方法
清空本地補丁,並且不再拉取被清空的版本的補丁。
1.3.2.5 PatchLoadStatusListener接口
該接口需要自行實現並傳入initialize方法中, 補丁加載狀態會回調給該接口, 參數說明如下:
- mode: 補丁模式, 0:正常請求模式 1:掃碼模式 2:本地補丁模式
- code: 補丁加載狀態碼, 詳情查看PatchStatusCode類說明
- info: 補丁加載詳細說明, 詳情查看PatchStatusCode類說明
- handlePatchVersion: 當前處理的補丁版本號, 0:無 -1:本地補丁 其它:後臺補丁
這裏列舉幾個常見的code碼說明,
- code: 1 補丁加載成功
- code: 6 服務端沒有最新可用的補丁
- code: 11 RSASECRET錯誤,官網中的密鑰是否正確請檢查
- code: 12 當前應用已經存在一箇舊補丁, 應用重啓嘗試加載新補丁
- code: 13 補丁加載失敗, 導致的原因很多種, 比如UnsatisfiedLinkError等異常, 此時應該嚴格檢查logcat異常日誌
- code: 16 APPSECRET錯誤,官網中的密鑰是否正確請檢查
- code: 18 一鍵清除補丁
- code: 19 連續兩次queryAndLoadNewPatch()方法調用不能短於3s