日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

热修复框架Tinker的从0到集成之路(转)

發布時間:2025/7/25 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 热修复框架Tinker的从0到集成之路(转) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉自:http://blog.csdn.net/lisdye2/article/details/54411727

熱修復框架Tinker的從0到集成之路

轉載請標明出處:?
http://blog.csdn.net/lisdye2/article/details/54411727?
本文出自:【Alex_MaHao的博客】?
項目中的源碼已經共享到github,有需要者請移步【Alex_MaHao的github】

2015年10月,QQ空間提出了熱修復方案,一時間熱修復風靡Android界,阿里的AndFix,360的插件形式,開源的NUWA等,都分別提出了自己的熱修復實現方案,2016年微信推出了自己的熱修復框架Tinker,從原理上來說,Tinker的實現方式和QQ空間的一脈相承,隨著時間的推移,Tinker逐漸的成熟并推出了一鍵集成的SDK,感覺應該比較是靠譜了,所以就動手集成一下吧。集成比較方便,甚至推出了方便集成的SDK版本,不過需要money,所以我們還是從github上入手吧。

推薦大家看一下Tinker的Wiki,一些細節配置上比較清晰,本篇會忽略這些細節的配置

大致原理

為什么說大致原理呢,因為我也不知道他的原理,不過是在使用中猜的,如果錯了,別怪我~~~。

  • 利用MulitDex打出多個dex文件。?
    • 主dex中沒有邏輯,只是Tinker的所有邏輯,不能更新。
    • 其余dex文件保存我們編寫的邏輯,主要用于更新方便。
  • 保存上次打包的記錄,便于打補丁包。
  • 根據上一次的記錄,一一對比,找出區別之后整合,加簽,打包,生成補丁文件。
  • 將補丁文件放到對應位置,驗簽,加載到內存。(QQ控件熱修復原理加載到內存)

具體原理可以看我之前的熱修復文章或者百度一下,本篇只說集成。

Gradle 配置

工程的根目錄的gradle.properties文件中添加Tinker的版本號

org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 TINKER_VERSION=1.7.6
  • 1
  • 2
  • 3
  • 4
  • 5

工程根目錄下的build.gradle中添加Tinker的編譯插件

