Android热更新框架Tinker探索之旅.docx
《Android热更新框架Tinker探索之旅.docx》由会员分享,可在线阅读,更多相关《Android热更新框架Tinker探索之旅.docx(42页珍藏版)》请在冰豆网上搜索。
Android热更新框架Tinker探索之旅
Android热更新框架Tinker探索之旅
现在比较流行的热修复框架的优缺点和特色,在Tinker的wiki下都可以看到,这里就不在赘述了,Tinker只用了这句话来展现自己的优势:
Tinker已运行在微信的数亿Android设备上,那么为什么你不使用Tinker呢?
Tinker分为gradle接入和命令行接入,但是我这种菜鸟玩不转命令行,所以只能照搬gradle接入Tinker了。
但是我感觉,命令行算不上一种接入,仅仅是多了一种编译差分包的方式而已,当然,这仅仅是个人见解哈,至于我为什么这么说,大家往下看就知道了。
首先,在project的build.gradle文件里需要添加Tinker的插件依赖
dependencies{
classpath'com.android.tools.build:
gradle:
1.3.0'
...
//Tinker
classpath"com.tencent.tinker:
tinker-patch-gradle-plugin:
1.7.7"
}
其次就是主module的build.gradle文件的配置,主要有下面几个地方
1.引入Tinker依赖
dependencies{
//tinker必须要引入多dex打包支持
compile'com.android.support:
multidex:
1.0.1'
//可选,用于注解生成application类
provided("com.tencent.tinker:
tinker-android-anno:
1.7.7"
//tinker依赖版本
compile("com.tencent.tinker:
tinker-android-lib:
1.7.7")
}
2.tinker的一些配置
//-------------------------tinkerconfigstart-------------------------
buildConfigField"String","TINKER_VERSION","\"1.7.7\""
/**
*buildConfigcanchangeduringpatch!
*wecanusethenewlyvaluewhenpatch
*/
buildConfigField"String","MESSAGE","\"Iamthebaseapk\""
/**
*clientversionwouldupdatewithpatch
*sowecangetthenewlygitversioneasily!
*/
buildConfigField"String","TINKER_ID","\"${getTinkerIdValue()}\""
buildConfigField"String","PLATFORM","\"all\""
//-------------------------tinkerconfigend-------------------------
3.编译差分包的开关和老版本文件位置确定
defbakPath=file("${buildDir}/bakApk/")
/**
*youcanuseassembleReleasetobuildyoubaseapk
*usetinkerPatchRelease-POLD_APK=-PAPPLY_MAPPING=-PAPPLY_RESOURCE=tobuildpatch
*addapkfromthebuild/bakApk
*/
ext{
defoldPrefixFormat="app_beta1_${appVersionName}_${newDate().format("yyyy_MM_dd")}_%s_build${appVersionCode}_%s"
defoldApkPath=String.format(oldPrefixFormat,"10_34_38","tinker.apk")
defapplyMappingPath=String.format(oldPrefixFormat,"10_34_38","mapping.txt")
defresourcePath=String.format(oldPrefixFormat,"10_34_38","R.txt")
//forsomereason,youmaywanttoignoretinkerBuild,suchasinstantrundebugbuild?
tinkerEnabled=true
//fornormalbuild
//oldapkfiletobuildpatchapk
tinkerOldApkPath="${bakPath}/${oldApkPath}"
//proguardmappingfiletobuildpatchapk
tinkerApplyMappingPath="${bakPath}/${applyMappingPath}"
//resourceR.txttobuildpatchapk,mustinputifthereisresourcechanged
tinkerApplyResourcePath="${bakPath}/${resourcePath}"
//onlyuseforbuildallflavor,ifnot,justignorethisfield
tinkerBuildFlavorDirectory="${bakPath}/app-patch-${releaseTime()}"
}
4.tinker编译差分包的task配置
tinkerPatch{
/**
*necessary,default'null'
*theoldapkpath,usetodiffwiththenewapktobuild
*addapkfromthebuild/bakApk
*/
oldApk=getOldApkPath()
/**
*optional,default'false'
*therearesomecaseswemaygetsomewarnings
*ifignoreWarningistrue,wewouldjustassertthepatchprocess
*case1:
minSdkVersionisbelow14,butyouareusingdexModewithraw.
*itmustbecrashwhenload.
*case2:
newlyaddedAndroidComponentinAndroidManifest.xml,
*itmustbecrashwhenload.
*case3:
loaderclassesindex.loader{}arenotkeepinthemaindex,
*itmustbelettinkernotwork.
*case4:
loaderclassesindex.loader{}changes,
*loaderclassesisuestoloadpatchdex.itisuselesstochangethem.
*itwon'tcrash,butthesechangescan'teffect.youmayignoreit
*case5:
resources.arschaschanged,butwedon'tuseapplyResourceMappingtobuild
*/
ignoreWarning=false
/**
*optional,default'true'
*whethersignthepatchfile
*ifnot,youmustdoyourself.otherwiseitcan'tchecksuccessduringthepatchloading
*wewillusethesignconfigwithyourbuildtype
*/
useSign=true
/**
*optional,default'true'
*whetherusetinkertobuild
*/
tinkerEnable=buildWithTinker()
/**
*Warning,applyMappingwillaffectthenormalandroidbuild!
*/
buildConfig{
/**
*optional,default'null'
*ifweusetinkerPatchtobuildthepatchapk,you'dbettertoapplytheold
*apkmappingfileifminifyEnabledisenable!
*Warning:
*youmustbecarefulthatitwillaffectthenormalassemblebuild!
*/
applyMapping=getApplyMappingPath()
/**
*optional,default'null'
*ItisnicetokeeptheresourceidfromR.txtfiletoreducejavachanges
*/
applyResourceMapping=getApplyResourceMappingPath()
/**
*necessary,default'null'
*becausewedon'twanttocheckthebaseapkwithmd5intheruntime(itisslow)
*tinkerIdisusetoidentifytheuniquebaseapkwhenthepatchistriedtoapply.
*wecanusegitrev,svnrevorsimplyversionCode.
*wewillgenthetinkerIdinyourmanifestautomatic
*/
tinkerId=getTinkerIdValue()
/**
*ifkeepDexApplyistrue,classinwhichdexrefertotheoldapk.
*openthiscanreducethedexdifffilesize.
*/
keepDexApply=false
}
dex{
/**
*optional,default'jar'
*onlycanbe'raw'or'jar'.forraw,wewouldkeepitsoriginalformat
*forjar,wewouldrepackdexeswithzipformat.
*ifyouwanttosupportbelow14,youmustusejar
*oryouwanttosaveromorcheckquicker,youcanuserawmodealso
*/
dexMode="jar"
/**
*necessary,default'[]'
*whatdexesinapkareexpectedtodealwithtinkerPatch
*itsupport*or?
pattern.
*/
pattern=["classes*.dex",
"assets/secondary-dex-?
.jar"]
/**
*necessary,default'[]'
*Warning,itisveryveryimportant,loaderclassescan'tchangewithpatch.
*thus,theywillberemovedfrompatchdexes.
*youmustputthefollowingclassintomaindex.
*Simply,youshouldaddyourownapplication{@codetinker.sample.android.SampleApplication}
*owntinkerLoader,andtheclassesyouuseinthem
*
*/
loader=[
//usesample,letBaseBuildInfounchangeablewithtinker
"com.simpletour.client.tinker.app.BaseBuildInfo"
]
}
lib{
/**
*optional,default'[]'
*whatlibraryinapkareexpectedtodealwithtinkerPatch
*itsupport*or?
pattern.
*forlibraryinassets,wewouldjustrecovertheminthepatchdirectory
*youcangettheminTinkerLoadResultwithTinker
*/
pattern=["lib/*/*.so"]
}
res{
/**
*optional,default'[]'
*whatresourceinapkareexpectedtodealwithtinkerPatch
*itsupport*or?
pattern.
*youmustincludeallyourresourcesinapkhere,
*otherwise,theywon'trepackinthenewapkresources.
*/
pattern=["res/*","assets/*","resources.arsc","AndroidManifest.xml"]
/**
*optional,default'[]'
*theresourcefileexcludepatterns,ignoreadd,deleteormodifyresourcechange
*itsupport*or?
pattern.
*Warning,wecanonlyuseforfilesnorelativewithresources.arsc
*/
ignoreChange=["assets/sample_meta.txt"]
/**
*default100kb
*formodifyresource,ifitislargerthan'largeModSize'
*wewouldliketousebsdiffalgorithmtoreducepatchfilesize
*/
largeModSize=100
}
packageConfig{
/**
*optional,default'TINKER_ID,TINKER_ID_VALUE''NEW_TINKER_ID,NEW_TINKER_ID_VALUE'
*packagemetafilegen.pathisassets/package_meta.txtinpatchfile
*youcanusesecurityCheck.getPackageProperties()inyourownPackageCheckmethod
*orTinkerLoadResult.getPackageConfigByName
*wewillgettheTINKER_IDfromtheoldapkmanifestforyouautomatic,
*otherconfigfiles(suchaspatchMessagebelow)isnotnecessary
*/
configField("patchMessage","tinkerissampletouse")
/**
*justasamplecase,youcanusesuchassdkVersion,brand,channel...
*youcanparseitintheSamplePatchListener.
*Thenyoucanusepatchconditional!
*/
configField("platform","all")
/**
*patchversionviapackageConfig
*/
configField("patchVersion","1.0")
}
//oryoucanaddconfigfiledoutside,orgetmetavaluefromoldapk
//project.tinkerPatch.packageConfig.configField("test1",project.tinkerPatch.packageConfig.getMetaDataFromOldApk("Test"))
//project.tinkerPatch.packageConfig.configField("test2","sample")
/**
*ifyoudon'tusezipArtifactorpath,wejustuse7zatotry
*/
sevenZip{
/**
*optional,default'7za'
*the7zipartifactpath,itwillusetheright7zawithyourplatform
*/
zipArtifact="com.tencent.mm:
SevenZip:
1.1.10"
/**
*optional,default'7za'
*youcanspecifythe7zapathyourself,itwilloverwritethezipArtifactvalue
*/
//path="/usr/local/bin/7za"
}
}
5.拷贝和自定义apk、mapping、R文件的别名
/**
*bakapkandmapping
*/
android.applicationVariants.all{variant->
/**
*tasktype,youwanttobak
*/
deftaskName=variant.name
defdate=newDate().format("yyyy_MM_dd_HH_mm_ss")
defbuildTypeName=variant.baseName+""
defisRelease=buildTypeName=="release"
tasks.all{
if("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)){
it.doLast{
copy{
//正常情况下生成的apk文件的文件名前缀
deffileNamePrefix="${project.name}-${buildTypeName}"
//自定义apk文件的别名
defnewFileNamePrefix="app_beta1_${isRelease?
"${buildTypeName}_":
""}${appVersionName}_${date}_build${appVersionCode}"
//自定义apk、mapping、R文件存放的目录
defdestPath=hasFlavors?
file("${bakPath}/${project.name}-${date}/${variant.flavorName}"):
bakPath
//复制apk文件和重命名
fromvariant.outputs.outputFile
intodestPath
rename{StringfileName->
fileName.replace("${fileNamePrefix}.apk","${newFileNamePrefix}_tinker.apk")
}
//复制和重命名mapping文件
from"${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"
intodestPath
rename{StringfileName->
fileName.replace("mapping.txt","${newFileNamePrefix}_mapping.txt")
}
//复制和重命名R文件
from"${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
intodestPath
rename{StringfileName->
fileName.replace("R.txt","${newFileNamePrefix}_R.txt")
}
}
}
}
}
}
有了上面这些基本的配置,基本上tinker的引入就算完成了。
这里要说一下引入multidex的目的不仅是解决65536的限制,还有就是因为tinker的修复原理是de