AndResGuard 接入
微信的资源混淆(github地址)接入比较简单,在工程的build.gradle中添加
classpath ('com.tencent.mm:AndResGuard-gradle-plugin:1.1.16') {changing=true}
添加 ** resguard.gradle **配置文件,如下
apply plugin: 'AndResGuard'
//微信的混淆资源 放在签名的后面
andResGuard {
mappingFile = null
use7zip = true
useSign = true
keepRoot = false
// add <your_application_id>.R.drawable.icon into whitelist.
// because the launcher will get thgge icon with his name
whiteList = [
//for your icon
"R.drawable.icon",
//for fabric
"R.string.com.crashlytics.*",
//for umeng update
"R.string.umeng*",
"R.string.UM*",
"R.string.tb_*",
"R.layout.umeng*",
"R.layout.tb_*",
"R.drawable.umeng*",
"R.drawable.tb_*",
"R.anim.umeng*",
"R.color.umeng*",
"R.color.tb_*",
"R.style.*UM*",
"R.style.umeng*",
"R.id.umeng*"
]
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"resources.arsc"
]
sevenzip {
artifact = 'com.tencent.mm:SevenZip:1.1.16'
//path = "/usr/local/bin/7za"
}
}
接着在app项目的build.gradle目录下引入上面的配置即可
//添加微信资源混淆
apply from: "../config/resguard.gradle"
因为我的文件在工程目录新建了一个config文件夹,resguard.gradle在config文件夹里面放着,所以如上引入,当然你也可以放到其他目录。到这里资源混淆就玩了,贴了这么多代码就是为了方便复制粘贴,呵呵,看看效果吧!
生成的app文件看名字就知道啦,解压后资源文件就是abc这种混淆后的格式了,** 最后再提醒一点,如果项目中有用到的资源文件是写死的,那必须加入白名单,不然运行后会报错,因为文件混淆后名称改了是找不到的,这点必须注意。**
Tinker的接入 github地址
再来看看tinker的接入吧(ps:tinker相比接入较复杂,具体可以看github的demo),在工程目录添加
//添加tinker patch插件
classpath('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.7')
接着项目目录添加
//tinker的核心库
compile("com.tencent.tinker:tinker-android-lib:1.7.7")
// 用于生成application类
provided("com.tencent.tinker:tinker-android-anno:1.7.7")
compile "com.android.support:multidex:1.0.1"
最后添加即可
apply from: "../config/tinker.gradle"
附上tinker.gradle配置文件
//备份文件夹显位置
def bakPath = file("${buildDir}/bakApk/")
//AndResguard生成的文件位置
def bakResguard = file("${bakPath}")
//当前路径为tinker基准包路径(这些地址可根据自身情况自行 )
def tinkerPath = bakPath
//是否需要备份
def isNeedBackup = true
/**
* 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 = ""
//proguard mapping file to build patch apk
tinkerApplyMappingPath = ""
//resource R.txt to build patch apk, must input if there is resource changed
tinkerApplyResourcePath = ""
//当前设置tinker基准包的路径,此路经会根据不同渠道包生成不同的修复包
tinkerBuildFlavorDirectory = "${tinkerPath}/inspection2-0505-1719"
}
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") ? 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
}
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 = []
}
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/*", "r/*", "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.16"
/**
* 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<>();
android.productFlavors.each { flavor ->
flavors.add(flavor.name)
}
boolean hasFlavors = flavors.size() > 0
//根据前面设置的渠道包路径遍历设置相关文件
def setFilePatch = {
if (it != null && it.isDirectory()) {
File[] files = it.listFiles()
files.each {
if (it.isFile()) {
if (it.name.endsWith(".apk")) {
project.tinkerPatch.oldApk = "${it.absolutePath}"
} else if (it.name.endsWith("mapping.txt")) {
project.tinkerPatch.buildConfig.applyMapping = "${it.absolutePath}"
} else if (it.name.endsWith("R.txt")) {
project.tinkerPatch.buildConfig.applyResourceMapping = "${it.absolutePath}"
}
}
}
}
}
/**
* bak apk and mapping
*/
android.applicationVariants.all { variant ->
/**
* task type, you want to bak
*/
// def taskName = 'release'
def taskName = 'debug'
String apkName = variant.outputs.outputFile[0].name
apkName = apkName.substring(0, apkName.length() - 4)
tasks.all {
if (variant.buildType.name == taskName) {
if ("tinkerPatch${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
def resguardTask
tasks.all {
if (it.name.equalsIgnoreCase("resguard${taskName.capitalize()}")) {
resguardTask = it
}
}
it.doFirst({
File flavorFile = file("${getTinkerBuildFlavorDirectory()}/${variant.flavorName}")
setFilePatch(flavorFile)
// // change build apk path
})
it.dependsOn resguardTask
}
if ("resguard${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
it.doLast {
if (!isNeedBackup) {
return 0
}
def date = new Date().format("MMdd-HHmm")
def destPath = hasFlavors ? file("${bakResguard}/${project.name}-${date}/${variant.flavorName}") : bakResguard
//backUpVersion
copy {
//这里的路径也是根据自身实际情况获取相应文件,下面的这些路径都是一样
from "${buildDir}/outputs/apk/AndResGuard_${apkName}/${apkName}_signed_7zip_aligned.apk"
into file(destPath.absolutePath)
rename { String fileName ->
try {
fileName.replace("${apkName}_signed_7zip_aligned.apk", "${apkName}.apk")
} catch (Exception e) {
print "rename apk mapping error"
e.printStackTrace()
}
}
from "${buildDir}/outputs/mapping/${hasFlavors?variant.flavorName +"/"+ taskName : taskName}/mapping.txt"
into file(destPath.absolutePath)
rename { String fileName ->
fileName.replace("mapping.txt", "${apkName}_mapping.txt")
}
from "${buildDir}/intermediates/symbols/${hasFlavors?variant.flavorName +"/"+ taskName : taskName}/R.txt"
into file(destPath.absolutePath)
rename { String fileName ->
fileName.replace("R.txt", "${apkName}_R.txt")
}
from "${buildDir}/outputs/apk/AndResGuard_${apkName}/resource_mapping_${apkName}.txt"
into file(destPath.absolutePath)
// rename { String fileName ->
// try {
// fileName.replace("resource_mapping_${orgApkPrefix}.txt", "resource_mapping_${targetApkPrefix}.txt")
// } catch (Exception e) {
// print "rename resource mapping error"
// e.printStackTrace()
// }
// }
print "one resguard backup tinker base apk ok! \n"
}
}
}
}
}
}
//从指定的旧的目录中打出差分包
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) + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)
File flavorFile = file("${originOldPath}/${flavorName}")
setFilePatch(flavorFile)
}
}
}
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) + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)
File flavorFile = file("${originOldPath}/${flavorName}")
setFilePatch(flavorFile)
}
}
}
}
}
}
接着我们再看看生成的修复包吧
配置完了,但是还要修改Application呢,看图
自己写个MyApplication 继承了tinker 的DefaultApplicationLike类,记得在自定义的application添加注解呀,然后用注解的application写在manifest中的即可
到此配置就结束了,这里只是用debug演示而已,详细的可以参考demo一起交流,最后我们看看演示结果吧。
上面这个是修复之前的安装包,跳转后是AActivity显示样子。
这是修复后的,这里比较简单,只是把修复包改名为test放到内存卡根目录,然后跳转后就直接修复了(修复这里需要注意一下,就是app进程杀死后或者屏幕熄灭后才会修复哦)。好了,到此就完了,欢迎大家一起学习探讨。