dependencies {// Tinker 編譯的插件classpath 'com.tencent.tinker:tinker-patch-gradle-plugin:1.7.6'classpath 'com.android.tools.build:gradle:2.1.0'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

app 目錄下的build.gradle中添加程序運行時的jar包

apply plugin: 'com.android.application'android {compileSdkVersion 23buildToolsVersion "23.0.2"defaultConfig {applicationId "com.alex.tinkerdemo"minSdkVersion 15 targetSdkVersion 21 versionCode 1 versionName "1.0" // tinker 基本配置 multiDexEnabled true buildConfigField "String", "MESSAGE", "\"I am the base apk\"" buildConfigField "String", "TINKER_ID", "\"${getTinkerIdValue()}\"" buildConfigField "String", "PLATFORM", "\"all\"" } // Tinker 推薦設置 dexOptions { jumboMode = true } // 簽名信息的配置 signingConfigs { release { try { storeFile file("./keystore/key.jks") storePassword "123456" keyAlias "1111" keyPassword "1234567" } catch (ex) { throw new InvalidUserDataException(ex.toString()) } } } // 編譯類型的配置 buildTypes { release { minifyEnabled true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { debuggable true minifyEnabled false signingConfig signingConfigs.debug } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:24.2.1' // 多dex 打包的類庫 compile "com.android.support:multidex:1.0.1" // Tinker 基本類庫 compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true } // 編譯時生成Application provided("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

在build.gradle中,關鍵是依賴的添加,分別三個依賴,第一個是MultiDex分包使用的依賴,第二個是基本功能的類庫,第三個是重點。

Tinker不推薦我們自己實現Application,而是由Tinker自己生成,那么我們通常開發時都會自己定義一個Application類,進行一些App的初始化操作,怎么解決呢?Tinker推薦了另一種實現的方式,后面再說,而第三個依賴便是方便實現自定義Application所推薦的類庫。

如上,就是基本的環境配置,當然還有Tinker的配置,這里我們直接采用Tinker提供的基本配置,具體配置的細節可以看wiki。

在app的build.gradle最后,添加如下配置

//=======================Tinker 配置=======================================def gitSha() {try {// String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim()String gitRev = "1008611" if (gitRev == null) { throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'") } return gitRev } catch (Exception e) { throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'") } } // 保存打包old apk 的地址,便于生成補丁包時的對比 def bakPath = file("${buildDir}/bakApk/") ext { //是否打開tinker的功能。 tinkerEnabled = true // old apk地址 tinkerOldApkPath = "${bakPath}/app-release-16-13.apk" //old apk 混淆文件地址 tinkerApplyMappingPath = "${bakPath}/app-release-16-13-mapping.txt" //old apk R 文件地址 tinkerApplyResourcePath = "${bakPath}/app-release-16-13-R.txt" // 多渠道打包相關 tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47" } 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 } if (buildWithTinker()) { apply plugin: 'com.tencent.tinker.patch' tinkerPatch { /** * necessary,default 'null' * the old apk path, use to diff with the new apk to build * add apk from the build/bakApk */ oldApk = getOldApkPath() /** * optional,default 'false' * there are some cases we may get some warnings * if ignoreWarning is true, we would just assert the patch process * case 1: minSdkVersion is below 14, but you are using dexMode with raw. * it must be crash when load. * case 2: newly added Android Component in AndroidManifest.xml, * it must be crash when load. * case 3: loader classes in dex.loader{} are not keep in the main dex, * it must be let tinker not work. * case 4: loader classes in dex.loader{} changes, * loader classes is ues to load patch dex. it is useless to change them. * it won't crash, but these changes can't effect. you may ignore it * case 5: resources.arsc has changed, but we don't use applyResourceMapping to build */ ignoreWarning = false /** * 保證簽名的唯一性 */ useSign = true /** * optional,default 'true' * whether use tinker to build */ tinkerEnable = buildWithTinker() /** * 編譯相關配置 */ buildConfig { /** * 新的apk使用舊的Map 文件,減少補丁包大小 */ applyMapping = getApplyMappingPath() /** * 同上所述,相同的R文件,減少補丁包大小 */ applyResourceMapping = getApplyResourceMappingPath() /** * 補丁的id標識,補丁包的tinkerId和apk的tinkerId相同才能加載補丁 */ tinkerId = getTinkerIdValue() /** * 打開keepDexApply模式,補丁包將根據基準包的類分布來編譯。 */ keepDexApply = false } dex { /** * 'raw'模式,將會保持輸入dex的格式。 * 'jar'模式,我們將會把輸入dex重新壓縮封裝到jar */ dexMode = "jar" /** * 需要處理dex路徑 */ pattern = ["classes*.dex", "assets/secondary-dex-?.jar"] /** * 放在main.dex中的類,這些類不會被加載 */ loader = [ //use sample, let BaseBuildInfo unchangeable with tinker "tinker.sample.android.app.BaseBuildInfo" ] } lib { /** * 需要處理的lib 路徑 */ pattern = ["lib/armeabi/*.so"] } res { /** * 需要處理的資源路徑 */ pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] /** * 忽視改變的文件,即這些文件的改變不會被打到補丁包中 */ ignoreChange = ["assets/sample_meta.txt"] /** *對于修改的資源,如果大于largeModSize,我們將使用bsdiff算法。這可以降低補丁包的大小,但是會增加合成時的復雜度。默認大小為100kb */ largeModSize = 100 } packageConfig { /** * 配置到清單文件的一些字段,沒啥用 */ configField("patchMessage", "tinker is sample to use") /** * 配置到清單文件的一些字段,沒啥用 */ configField("platform", "all") /** * 配置到清單文件的一些字段,沒啥用 */ configField("patchVersion", "1.0") } //or you can add config filed outside, or get meta value from old apk //project.tinkerPatch.packageConfig.configField("test1", project.tinkerPatch.packageConfig.getMetaDataFromOldApk("Test")) //project.tinkerPatch.packageConfig.configField("test2", "sample") /** * if you don't use zipArtifact or path, we just use 7za to try */ sevenZip { /** * zip路徑配置項,執行前提是useSign為true,推薦配置 */ zipArtifact = "com.tencent.mm:SevenZip:1.1.10" } } List<String> flavors = new ArrayList<>(); project.android.productFlavors.each {flavor -> flavors.add(flavor.name) } boolean hasFlavors = flavors.size() > 0 /** * bak apk and mapping */ android.applicationVariants.all { variant -> /** * task type, you want to bak */ def taskName = variant.name //def date = new Date().format("MMdd-HH-mm-ss") def date = new Date().format("mm-ss") tasks.all { if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) { it.doLast { copy { def fileNamePrefix = "${project.name}-${variant.baseName}" def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}" def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath from variant.outputs.outputFile into destPath rename { String fileName -> fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk") } from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt" into destPath rename { String fileName -> fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt") } from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt" into destPath rename { String fileName -> fileName.replace("R.txt", "${newFileNamePrefix}-R.txt") } } } } } } project.afterEvaluate { //sample use for build all flavor for one time if (hasFlavors) { task(tinkerPatchAllFlavorRelease) { group = 'tinker' def originOldPath = getTinkerBuildFlavorDirectory() for (String flavor : flavors) { def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release") dependsOn tinkerTask def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest") preAssembleTask.doFirst { String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15) project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk" project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt" project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt" } } } task(tinkerPatchAllFlavorDebug) { group = 'tinker' def originOldPath = getTinkerBuildFlavorDirectory() for (String flavor : flavors) { def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug") dependsOn tinkerTask def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest") preAssembleTask.doFirst { String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13) project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk" project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt" project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt" } } } } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287

在一些基本的配置上都有注釋,方便大家理解。

Tinker的編譯配置就到此為止,下面便是代碼的配置。

代碼編寫

因為需要的代碼不少,這里不在一一講解,只標明主要的邏輯代碼。具體代碼可以去github上下載。

首先看一下整個類的結構

  • app:這個我也不知道是干什么的,我感覺沒什么用,自己可是刪掉試試。

  • crash:崩潰保護。

  • Log:修復過程中的日志打印。

  • reporter:補丁過程中的一些流程的回調。

  • service:修復成功以及耗時等回調

  • util?: 工具類,關鍵是TinkerManager。

Application編寫

Tinker不推薦編寫自定義Application,我們在自定義Application的邏輯,不在繼承Application,而繼承由Tinker提供的DefaultApplicationLike類,提供好編寫的模板如下:

@SuppressWarnings("unused") @DefaultLifeCycle( application = "com.alex.tinkerdemo.TinkerApp",// 自定義生成 flags = ShareConstants.TINKER_ENABLE_ALL, loadVerifyFlag = false) public class App extends DefaultApplicationLike { public static App sApp; public App(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(); sApp = this; /*JPushInterface.setDebugMode(true); JPushInterface.init(getApplication()); // 友盟統計日志加密 AnalyticsConfig.enableEncrypt(true);*/ } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override public void onBaseContextAttached(Context base) { super.onBaseContextAttached(base); // 其原理是分包架構,所以在加載初要加載其余的分包 MultiDex.install(base); // Tinker管理類,保存當前對象 TinkerManager.setTinkerApplicationLike(this); // 崩潰保護 TinkerManager.initFastCrashProtect(); // 是否重試 TinkerManager.setUpgradeRetryEnable(true); //Log 實現,打印加載補丁的信息 TinkerInstaller.setLogIml(new MyLogImp()); // 運行Tinker ,通過Tinker添加一些基本配置 TinkerManager.installTinker(this); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) { // 生命周期,默認配置 getApplication().registerActivityLifecycleCallbacks(callback); } public static App getApp(){ return sApp; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

在類上標有注解@DefaultLifeCycle,該注解是為了定義在編譯是自動生成的Application類的信息,清單文件中的Application注冊需要填此處定義的名字。

onCreate()方法和Applciation的onCreate()類似,在這里做一些初始化操作,例如友盟的初始化等等,不在過多的講解。

其中一個區別便是上下文對象不能夠在使用該對象,而是使用app.getApplication()方法獲取上下文對象。

onBaseContextAttached()和Applcation的方法功能類似,在這里我們做一些基本操作。

  • 加載分包:MultiDex.install(base);
  • 初始化Tinker對象,設置一些信息。

關于Tinker初始化信息,主要用到TinkerManager對象,注釋很詳細,看注釋即可。關鍵方法便是TinkerManager.installTinker(this);,他進行初始化的最終操作。看一下這個方法

/*** you can specify all class you want.* sometimes, you can only install tinker in some process you want!** @param appLike*/public static void installTinker(ApplicationLike appLike) { if (isInstalled) { TinkerLog.w(TAG, "install tinker, but has installed, ignore"); return; } //Tinker在加載補丁時的一些回調,我們實現對應方法獲取回調 LoadReporter loadReporter = new SampleLoadReporter(appLike.getApplication()); //Tinker在修復或者升級補丁時的一些回調 PatchReporter patchReporter = new SamplePatchReporter(appLike.getApplication()); //用來過濾Tinker收到的補丁包的修復、升級請求 PatchListener patchListener = new SamplePatchListener(appLike.getApplication()); // 補丁包的核心處理類 AbstractPatch upgradePatchProcessor = new UpgradePatch(); TinkerInstaller.install(appLike, loadReporter, patchReporter, patchListener, SampleResultService.class, upgradePatchProcessor); isInstalled = true; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

注釋很清晰,自己理解吧~~。

加載補丁包

MainActivity中的代碼

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 加載補丁包 TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), "/sdcard/mdtinker.apk"); } /** * =====================Tinker 默認配置======================== */ protected void onResume() { super.onResume(); Utils.setBackground(false); } @Override protected void onPause() { super.onPause(); Utils.setBackground(true); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

關鍵性便是加載補丁包,這里為了省事直接加載,如果內存理沒有補丁,直接會返回了,程序不會崩潰。

開始操作

Debug的不在演示,直接按照Release的方式演示。

  • 正常打出簽名包

先按照正常流程打包簽名APK,此時將簽名文件安裝到手機中。

會在上圖目錄下多出一些文件,這些文件保存好,因為生成補丁的時候需要這個東西。

  • 代碼隨便改出一些不同

  • 修改生成補丁的配置文件

隨便修改一些文件之后,打開app下的build.gradle文件,修改如下文件配置為我們的文件地址

ext {//是否打開tinker的功能。tinkerEnabled = true// old apk地址tinkerOldApkPath = "${bakPath}/app-release-27-43.apk"//old apk 混淆文件地址 tinkerApplyMappingPath = "${bakPath}/app-release-27-43-mapping.txt" //old apk R 文件地址 tinkerApplyResourcePath = "${bakPath}/app-release-27-43-R.txt" // 多渠道打包相關 tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47" }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 運行補丁命令

  • 獲取補丁文件,導入到手機文件對應目錄

注意,其中紅色框處圈出的就是最終的補丁包,將他改成mdtinker.apk導入到手機對應存儲。

  • 之后打開app會提示補丁加載成功,后臺殺死進程再次打開,補丁就成功加載了。

補丁第一次加載之后,會刪除補丁包,之后每次加載,都是已經修改過后,打過補丁的apk。

附加

// 清除補丁包方法//Tinker.with(getApplicationContext()).cleanPatch();

轉載于:https://www.cnblogs.com/weizhxa/p/7735218.html

總結

以上是生活随笔為你收集整理的热修复框架Tinker的从0到集成之路(转)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。