tinker 热修复 gradle 接入
}
//recommend
dexOptions {
jumboMode = true
}
//配置自己的签名文件,签名文件的生成和导入可以去百度,本篇不讲解
signingConfigs {
release {
try {
keyAlias 'china'
keyPassword '123456'
storeFile file('D:/work/release.jks')
storePassword '123456'
} catch (ex) {
throw new InvalidUserDataException(ex.toString())
}
}
debug {
storeFile file('D:/work/debug.jks')
keyAlias 'china'
keyPassword '123456'
storePassword '123456'
}
}
defaultConfig {
applicationId "tinker.sample.android"
minSdkVersion 10
targetSdkVersion 22
versionCode 1
versionName "1.0.0"
/**
you can use multiDex and install it in your ApplicationLifeCycle implement
*/
multiDexEnabled true
/**
buildConfig can change during patch!
we can use the newly value when patch
*/
buildConfigField "String", "MESSAGE", ""I am the base apk""
// buildConfigField "String", "MESSAGE", ""I am the patch apk""
/**
client version would update with patch
so we can get the newly git version easily!
*/
buildConfigField "String", "TINKER_ID", ""1.0""
buildConfigField "String", "PLATFORM", ""all""
}
// aaptOptions{
// cruncherEnabled false
// }
// //use to test flavors support
// productFlavors {
// flavor1 {
// applicationId 'tinker.sample.android.flavor1'
// }
//
// flavor2 {
// applicationId 'tinker.sample.android.flavor2'
// }
// }
buildTypes {
release {
minifyEnabled true
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
minifyEnabled false
signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
def bakPath = file("${buildDir}/bakApk/")
/**
you can use assembleRelease to build you base apk
use tinkerPatchRelease -POLD_APK= -PAPPLY_MAPPING= -PAPPLY_RESOURCE= to build patch
add apk from the build/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-0313-16-16-16.apk"
//proguard mapping file to build patch apk
tinkerApplyMappingPath = "${bakPath}/app-release-0313-16-16-mapping.txt"
//resource R.txt to build patch apk, must input if there is resource changed
tinkerApplyResourcePath = "${bakPath}/app-release-0313-16-16-R.txt"
//only use for build all flavor, if not, just ignore this field
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.
case 2: newly added Android Component in AndroidManifest.xml,
case 3: loader classes in dex.loader{} are not keep in the main dex,
rk.
case 4: loader classes in dex.loader{} changes,
case 5: resources.arsc has changed, but we don't use applyResourceMapping to build
*/
ignoreWarning = true
/**
optional,default 'true'
whether sign the patch file
if not, you must do yourself. otherwise it can't check success during the patch loading
we will use the sign config with your build type
*/
useSign = true
/**
optional,default 'true'
whether use tinker to build
*/
tinkerEnable = buildWithTinker()
/**
Warning, applyMapping will affect the normal android build!
*/
buildConfig {
/**
optional,default 'null'
if we use tinkerPatch to build the patch apk, you'd better to apply the old
apk mapping file if minifyEnabled is enable!
Warning:
you must be careful that it will affect the normal assemble build!
*/
applyMapping = getApplyMappingPath()
/**
optional,default 'null'
It is nice to keep the resource id from R.txt file to reduce java changes
*/
applyResourceMapping = getApplyResourceMappingPath()
/**
necessary,default 'null'
because we don't want to check the base apk with md5 in the runtime(it is slow)
tinkerId is use to identify the unique base apk when the patch is tried to apply.
we can use git rev, svn rev or simply versionCode.
we will gen the tinkerId in your manifest automatic
*/
tinkerId = "1.0"/getTinkerIdValue()/
/**
if keepDexApply is true, class in which dex refer to the old apk.
open this can reduce the dex diff file size.
*/
keepDexApply = false
}
dex {
/**
optional,default 'jar'
only can be 'raw' or 'jar'. for raw, we would keep its original format
for jar, we would repack dexes with zip format.
if you want to support below 14, you must use jar
or you want to save rom or check quicker, you can use raw mode also
*/
dexMode = "jar"
/**
necessary,default '[]'
what dexes in apk are expected to deal with tinkerPatch
it support * or ? pattern.
*/
pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]
/**
necessary,default '[]'
Warning, it is very very important, loader classes can't change with patch.
thus, they will be removed from patch dexes.
you must put the following class into main dex.
Simply, you should add your own application {@code tinker.sample.android.SampleApplication}
own tinkerLoader, and the classes you use in them
*/
loader = [
//use sample, let BaseBuildInfo unchangeable with tinker
"tinker.sample.android.app.BaseBuildInfo"
]
}
lib {
/**
optional,default '[]'
what library in apk are expected to deal with tinkerPatch
it support * or ? pattern.
for library in assets, we would just recover them in the patch directory
you can get them in TinkerLoadResult with Tinker
*/
pattern = ["lib//.so"]
}
res {
/**
optional,default '[]'
what resource in apk are expected to deal with tinkerPatch
it support * or ? pattern.
you must include all your resources in apk here,
otherwise, they won't repack in the new apk resources.
*/
pattern = ["res/", "assets/", "resources.arsc", "AndroidManifest.xml"]
/**
optional,default '[]'
the resource file exclude patterns, ignore add, delete or modify resource change
it support * or ? pattern.
Warning, we can only use for files no relative with resources.arsc
*/
ignoreChange = ["assets/sample_meta.txt"]
/**
default 100kb
for modify resource, if it is larger than 'largeModSize'
we would like to use bsdiff algorithm to reduce patch file size
*/
largeModSize = 100
}
packageConfig {
/**
optional,default 'TINKER_ID, TINKER_ID_VALUE' 'NEW_TINKER_ID, NEW_TINKER_ID_VALUE'
package meta file gen. path is assets/package_meta.txt in patch file
you can use securityCheck.getPackageProperties() in your ownPackageCheck method
or TinkerLoadResult.getPackageConfigByName
we will get the TINKER_ID from the old apk manifest for you automatic,
other config files (such as patchMessage below)is not necessary
*/
configField("patchMessage", "tinker is sample to use")
/**
just a sample case, you can use such as sdkVersion, brand, channel...
you can parse it in the SamplePatchListener.
Then you can use patch conditional!
*/
configField("platform", "all")
/**
patch version via packageConfig
*/
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 {
/**
optional,default '7za'
the 7zip artifact path, it will use the right 7za with your platform
*/
zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
/**
optional,default '7za'
you can specify the 7za path yourself, it will overwrite the zipArtifact value
*/
// path = "/usr/local/bin/7za"
}
}
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")
tasks.all {
if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
it.doLast {
copy {
def fileNamePrefix = "{variant.baseName}"
def newFileNamePrefix = hasFlavors ? "{fileNamePrefix}-${date}"
def destPath = hasFlavors ? file("{project.name}-{variant.flavorName}") : bakPath
from variant.outputs.outputFile
into destPath
rename { String fileName ->
fileName.replace("{newFileNamePrefix}.apk")
}
from "{variant.dirName}/mapping.txt"
into destPath
rename { String fileName ->
fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
}
from "{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 = "{flavorName}/{flavorName}-release.apk"
project.tinkerPatch.buildConfig.applyMapping = "{flavorName}/{flavorName}-release-mapping.txt"
project.tinkerPatch.buildConfig.applyResourceMapping = "{flavorName}/{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 = "{flavorName}/{flavorName}-debug.apk"
project.tinkerPatch.buildConfig.applyMapping = "{flavorName}/{flavorName}-debug-mapping.txt"
project.tinkerPatch.buildConfig.applyResourceMapping = "{flavorName}/{flavorName}-debug-R.txt"
}
}
}
}
}
}
主要修改了下面几个地方
buildConfigField "String", "TINKER_ID", ""${getTinkerIdValue()}""
官方文档默认是将 git 提交的记录作为 think_id 记录下来,这里我不需要,所以我改成下图
buildConfigField "String", "TINKER_ID", ""1.0""
我直接写死,需要 git 提交记录作为 tinker_id 的也可以按照官方文档推荐的写
同时,这些地方也可以相应的替换掉
//废弃
/*def getTinkerIdValue() {
return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()
}*/
tinkerId = "1.0"/getTinkerIdValue()/
接下来修改自己的签名文件,我这里配置的文件路径是自己电脑上面的,这个位置大家需要修改为自己的签名文件路径,也可以按照我的去生成一个签名文件,我相信这个还是很简单的
//配置自己的签名文件,签名文件的生成和导入可以去百度,本篇不讲解
signingConfigs {
release {
try {
keyAlias 'china'
keyPassword '123456'
storeFile file('D:/work/release.jks')
storePassword '123456'
} catch (ex) {
throw new InvalidUserDataException(ex.toString())
}
}
debug {
storeFile file('D:/work/debug.jks')
keyAlias 'china'
keyPassword '123456'
storePassword '123456'
}
}
buildTypes {
release {
minifyEnabled true
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
minifyEnabled false
signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
官网文档没有设置 debug 的 proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’也就是说没有应用混淆文件,这样是不会生成 mapping 文件的,所以这里我也加上
android.applicationVariants.all { variant ->
/**
task type, you want to bak
*/
def taskName = variant.name
def date = new Date().format("MMdd-HH-mm-ss")
tasks.all {
if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
it.doLast {
copy {
def fileNamePrefix = "{variant.baseName}"
def newFileNamePrefix = hasFlavors ? "{fileNamePrefix}-${date}"
def destPath = hasFlavors ? file("{project.name}-{variant.flavorName}") : bakPath
from variant.outputs.outputFile
into destPath
rename { String fileName ->
fileName.replace("{newFileNamePrefix}.apk")
}
from "{variant.dirName}/mapping.txt"
into destPath
rename { String fileName ->
fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
}
from "{variant.dirName}/R.txt"
into destPath
rename { String fileName ->
fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
}
}
}
}
}
这下面主要是设置生成的文件所在的目录是什么
配置完上面之后,build 就配置好了,接下来配置 application
先上代码
package com.anlaiye.swt.gradletest;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.support.multidex.MultiDex;
import com.tencent.tinker.anno.DefaultLifeCycle;
import com.tencent.tinker.lib.tinker.TinkerInstaller;
import com.tencent.tinker.loader.app.ApplicationLike;
import com.tencent.tinker.loader.shareutil.ShareConstants;
@DefaultLifeCycle(application = ".SimpleTinkerInApplication",
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag =true)
public class SimpleTinkerInApplicationLike extends ApplicationLike {
public SimpleTinkerInApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
}
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
MultiDex.install(base);
TinkerInstaller.install(this);
}
@Override
public void onCreate() {
super.onCreate();
}
}
这个 application 官网有提供的,也可以 copy 我的,ApplicationLike 并不是一个 application
真正的 application 是 @DefaultLifeCycle(application = “.SimpleTinkerInApplication”,这个
所以在 androidmanifest 中配置 applica 的 name
评论