写点什么

tinker 热修复 gradle 接入

用户头像
Android架构
关注
发布于: 34 分钟前

}


//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,


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


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

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
tinker热修复gradle接入