Tinker 热修复
集成方式:
第一步:在project build.gradle 文件中添加:
dependencies { // Tinker classpath("com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:${TINKERPATCH_VERSION}") { changing = true } }
第二步:在app的 build.gradle 文件中添加
//apply tinker插件 最上面 apply plugin: 'com.tencent.tinker.patch' apply from: 'tinkerpatch.gradle' android { buildTypes { release { minifyEnabled false // proguardFiles getDefaultProguardFile('proguard-android.txt') signingConfig signingConfigs.debug } debug { debuggable true minifyEnabled false signingConfig signingConfigs.debug } } signingConfigs { release { storeFile rootProject.file("keystore/Android.keystore") storePassword 'jjjj' keyAlias 'kkkk' keyPassword 'mmm' } debug { storeFile rootProject.file("keystore/debug.keystore") } lintOptions { abortOnError false } } configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' } dependencies { // 热修复 //无需引入tinker的任何库,使用tinkerpatch sdk即可 implementation("com.tinkerpatch.sdk:tinkerpatch-android-sdk:${TINKERPATCH_VERSION}") { changing = true } implementation 'com.google.guava:guava:19.0' implementation "org.scala-lang:scala-library:2.11.7" implementation 'com.android.support:multidex:1.0.1' }
tinkerpatch.gradle
apply plugin: 'tinkerpatch-support' import java.util.regex.Matcher import java.util.regex.Pattern /** * TODO: 请按自己的需求修改为适应自己工程的参数 */ // 基包路径 def bakPath = file("${buildDir}/bakApk/") // 基准包文件夹(打补丁的时候需要修改) def baseInfo = "app-1.0.0-0214-16-22-58" // 版本名称 def variantName = "debug" /** * 对于插件各参数的详细解析请参考 * http://tinkerpatch.com/Docs/SDK */ tinkerpatchSupport { /** 可以在debug的时候关闭 tinkerPatch, isRelease() 可以判断BuildType是否为Release **/ tinkerEnable = true reflectApplication = true /** * 是否开启加固模式,只能在APK将要进行加固时使用,否则会patch失败。 * 如果只在某个渠道使用了加固,可使用多flavors配置 **/ protectedApp = false /** * 实验功能 * 补丁是否支持新增 Activity (新增Activity的exported属性必须为false) **/ supportComponent = true autoBackupApkPath = "${bakPath}" appKey = "6fa011ee98eb1995" /** 注意: 若发布新的全量包, appVersion一定要更新 **/ appVersion = "1.0.0" def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/" def name = "${project.name}-${variantName}" baseApkFile = "${pathPrefix}/${name}.apk" baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt" baseResourceRFile = "${pathPrefix}/${name}-R.txt" /** * (可选)重命名备份文件的格式化字符串,默认为'${appName}-${variantName}' * * Available vars: * 1. projectName * 2. appName * 3. packageName * 4. buildType * 5. versionName * 6. versionCode * 7. buildTime * 8. fileSHA1 * 9. flavorName * 10. variantName * * default value: '${appName}-${variantName}' * Note: plz use single-quotation wrapping this format string */ backupFileNameFormat = '${appName}-${variantName}' /** * 若有编译多flavors需求, 可以参照: https://github.com/TinkerPatch/tinkerpatch-flavors-sample * 注意: 除非你不同的flavor代码是不一样的,不然建议采用zip comment或者文件方式生成渠道信息(相关工具:walle 或者 packer-ng) **/ } /** * 用于用户在代码中判断tinkerPatch是否被使能 */ android { defaultConfig { buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}" } } /** * 一般来说,我们无需对下面的参数做任何的修改 * 对于各参数的详细介绍请参考: * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97 */ tinkerPatch { ignoreWarning = false useSign = true dex { dexMode = "jar" pattern = ["classes*.dex"] loader = [] } lib { pattern = ["lib/*/*.so"] } res { pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] ignoreChange = [] largeModSize = 100 } packageConfig { } sevenZip { zipArtifact = "com.tencent.mm:SevenZip:1.1.10" // path = "/usr/local/bin/7za" } buildConfig { keepDexApply = false } } import java.util.regex.Matcher import java.util.regex.Pattern /** * 如果只想在Release中打开tinker,可以把tinkerEnable赋值为这个函数的return * @return 是否为release */ def isRelease() { Gradle gradle = getGradle() String tskReqStr = gradle.getStartParameter().getTaskRequests().toString() Pattern pattern; if (tskReqStr.contains("assemble")) { println tskReqStr pattern = Pattern.compile("assemble(\\w*)(Release|Debug)") } else { pattern = Pattern.compile("generate(\\w*)(Release|Debug)") } Matcher matcher = pattern.matcher(tskReqStr) if (matcher.find()) { String task = matcher.group(0).toLowerCase() println("[BuildType] Current task: " + task) return task.contains("release") } else { println "[BuildType] NO MATCH FOUND" return true; } }
注意事项:
appKey = "6fa011ee98eb1995" 为平台注册的key
appVersion = "1.0.0":为基础版本的版本号,补丁的appVersion 必须一致 ,还有就是 Tinker platform 新建的版本号也必须一致,否则无法更新
在application 中初始化:
package com.xiaozhuyisheng.tinkerdemo; import android.app.Application; import android.content.Context; import android.util.Log; import com.tencent.tinker.entry.ApplicationLike; import com.tinkerpatch.sdk.TinkerPatch; import com.tinkerpatch.sdk.loader.TinkerPatchApplicationLike; public class IApp extends Application { public IApp() { } private static final String TAG = "Tinker.SampleApp"; private ApplicationLike tinkerApplicationLike; @Override public void onCreate() { super.onCreate(); initTinkerPatch(); } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); } /** * 我们需要确保至少对主进程跟patch进程初始化 TinkerPatch */ private void initTinkerPatch() { // 我们可以从这里获得Tinker加载过程的信息 if (BuildConfig.TINKER_ENABLE) { tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike(); // 初始化TinkerPatch SDK TinkerPatch.init( tinkerApplicationLike // new TinkerPatch.Builder(tinkerApplicationLike) // .requestLoader(new OkHttp3Loader()) // .build() ) .reflectPatchLibrary() .setPatchRollbackOnScreenOff(true) .setPatchRestartOnSrceenOff(true); // .setFetchPatchIntervalByHours(3) ; // 获取当前的补丁版本 Log.d(TAG, "Current patch version is " + TinkerPatch.with().getPatchVersion()); // fetchPatchUpdateAndPollWithInterval 与 fetchPatchUpdate(false) // 不同的是,会通过handler的方式去轮询 // TinkerPatch.with().fetchPatchUpdateAndPollWithInterval(); new FetchPatchHandler().fetchPatchWithInterval(3); } } }
不要忘记添加以下权限:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
配置为每三个小时轮询一次是否有新版本,为了验证自己添加了一个按钮,手动更新。
package com.xiaozhuyisheng.tinkerdemo; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import com.tencent.tinker.lib.util.TinkerLog; import com.tinkerpatch.sdk.TinkerPatch; import com.tinkerpatch.sdk.server.callback.ConfigRequestCallback; import java.util.HashMap; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btnUpdate).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { TinkerPatch.with().fetchPatchUpdate(true); } }); findViewById(R.id.addNewPage).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { OtherActivity.startPage(MainActivity.this); } }); } }
如何生成基准包:
基准包位置: