您的位置:首页 > 移动开发 > 微信开发

微信热修复框架的使用心得体会------Tinker

2016-11-15 10:19 513 查看
尼古拉丁 说过吃别人嚼过的馍不香 ,如果能再嚼一遍那很好 嚼过之后能咽进自己的肚子里 那更好

最近一直想为公司的项目选一个热修复插架并不是因为想跟风赶潮流,而是想站在风口,虽然我写不出这种插架

选来选取我决定用Tinker,原因? 恩,我比较喜欢微信

下面我们来梳理一下思路:

1 我们需要一款什么样的热修复技术?个人有个人的原因,根据我们公司的情况我选择的事Tinker,好了第一个问题解决。

2 那我们要看一下Tinker的源码,OK Tinker 拿去自己看。

3 那Tinker要怎么集成呢?Tinker接入指南 可以自己对照着看。Tinker常见问题 也一并拿去。

好了,现在研究一下怎么用Tinker

现在我们来下个官方demo来看看,虽然这个demo 写的很没有重点但是我们来运行一下看看,当你打开studio运行时你突然发现,我擦不能运行什么鬼?额,是这样的你需要先关掉你的 studio 的instanRun 如果你已经关了的话就当我没说,不知道怎么关的,打开 preference ,在搜所框中输入instant Run ,省下的就是把所有打钩的全点掉就OK了,还不懂看图我的是都已经挑掉了的



首先,我们很多时候的BUG 不需要新建一个类来处理一般的时候我们只是在已有的类中做一些判断和条件限制。那么我们现在模拟的就是 我有这样一个TextView 我们需要改变它上面的文字,好了这就是我们的需求。

第一步 集成Tinker

1 添加gradle依赖

在builder.gradle中添加依赖

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.3')
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}


然后在App/builde.gradle 中添加Tinker库的依赖 以及 apply Tinker 的gradle插件 可能很多同学对于gradle和 groovy 不是很熟悉,但是这不重要我会告诉你怎么用

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:support-v4:24.2.1'
testCompile 'junit:junit:4.12'
//可选,用于生成application类
provided('com.tencent.tinker:tinker-android-anno:1.7.3')
//tinker的核心库
compile('com.tencent.tinker:tinker-android-lib:1.7.3')
}


好的上面我们就集成好了,等等 集成好了么?当然还没有了 你看 Tinker的demo就知道还没有,那么接下来我们做什么呢?添加 apply tinker 的gradle插件

我们来一步步分析gradle中的代码,因为我也不是很熟悉groovy,和 gradle机理下面说的都是我的理解,还没有做过勘误,所以 不熟悉gradle的同学请继续往下看不影响 你接下来的理解,懂得大神帮忙看看有什么错误的地方请帮忙指正。

先上第一段代码 这段代码就是一个开关用来决定是否使用Tinker。

def buildWithTinker() {
return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled
}


groovy 是建立在Java语言上的,同时他还结合了其他语言强大的功能,def 是定义变量的关键字 定义变量的时候可以不指定变量的类型,同时这个 def 也可以不写 但是为了阅读的方便还是建议写上。

这句话的意思是否通过tinker build项目,hasProperty(“TINKER_ENABLE”) 返回是个boolean值,如果faulse 那么久调用方法 ext.tinkerEnabled. 那么我们来看一下 ext 函数中都包含那些方法,注意这个这个函数没有指定参数,同时也没有指定返回值

我们先看一个 bakPath 这个是Build的地址

def bakPath = file("${buildDir}/bakApk/")


下面是 ext函数

ext {
//TinkerBuild 的开关
tinkerEnabled = true

//for normal build
//"${bakPath}/app-debug-1018-17-32-47.apk" 这是字符串我相信你看的懂
tinkerOldApkPath = "${bakPath}/app-debug-1018-17-32-47.apk"
//proguard mapping file to build patch apk
tinkerApplyMappingPath = "${bakPath}/app-debug-1018-17-32-47-mapping.txt"
//R 文件路径 如果你的修复包括产生新的ID 或者 更改了ID 那么这个是必须要添加的
tinkerApplyResourcePath = "${bakPath}/app-debug-1018-17-32-47-R.txt"

//only use for build all flavor, if not, just ignore this field
//    这个是用来建全目录的 全目录就是所有的目录 如果你不需要可以不用管
tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"
}


