熱修復技術就是不重新啓動APP和用戶無感知的情況下,對應用進行增量的補丁更新。好處就不說了,好處太多了。即便有些功能無法做到不重啓應用,但是這種增量只更新補丁包的方式還是很贊,只需要應用冷啓動一下就自動完成了更新包的加載。至少不用再升級發佈一次了。
Android的熱修復框架有很多。如阿里的andfix,hotfix,微信的tinker和美團的Robust等。
要說哪個最簡單最好用,阿里的Hotfix簡直是傻瓜式接入,文檔和教程也很詳細,連打包工具都給你可視化了。還配備了開發提調試工具,讓你本機就能驗證下補丁包。不得不服阿里系對開發者也考慮的真周到啊。
以下以AndroidStudio3.2的集成爲例:
阿里熱修復Sophix已全面升級至3.0,可以用原先的-百川賬號-直接登錄阿里雲移動熱修復
Sophix即阿里的hotfix,最新版改名字了,改爲Sophix了。
Sophix提供了一套更加完美的客戶端服務端一體的熱更新方案,做到了圖形界面一鍵打包、加密傳輸、簽名校驗和服務端控制發佈與灰度功能,讓你用最少的時間實現最強大可靠的全方位熱更新。
新版本的功能更強大,更好用。以下介紹下最新版本3.2的接入方式:
android studio集成方式也很簡單:
在項目的gradle文件裏,添加emas-services插件和maven倉庫地址。
例如:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
maven {
url 'http://maven.aliyun.com/nexus/content/repositories/releases/'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
// 添加emas-services插件
classpath 'com.aliyun.ams:emas-services:1.0.1'
//classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.5'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
maven {
url 'http://maven.aliyun.com/nexus/content/repositories/releases/'
}
flatDir {
dirs 'libs'
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
如:
然後在App目錄的gradle文件裏,添加依賴和插件:
apply plugin: 'com.aliyun.ams.emas-services'
//阿里hotfix熱補丁更新
implementation 'com.aliyun.ams:alicloud-android-hotfix:3.2.12'
例如:
apply plugin: 'com.android.application'
apply plugin: 'com.aliyun.ams.emas-services'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.xxx.xxxx"
minSdkVersion 22
//noinspection ExpiredTargetSdkVersion
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
java {
srcDir 'src/main/java'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
ext {
rxjavaVersion = '2.0.1'
}
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//RxJava
implementation "io.reactivex.rxjava2:rxjava:$rxjavaVersion"
implementation "io.reactivex.rxjava2:rxandroid:$rxjavaVersion"
//json
implementation 'com.google.code.gson:gson:2.8.0'
api 'com.alibaba:fastjson:1.2.31'
//日誌框架 自動寫入文件
implementation 'com.github.hijesse:android-logger:2.0.0'
//protobuf
// implementation "com.google.protobuf:protobuf-java:3.1.0"
// implementation ('com.squareup.retrofit2:converter-protobuf:2.2.0') {
// exclude group: 'com.google.protobuf', module: 'protobuf-java'
// }
//屏幕自動適配
//implementation 'me.yatoooon:screenadaptation:1.0.3'
//sys
implementation(name: 'b601sys', ext: 'aar')
implementation files('libs/b601auxlib.jar')
//阿里hotfix熱補丁更新
implementation 'com.aliyun.ams:alicloud-android-hotfix:3.2.12'
}
//構建task
//protobuf {
// protoc {
// artifact = 'com.google.protobuf:protoc:3.1.0'
// }
//
// generateProtoTasks {
// all().each { task ->
// task.builtins {
// remove java
// }
// task.builtins {
// java {}
// // Add cpp output without any option.
// // DO NOT omit the braces if you want this builtin to be added.
// }
// }
// }
// //生成目錄
// generatedFilesBaseDir = "$projectDir/src/generated"
//}
以上完成了gradle的配置。gradle就這些了。
接着是AndroidManifest.xml文件中,增加幾個meta屬性和指定初始的Application.
在Application的標籤下增加以下屬性:
<meta-data
android:name="com.taobao.android.hotfix.IDSECRET"
android:value="AppID" />
<meta-data
android:name="com.taobao.android.hotfix.APPSECRET"
android:value="AppSecret" />
<meta-data
android:name="com.taobao.android.hotfix.RSASECRET"
android:value="RSAkey" />
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權限屬於Dangerous Permissions,僅調試工具獲取外部補丁需要,不影響線上發佈的補丁加載,調試時請自行做好android6.0以上的運行時權限獲取。
然後指定初始的Application爲阿里hotfix指定的入口類:
這個SophixStubApplication類,直接拷貝進去即可。幾乎零改動。這裏可以看出來,阿里的hotfix對你的代碼幾乎是零侵入的。
這個類加進來,裏面只改一個地方,把你的applicatin類指定進去即可,就完成了所有的集成了。
注意混淆配置,如果代碼裏啓用的有混淆。需配置下混淆配置。混淆的配置,參照最新文檔。
需要注意的是,這個類裏面的
setSecretMetaData爲設置AndroidManifest.xml中的那幾項保密的參數的。
分別是idSecret, appSecret, rsaSecret。
這三個參數從哪來呢?需要你註冊並登陸上阿里雲。從上面創建應用,會給你分配的。記下來即可,保存好。
應用版本要和app的版本一致。
這個雖說是收費的,但是每個賬號每個月可以有5萬臺設備接入的免費使用額度,超過了才收費的。
完成了以上這些,算是集成和接入成功了。接下來說下如何使用吧,來驗證下熱更新的功能。
patch補丁包生成需要使用到打補丁工具SophixPatchTool, 如還未下載打包工具,請前往下載Android打包工具。
下載打包工具:
patch補丁包生成需要使用到打補丁工具SophixPatchTool, 如還未下載打包工具,請前往下載Android打包工具。
Mac版本打包工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_macos.zip
Windows版本打包工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_windows.zip
Linux版本打包工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_linux.zip
調試工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/hotfix_debug_tool-release.apk
調試工具也是有的,調試工具是可以把你的補丁包以本地的方式,或掃碼下載的方式下載到你的應用所在的機器上,然後本地驗證。
調試工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/hotfix_debug_tool-release.apk
下載下來安裝到你的應用需要運行的設備上。
打包工具的使用還是很簡單的,如下圖,那麼多key都咋填?如果你的應用只是debug模式,沒簽名,那麼那幾項可忽略。
如果你的應用簽名了,那麼那些key你自然是知道的。
點擊生成,即生成了補丁包,補丁包不大,跟你的改動有關係吧。
我這個生成的sophix-patch.jar有9k。即便改動了一行代碼也可能有這麼大。因爲有一些其它信心包含在內的。
補丁生成了,接下來驗證一下吧,可以先不用把補丁包上送到阿里雲,用那個調試工具就能驗證。
先用adb push sophix-patch.jar /sdcard/ 把補丁包傳到設備上去。
然後在設備上打開那個調試工具:
補丁工具生成的補丁包sophix-patch.jar推送到本地的/sdcard/Download目錄, 然後輸入該補丁包的絕對路徑, 點擊應用本地補丁按鈕;
Mode:2 表示本地補丁模式, Code:1 表示加載成功 HandlePatchVersion:-1 表示本地補
掃描二維碼示例
補丁工具生成的補丁包sophix-patch.jar上傳到hotfix控制檯;
點擊掃描二維碼按鈕掃後臺補丁二維碼;
Mode:1 表示掃碼模式, Code:12 表示應用當前已經有一個補丁, 所以新補丁不會立刻加載需要等下一次重啓加載, HandlePatchVersion:89 表示後臺拉取下來的補丁版本89。
注意的是,調試工具用於patch正式發佈前的調試環節,目前有兩種測試方式:
掃碼二維碼方式,將剛剛上傳到後臺的補丁通過掃描二維碼下載到本地,嘗試加載補丁;
應用本地補丁方式,傳入本地補丁的絕對路徑,嘗試加載補丁。此方式必須確保Sophix初始化時setEnableDebug爲true。出於安全考慮,我們禁止在setEnableDebug爲false的包上加載本地補丁,因此setEnableDebug爲false的包需要以二維碼方式進行驗證。
最後說下接口說明
initialize方法
initialize(): <必選>
該方法主要做些必要的初始化工作以及如果本地有補丁的話會加載補丁, 但不會自動請求補丁。因此需要自行調用queryAndLoadNewPatch方法拉取補丁。這個方法調用需要儘可能的早, 必須在Application的attachBaseContext方法的最前面調用(在super.attachBaseContext之後,如果有Multidex,也需要在Multidex.install之後), initialize()方法調用之前你需要先調用如下幾個方法進行一些必要的參數設置, 方法調用說明如下:
setContext(application): <必選>
傳入入口Application即可
setAppVersion(appVersion): <必選>
應用的版本號和創建補丁的版本號要一致,不然補丁沒有用
setSecretMetaData(idSecret, appSecret, rsaSecret): <可選,推薦使用>
三個Secret分別對應AndroidManifest裏面的三個,可以不在AndroidManifest設置而是用此函數來設置Secret。放到代碼裏面進行設置可以自定義混淆代碼,更加安全,此函數的設置會覆蓋AndroidManifest裏面的設置,如果對應的值設爲null,默認會在使用AndroidManifest裏面的。
setEnableDebug(isEnabled): <可選>
isEnabled默認爲false, 是否調試模式, 調試模式下會輸出日誌以及不進行補丁簽名校驗. 線下調試此參數可以設置爲true, 查看日誌過濾TAG:Sophix, 同時強制不對補丁進行簽名校驗, 所有就算補丁未簽名或者簽名失敗也發現可以加載成功. 但是正式發佈該參數必須爲false, false會對補丁做簽名校驗, 否則就可能存在安全漏洞風險
setAesKey(aesKey): <可選>
用戶自定義aes祕鑰, 會對補丁包採用對稱加密。這個參數值必須是16位數字或字母的組合,是和補丁工具設置裏面AES Key保持完全一致, 補丁才能正確被解密進而加載。此時平臺無感知這個祕鑰, 所以不用擔心阿里雲移動平臺會利用你們的補丁做一些非法的事情。
setPatchLoadStatusStub(new PatchLoadStatusListener()): <可選>
設置patch加載狀態監聽器, 該方法參數需要實現PatchLoadStatusListener接口, 接口說明見1.3.2.說明
setUnsupportedModel(modelName, sdkVersionInt):<可選>
把不支持的設備加入黑名單,加入後不會進行熱修復。modelName爲該機型上Build.MODEL的值,這個值也可以通過adb shell getprop | grep ro.product.model取得。sdkVersionInt就是該機型的Android版本,也就是Build.VERSION.SDK_INT,若設爲0,則對應該機型所有安卓版本。目前控制檯也可以直接設置機型黑名單,更加靈活。
queryAndLoadNewPatch方法
該方法主要用於查詢服務器是否有新的可用補丁. SDK內部限制連續兩次queryAndLoadNewPatch()方法調用不能短於3s, 否則的話就會報code:19的錯誤碼. 如果查詢到可用的話, 首先下載補丁到本地, 然後應用原本沒有補丁, 那麼如果當前應用的補丁是熱補丁, 那麼會立刻加載(不管是冷補丁還是熱補丁). 如果當前應用的補丁是冷補丁, 那麼需要重啓生效。
應用原本沒有補丁, 那麼如果當前應用的補丁是熱補丁, 那麼會立刻加載(不管是冷補丁還是熱補丁). 如果當前應用的補丁是冷補丁, 那麼需要重啓生效。
應用已經存在一個補丁, 請求發現有新補丁後,本次不受影響。並且在下次啓動時補丁文件刪除, 下載並預加載新補丁。在下下次啓動時應用新補丁。
補丁在後臺發佈之後, 並不會主動下行推送到客戶端, 需要手動調用queryAndLoadNewPatch方法查詢後臺補丁是否可用.
只會下載補丁版本號比當前應用存在的補丁版本號高的補丁, 比如當前應用已經下載了補丁版本號爲5的補丁, 那麼只有後臺發佈的補丁版本號>5纔會重新下載.
同時1.4.0以上版本服務後臺上線了“一鍵清除”補丁的功能, 所以如果後臺點擊了“一鍵清除”那麼這個方法將會返回code:18的狀態碼. 此時本地補丁將會被強制清除, 同時不清除本地補丁版本號。
killProcessSafely方法
可以在PatchLoadStatusListener監聽到CODE_LOAD_RELAUNCH後在合適的時機,調用此方法殺死進程。注意,不可以直接Process.killProcess(Process.myPid())來殺進程,這樣會擾亂Sophix的內部狀態。因此如果需要殺死進程,建議使用這個方法,它在內部做一些適當處理後才殺死本進程。
cleanPatches()方法
清空本地補丁,並且不再拉取被清空的版本的補丁。正常情況下不需要開發者自己調用,因爲Sophix內部會判斷對補丁引發崩潰的情況進行自動清空。
PatchLoadStatusListener接口
該接口需要自行實現並傳入initialize方法中, 補丁加載狀態會回調給該接口, 參數說明如下:
mode: 無實際意義, 爲了兼容老版本, 默認始終爲0
code: 補丁加載狀態碼, 詳情查看PatchStatus類說明
info: 補丁加載詳細說明
handlePatchVersion: 當前處理的補丁版本號, 0:無 -1:本地補丁 其它:後臺補丁
更詳細的參見博文:https://blog.csdn.net/ffhelly/article/details/80696202
https://www.jianshu.com/p/6ae1e09ebbf5
https://www.cnblogs.com/popfisher/p/8543973.html
以上就是對阿里的hotfix的試用。接下來會再試用下美團的Robust。據說也是很不錯,開源免費。
Robust優缺點:
優點
- 高兼容性(Robust只是在正常的使用DexClassLoader)、高穩定性,修復成功率高達99.9%
- 補丁實時生效,不需要重新啓動
- 支持方法級別的修復,包括靜態方法
- 支持增加方法和類
- 支持ProGuard的混淆、內聯、優化等操作
缺點
- 代碼是侵入式的,會在原有的類中加入相關代碼
- so和資源的替換暫時不支持
- 會增大apk的體積,平均一個函數會比原來增加17.47個字節,10萬個函數會增加1.67M
摘錄一篇網絡總結,鏈接地址:https://www.cnblogs.com/popfisher/p/8543973.html
如果不考慮增大apk的體積,只是簡單的修復代碼,不修復so和資源,選擇Robust是最穩定的,否則的話選擇Tinker是一個不錯的方案。雖然阿里Sophix橫空出世,但是它不開源,而且商業收費,所以一般不是很賺錢的app選擇收費的可能就很小了。不過它確實各方面都做了大量的優化,本文中的很多知識點也來源於阿里的《Android熱修復技術原理.pdf》一書,本書值得一讀,裏面就是基於Sophix框架來編排的。
GitHub文章地址
https://www.jianshu.com/p/f2d8f7d11854