公司項目的Bug、Crash統計一直用的的騰訊的 Bugly ,今日發現多了一個熱更新(HotFix)功能,使用的技術是微信的方案 Tinker ,再三考慮後決定接入這個功能。javascript
Tinker是微信官方的Android熱補丁解決方案,它支持動態下發代碼、So庫以及資源,讓應用可以在不須要從新安裝的狀況下實現更新。固然,你也可使用Tinker來更新你的插件。java
Tinker與其餘熱更新插件的比較:
android
其餘關於Tinker的介紹請看Tinker的 wiki。git
咱們選擇Tinker的道理很簡單:穩定、開發透明。
而咱們選擇經過Bugly來接入Tinker,由於它簡單,不須要咱們再提供補丁下載接口和補丁管理後臺。程序員
// tinker gradle插件
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.5')
// tinkersupport插件
classpath "com.tencent.bugly:tinker-support:latest.release"複製代碼
apply plugin: 'com.tencent.bugly.tinker-support'
def bakPath = file("${buildDir}/bakApk/")
ext {
// for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
tinkerEnabled = true
// for normal build
// old apk file to build patch apk
tinkerOldApkPath = "${bakPath}/app-release.apk"
// proguard mapping file to build patch apk
tinkerApplyMappingPath = "${bakPath}/mapping.txt"
// resource R.txt to build patch apk, must input if there is resource changed
tinkerApplyResourcePath = "${bakPath}/R.txt"
// only use for build all flavor, if not, just ignore this field
tinkerBuildFlavorDirectory = "${bakPath}/app-1124-23-03-52"
}
def getOldApkPath() {
return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath
}
def getApplyMappingPath() {
return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath
}
def getApplyResourceMappingPath() {
return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath
}
def getTinkerIdValue() {
return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()
}
def buildWithTinker() {
return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled
}
def getTinkerBuildFlavorDirectory() {
return ext.tinkerBuildFlavorDirectory
}
// 注意:必需要配置tinker-support
tinkerSupport {
enable = true
}
// 全局信息相關配置項
tinkerPatch {
oldApk = getOldApkPath() //打補丁包時必選, 基準包路徑
ignoreWarning = false // 可選,默認false
useSign = true // 可選,默認true, 驗證基準apk和patch簽名是否一致
// 編譯相關配置項
buildConfig {
applyMapping = getApplyMappingPath() // 可選,設置mapping文件,建議保持舊apk的proguard混淆方式
applyResourceMapping = getApplyResourceMappingPath() // 可選,設置R.txt文件,經過舊apk文件保持ResId的分配
tinkerId = "v1.0.0" // 必選,當前版本的惟一標識,能夠是git版本號、versionName
}
// dex相關配置項
dex {
dexMode = "jar" // 可選,默認爲jar
usePreGeneratedPatchDex = false // 可選,默認爲false
pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]
loader = ["com.tencent.tinker.loader.*", // 必選
"com.huidr.patient.HuiTinkerApplication",
]
}
// lib相關的配置項
lib {
pattern = ["lib/armeabi/*.so"]
}
// res相關的配置項
res {
pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
ignoreChange = ["assets/sample_meta.txt"]
largeModSize = 100
}
// 用於生成補丁包中的'package_meta.txt'文件
packageConfig {
configField("patchMessage", "tinker is sample to use")
configField("platform", "all")
configField("patchVersion", "1.0")
}
// 7zip路徑配置項,執行前提是useSign爲true
sevenZip {
zipArtifact = "com.tencent.mm:SevenZip:1.1.10" // optional
}
}複製代碼
咱們以前的自定義Application類如今是不能直接繼承Application了,須要改爲繼承Tinker提供的DefaultApplicationLike類github
/** * Application代理類 */
public class ApplicationDelegate extends DefaultApplicationLike {
public ApplicationDelegate(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent, Resources[] resources, ClassLoader[] classLoader, AssetManager[] assetManager) {
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent, resources, classLoader, assetManager);
}
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.RELEASE) {
Bugly.init(getApplication(), BuildConfig.buglyAppId, false);
} else {
Bugly.init(getApplication(), BuildConfig.buglyAppId, true);
}
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
// you must install multiDex whatever tinker is installed!
MultiDex.install(base);
// 安裝tinker
Beta.installTinker(this);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
getApplication().registerActivityLifecycleCallbacks(callbacks);
}
}複製代碼
這個代理的Application類的時候方法和原來同樣,只不過它的調用是由Tinker來調用了,而這個調用者就是咱們即將要定義的Application。微信
/** * TinkerApplication. * <p> * 注意:這個類集成TinkerApplication類,這裏面不作任何操做,全部Application的代碼都會放到ApplicationLike繼承類當中<br/> * <pre> * 參數解析: * 參數1:int tinkerFlags 表示Tinker支持的類型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL * 參數2:String delegateClassName Application代理類 這裏填寫你自定義的ApplicationLike * 參數3:String loaderClassName Tinker的加載器,使用默認便可 * 參數4:boolean tinkerLoadVerifyFlag 加載dex或者lib是否驗證md5,默認爲false * </pre> */
public class HuiTinkerApplication extends TinkerApplication {
public HuiTinkerApplication() {
super(ShareConstants.TINKER_ENABLE_ALL, "com.huidr.patient.ApplicationDelegate",
"com.tencent.tinker.loader.TinkerLoader", false);
}
}複製代碼
這裏構造方法裏的"com.huidr.patient.ApplicationDelegate"改爲剛剛定義的Application代理類。
同時在AndroidMenifest.xml中把Application改爲新定義的Appcation。
到這裏,配置已經完成了。app
回到項目主module的build.gradle中,找到:ide
// dex相關配置項
dex {
dexMode = "jar" // 可選,默認爲jar
usePreGeneratedPatchDex = false // 可選,默認爲false
pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]
loader = ["com.tencent.tinker.loader.*", // 必選
"com.huidr.patient.HuiTinkerApplication",
]
}複製代碼
將這裏配置的Applicaiton改爲你剛剛建立的。gradle
如今能夠打包出ralease版本,獲得app-release.apk文件,將其運行起來。
如今主要關注這部分配置就能夠了
// 編譯相關配置項
buildConfig {
applyMapping = getApplyMappingPath() // 可選,設置mapping文件,建議保持舊apk的proguard混淆方式
applyResourceMapping = getApplyResourceMappingPath() // 可選,設置R.txt文件,經過舊apk文件保持ResId的分配
tinkerId = "v1.0.0" // 必選,當前版本的惟一標識,能夠是git版本號、versionName
}複製代碼
最後修改tinkerId(當前版本的惟一標識,能夠是git版本號、versionName)
運行後獲得補丁app\build\outputs\patch\release\patch_signed_7zip.apk
這裏最好把patch_signed_7zip.apk文件擴展名改爲.zip,防止有些手機廠商對apk文件劫持。
打開Bugly熱更新後臺界面,選擇發佈補丁,並下發
如今將App退出從新打開2次,就能夠看到更新後的結果了
第一次打開會監測補丁版本;
第二次打開會安裝新補丁。
ps:若是發現補丁有問題,還可使用撤回功能,讓App回覆補丁前的樣子。
感謝在一線上爲熱更新奮戰的程序員,這是一條跪着走完的道路,正是有了他們背後的默默付出,咱們才能這麼方便地用上熱更新功能,致敬!
以上集成過程主要參考如下文檔:
Bugly Android熱更新使用指南
Tinker 接入指南