@lovesosoi
2018-04-08T09:29:17.000000Z
字数 21234
阅读 1358



注意:1、若项目中有butterknife依赖且版本号高于8.4.0,修改butterknife版本号为8.4.0。
2、tinker还需要分包库,此处baselib中已有依赖所以app中没有再次依赖。compile'com.android.support:multidex:1.0.2'


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 buildWithTinker() {return hasProperty("TINKER_ENABLE") ? Boolean.parseBoolean(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/*** 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 = 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/*** optional, default 'false'* Whether tinker should treat the base apk as the one being protected by app* protection tools.* If this attribute is true, the generated patch package will contain a* dex including all changed classes instead of any dexdiff patch-info files.*/isProtectedApp = false/*** optional, default 'false'* Whether tinker should support component hotplug (add new component dynamically).* If this attribute is true, the component added in new apk will be available after* patch is successfully loaded. Otherwise an error would be announced when generating patch* on compile-time.** <b>Notice that currently this feature is incubating and only support NON-EXPORTED Activity</b>*/supportHotplugComponent = 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 tinkergetTinkerAppliactionName()]}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() > 0def date = new Date().format("MMdd-HH-mm-ss")/*** bak apk and mapping*/android.applicationVariants.all { variant ->/*** task type, you want to bak*/def taskName = variant.nametasks.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}") : bakPathfrom variant.outputs.first().outputFileinto destPathrename { String fileName ->fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")}from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"into destPathrename { String fileName ->fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")}from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"into destPathrename { String fileName ->fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")}}}}}}project.afterEvaluate {//sample use for build all flavor for one timeif (hasFlavors) {task(tinkerPatchAllFlavorRelease) {group = 'tinker'def originOldPath = getTinkerBuildFlavorDirectory()for (String flavor : flavors) {def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")dependsOn tinkerTaskdef 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 tinkerTaskdef 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"}}}}}}
apply plugin: 'com.android.application'apply plugin: 'com.tencent.tinker.patch'def javaVersion=JavaVersion.VERSION_1_7def bakPath = file("${buildDir}/bakApk/")//指定基准文件(oldApk)存放位置def getTinkerIdValue() {//和版本号一致return "2.0.1"}def getTinkerAppliactionName(){//继承TinkerApplication的类名return "com.lookbi.miaoguo.AppContext"}ext {tinkerEnabled = truetinkerOldApkPath = "${bakPath}/app-offLine-debug.apk"tinkerApplyMappingPath = "${bakPath}/app-offLine-debug-mapping.txt"tinkerApplyResourcePath = "${bakPath}/app-offLine-debug-R.txt"tinkerBuildFlavorDirectory = "${bakPath}/app-0408-15-13-44"}dependencies {implementation fileTree(include: ['*.jar'], dir: 'libs')implementation 'com.android.support:appcompat-v7:27.+'implementation 'com.android.support.constraint:constraint-layout:1.0.2'testImplementation 'junit:junit:4.12'androidTestImplementation 'com.android.support.test:runner:1.0.1'androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'implementation 'com.jakewharton:butterknife:8.4.0'annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'implementation("com.tencent.tinker:tinker-android-lib:1.9.5") { changing = true }annotationProcessor("com.tencent.tinker:tinker-android-anno:1.9.5") { changing = true }compileOnly("com.tencent.tinker:tinker-android-anno:1.9.5") { changing = true }implementation project(':baselib')implementation project(':deckview')implementation project(':jiaozivideoplayer')implementation project(':mzbanner')implementation('com.github.gzu-liyujiang.AndroidPicker:WheelPicker:1.5.5') {exclude group: 'com.android.support'}implementation files('libs/MobCommons-2017.0412.1554.jar')}android {signingConfigs {key {keyAlias RELEASE_KEY_ALIASkeyPassword RELEASE_STORE_PASSWORDstoreFile file(RELEASE_STORE_FILE)storePassword RELEASE_KEY_PASSWORD}}compileSdkVersion 26defaultConfig {applicationId "com.lookbi.miaoguo"minSdkVersion 17targetSdkVersion 26versionCode 201versionName "2.0.1"flavorDimensions "versionCode"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"multiDexEnabled truebuildConfigField "String", "MESSAGE", "\"I am the base apk\""buildConfigField "String", "TINKER_ID", "\"${getTinkerIdValue()}\""buildConfigField "String", "PLATFORM", "\"all\""}//执行lint检查,有任何的错误或者警告提示,都会终止构建,我们可以将其关掉。lintOptions {checkReleaseBuilds falseabortOnError false}repositories {flatDir {dirs 'libs'}}//java版本compileOptions {sourceCompatibility javaVersiontargetCompatibility javaVersion}//建议 recommend Tinker相关配置dexOptions {//启动矩形模式jumboMode = true}buildTypes {debug {minifyEnabled true//是否移除无用资源zipAlignEnabled true// 移除无用的resource文件shrinkResources truesigningConfig signingConfigs.key//混淆配置proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'multiDexEnabled true}release {//是否混淆minifyEnabled true//是否移除无用资源zipAlignEnabled true// 移除无用的resource文件shrinkResources truesigningConfig signingConfigs.key//混淆配置proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'multiDexEnabled true}}productFlavors {//测试环境offLine {applicationId 'com.lookbi.miaoguo'manifestPlaceholders = [app_name: "测-妙果"]resValue("string" , "baseUrl","http://mg.ketao.com/")}//生产环境onLine {applicationId 'com.lookbi.miaoguo'manifestPlaceholders = [app_name: "妙果"]resValue("string" , "baseUrl","http://miaoguo.cn/")}}}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 buildWithTinker() {return hasProperty("TINKER_ENABLE") ? Boolean.parseBoolean(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/*** 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 = 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/*** optional, default 'false'* Whether tinker should treat the base apk as the one being protected by app* protection tools.* If this attribute is true, the generated patch package will contain a* dex including all changed classes instead of any dexdiff patch-info files.*/isProtectedApp = false/*** optional, default 'false'* Whether tinker should support component hotplug (add new component dynamically).* If this attribute is true, the component added in new apk will be available after* patch is successfully loaded. Otherwise an error would be announced when generating patch* on compile-time.** <b>Notice that currently this feature is incubating and only support NON-EXPORTED Activity</b>*/supportHotplugComponent = 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 tinkergetTinkerAppliactionName()]}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() > 0def date = new Date().format("MMdd-HH-mm-ss")/*** bak apk and mapping*/android.applicationVariants.all { variant ->/*** task type, you want to bak*/def taskName = variant.nametasks.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}") : bakPathfrom variant.outputs.first().outputFileinto destPathrename { String fileName ->fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")}from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"into destPathrename { String fileName ->fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")}from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"into destPathrename { String fileName ->fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")}}}}}}project.afterEvaluate {//sample use for build all flavor for one timeif (hasFlavors) {task(tinkerPatchAllFlavorRelease) {group = 'tinker'def originOldPath = getTinkerBuildFlavorDirectory()for (String flavor : flavors) {def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")dependsOn tinkerTaskdef 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 tinkerTaskdef 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、此时去修改bug。
注意:1、不能直接修改layout内的内容。若要修改layout内容需要新增layout xml文件。(复制一份,改改,老的xml不用了)
2、修改完成后,点击tinker内的目标选项,只用选一个,等待编译完成


1、下载完成后客户端加载补丁包

2、修复完之后需要彻底杀死App进程,下次才能进才能显示修复后的内容
tinker Demo中修复完后,直接杀死了app进程,体验不好。此时需要重写DefaultTinkerResultService,在application中调用。
