前言:
刚跌进了几个坑,又呛过了几条河,怎么程序bug它就这么多…
问题描述:
之前集成了AndFix,一路毫无障碍的过去了,可以看我的Android 热修复 - AndFix 使用心得,这是基本使用。然后搭档在MainActivity里面加了几个新类,我打完补丁,看到了一些奇怪的log,不过补丁确实是打出来了,正确的log是如前面介绍AndFix使用的文章中的log,就只会告诉你 add Modified method:…,其它什么都不会有,,结果在测试的时候,bugApk在loadPatch之后,再次打开APP,报错了,恩,没错 ClassNotFoundException & IllegaleAccessException,大写的蒙蔽!!!
沉思了一会儿,我把问题定位在 MultiDexApplication ,不知道这个的童鞋Google一下就出来,简而言之这个类的添加就是为了解决android 中 dex文件方法超出65536限制的情况。
分析:
既然之前的demo成功了,那么问题就不该是在AndFix上面,就只有可能是AndFix的patch机制和我的代码冲突了,于是我找到了关于AndFix的具体运作原理的博文:Android热补丁之AndFix原理解析。仔细阅读这篇博文后,对于我的问题简单的描述下:
1.使用 MultiDexApplication 之后,超出主dex限制的类会被打包成另外一个辅dex,而且除了一些比较重要的类也就是和APP启动有关的类会被优先打到主dex中,一些不影响APP启动的类的优先级次之,而且不是固定的。
2.AndFix制作patch补丁包的时候,仅仅比较了主dex文件,没有去比较其它的dex文件
那么问题就很明显了,我的问题就出在,我的bug.apk和fix.apk,dex都是不一定的,或者说我要修复的类都不一定在要比对的相应的dex包中,那么一定就会出问题。
思路一:
修改AndFix的patch机制,以及修改加载补丁的机制。这一定是可以实现的,而且我顺着这个思路也确实从网上找到了相应的牛人写的分享博文:AndFix支持Multidex的解决方案。他的思路非常明确,就是通过改变patch的比较机制,然后代码注入,最后修改相应的补丁机制。虽然思路我看明白了,而且这也是一劳永逸的方法,但是是需要大量的时间成本和测试的,项目一直赶着上线,我只能默默放弃(PS:而且感觉真的好复杂啊)。
思路二:
思路一至少目前不能投入那么大的精力去使用,那么针对项目如此着急的情况下,只能想想别的:
1.因为程序方法太多导致必须使用 MultiDexApplication ,要么就使用 dex 分包的技术,事实上都是一样的。
2.类找不是因为我打包的时候主dex中的类,或者说我需要的类没有被指定的打包到主dex中,导致了ClassNotFoundException。
那么,很显然,要解决的问题就是我需要指定我需要的类必须被打包到主dex文件中,简而言之:我要控制 dex 打包的部分流程,也就是控制那些类必须在主dex中。
解决:
参考博客:Android 分Dex (MultiDex)
思路:在build.gradle中配置和打包相关的配置文件,dex 文本,其中放入的类就会被打包到classes.dex中。
上具体图和代码:
build.gradle:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion 23
buildToolsVersion "24"
defaultConfig {
applicationId "com.xxx.xxx"
minSdkVersion 14
targetSdkVersion 23
versionCode 14
versionName "2.1.5"
multiDexEnabled = true
ndk {
abiFilter "armeabi"
}
}
lintOptions {
abortOnError false
}
productFlavors {
...
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/ASL2.0'
exclude 'META-INF/notice.txt'
}
dexOptions {
javaMaxHeapSize "2g"
}
useLibrary 'org.apache.http.legacy'
sourceSets.main {
assets.srcDirs = ['src/main/assets', 'src/assets/']
jni.srcDirs = []
}
afterEvaluate {
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
if (dx.additionalParameters == null) {
dx.additionalParameters = []
}
dx.additionalParameters += '--multi-dex'
dx.additionalParameters += "--main-dex-list=$projectDir/dex".toString()
}
}
}
要贴加的就是
afterEvaluate {
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
if (dx.additionalParameters == null) {
dx.additionalParameters = []
}
dx.additionalParameters += '--multi-dex'
dx.additionalParameters += "--main-dex-list=$projectDir/dex".toString()
}
}
配置贴的比较全,相信看一下都会懂了。
再贴下 dex 的 txt 文本的位置:
dex和build.gradle同级。
dex 文件的具体内容:
android/support/multidex/CoreApiHelper/class
android/support/multidex/UpdateAppEntity/class
android/support/multidex/ISuccessBaseData/class
android/support/multidex/HawkUtils/class
android/support/multidex/NaviBottomBar/class
...
亲测是可以用,感谢以上两篇博主的帮助,还有一些没有列出来的博文。
至此,AndFix线下就已经OK了,不会有乱七八糟的问题,当然我这个方法其实也是有隐患的,如果你是插件化的APP,那么我这个方法直接忽略,看思路一中介绍的方法,另外,事实上我这个方法也只是指标不治本,这是无奈之举,项目集成了太多类库,而且有点积重难反,刚刚接手的时候已经花了很大的力气删掉一些无用的类和方法、类库,期待公司给出时间重构吧。
如果,有朋友使用了思路一中的方法,希望能写一篇博客分享出来,毕竟那才是一劳永逸的正确方法。