下面的函数 就是获取各种路径的以及获取TinkerID的

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()
}


那么这个 TinkerID是怎么获得的呢?

def gitSha() {
try {
String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim()
if (gitRev == null) {
throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
}
return gitRev
} catch (Exception e) {
throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
}
}


通过上面的代码返回一个字符串。用来做ID。

好了,说道现在我还没有集成 Apply Tinker的gradle插件呢?好了,有了上面的基础,我们开始集成吧。我们一段一段分析代码

if (buildWithTinker()) {
apply plugin: 'com.tencent.tinker.patch'

tinkerPatch {

oldApk = getOldApkPath()

ignoreWarning = false

useSign = true

/**
* Warning, applyMapping will affect the normal android build!
*/
buildConfig {

applyMapping = getApplyMappingPath()

applyResourceMapping = getApplyResourceMappingPath()

tinkerId = getTinkerIdValue()
}

dex {

dexMode = "jar"

usePreGeneratedPatchDex = false

pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]

loader = ["com.tencent.tinker.loader.*",
//注意这里要改成你的 Application
"tinker.sample.android.app.SampleApplication",
//use sample, let BaseBuildInfo unchangeable with tinker
"tinker.sample.android.app.BaseBuildInfo"
]
}

lib {

pattern = ["lib/armeabi/*.so"]
}

res {

pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]

ignoreChange = ["assets/sample_meta.txt"]

largeModSize = 100
}

packageConfig {

configField("patchMessage", "tinker is sample to use")

configField("platform", "all")

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 {

zipArtifact = "com.tencent.mm:SevenZip:1.1.10"

}
}
```

添加好插件后


apply plugin: 'com.tencent.tinker.patch'




我们先分析tinkerPatch()这个函数

tinkerPatch {

oldApk = getOldApkPath()

ignoreWarning = false

useSign = true

/**
* Warning, applyMapping will affect the normal android build!
*/
buildConfig {

applyMapping = getApplyMappingPath()

applyResourceMapping = getApplyResourceMappingPath()

tinkerId = getTinkerIdValue()
}

dex {

dexMode = "jar"

usePreGeneratedPatchDex = false

pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]

loader = ["com.tencent.tinker.loader.*",
//注意这里要改成你的 Application
"tinker.sample.android.app.SampleApplication",
//use sample, let BaseBuildInfo unchangeable with tinker
"tinker.sample.android.app.BaseBuildInfo"
]
}

lib {

pattern = ["lib/armeabi/*.so"]
}

res {

pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]

ignoreChange = ["assets/sample_meta.txt"]

largeModSize = 100
}

packageConfig {

configField("patchMessage", "tinker is sample to use")

configField("platform", "all")

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 {

zipArtifact = "com.tencent.mm:SevenZip:1.1.10"

}
}


我们看一下这个函数都做了些什么

这三个就不讲了

//获取原APK 也就是有BUG 的APK的路径
oldApk = getOldApkPath()
//是否忽视 warning
ignoreWarning = false
//是否需要签名
useSign = true


看一下 buildConfig 需要注意用applyMapping的时候可能会影响到你正常的APK build

/**
* 需要注意的事 applyMapping 可能会影响到你build 正常的APK
*/
buildConfig {

applyMapping = getApplyMappingPath()

applyResourceMapping = getApplyResourceMappingPath()

tinkerId = getTinkerIdValue()
}


下面就是我们的 dex 了 ,需要注意的是你要把我写注意的地方改成你的 application

dex {

dexMode = "jar"

usePreGeneratedPatchDex = false

pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]

loader = ["com.tencent.tinker.loader.*",
//注意这里要改成你的 Application
"com.newstart.zhangjianlong.demoview.App.MyApplication",                      //use sample, let BaseBuildInfo unchangeable with tinker
"tinker.sample.android.app.BaseBuildInfo"
]
}


之后的 lib 和 res 就没有什么就不讲了

接下来看packgeConfig 这里面的方法使用来解决之前的BUG照写就可以了

packageConfig {

configField("patchMessage", "tinker is sample to use")

configField("platform", "all")

configField("patchVersion", "1.0")
}


最后一个就是7.ZIP压缩了

sevenZip {

zipArtifact = "com.tencent.mm:SevenZip:1.1.10"

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Tinker-未完待