AndResGuard 接入
微信的資源混淆(github地址)接入比較簡(jiǎn)單闻镶,在工程的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項(xiàng)目的build.gradle目錄下引入上面的配置即可
//添加微信資源混淆
apply from: "../config/resguard.gradle"
因?yàn)槲业奈募诠こ棠夸浶陆艘粋€(gè)config文件夾,resguard.gradle在config文件夾里面放著丸升,所以如上引入铆农,當(dāng)然你也可以放到其他目錄。到這里資源混淆就玩了狡耻,貼了這么多代碼就是為了方便復(fù)制粘貼墩剖,呵呵,看看效果吧夷狰!
生成的app文件看名字就知道啦涛碑,解壓后資源文件就是abc這種混淆后的格式了,** 最后再提醒一點(diǎn)孵淘,如果項(xiàng)目中有用到的資源文件是寫(xiě)死的,那必須加入白名單歹篓,不然運(yùn)行后會(huì)報(bào)錯(cuò)瘫证,因?yàn)槲募煜竺Q改了是找不到的揉阎,這點(diǎn)必須注意。**
Tinker的接入 github地址
再來(lái)看看tinker的接入吧(ps:tinker相比接入較復(fù)雜背捌,具體可以看github的demo)毙籽,在工程目錄添加
//添加tinker patch插件
classpath('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.7')
接著項(xiàng)目目錄添加
//tinker的核心庫(kù)
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}")
//當(dāng)前路徑為tinker基準(zhǔn)包路徑(這些地址可根據(jù)自身情況自行 )
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 = ""
//當(dāng)前設(shè)置tinker基準(zhǔn)包的路徑,此路經(jīng)會(huì)根據(jù)不同渠道包生成不同的修復(fù)包
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
//根據(jù)前面設(shè)置的渠道包路徑遍歷設(shè)置相關(guān)文件
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 {
//這里的路徑也是根據(jù)自身實(shí)際情況獲取相應(yīng)文件助被,下面的這些路徑都是一樣
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)
}
}
}
}
}
}
接著我們?cè)倏纯瓷傻男迯?fù)包吧
配置完了,但是還要修改Application呢缸沃,看圖
自己寫(xiě)個(gè)MyApplication 繼承了tinker 的DefaultApplicationLike類恰起,記得在自定義的application添加注解呀,然后用注解的application寫(xiě)在manifest中的即可
到此配置就結(jié)束了趾牧,這里只是用debug演示而已检盼,詳細(xì)的可以參考demo一起交流,最后我們看看演示結(jié)果吧翘单。
上面這個(gè)是修復(fù)之前的安裝包吨枉,跳轉(zhuǎn)后是AActivity顯示樣子。
這是修復(fù)后的哄芜,這里比較簡(jiǎn)單貌亭,只是把修復(fù)包改名為test放到內(nèi)存卡根目錄,然后跳轉(zhuǎn)后就直接修復(fù)了(修復(fù)這里需要注意一下认臊,就是app進(jìn)程殺死后或者屏幕熄滅后才會(huì)修復(fù)哦)圃庭。好了,到此就完了,歡迎大家一起學(xué)習(xí)探討剧腻。