Android5.0以下手机上关于类冲突的总结
来源:互联网 发布:校园网络连接ip网关 编辑:程序博客网 时间:2024/04/30 00:06
问题描述
我们使用分层的方式接入一些第三方库,上层只调用下层的接口,完全不清楚下层的具体细节,当方法数超出65535时,由于是我们是先接入接口服务层SDK生成APK后再使用一个工具加入第三方库的具体实现,默认将第三方库的所有实现放到了第二个Dex中,这时发现当第一个Dex和第二个Dex中包含了相同的Jar(比如support-v4,gson等),在Android5.0上在使用相关功能时会崩溃,抛出的异常是:
主要是一个类检查的错误。
问题分析
问题分析: 分层机制使得应用在接入SDK接口服务层时无法接触到第三方SDK具体实现的Jar,这使得应用在无法发觉的情况下会添加一些和第三方SDK冲突的Jar,这里的冲突指的是和第三方SDK包含有相同package的class的Jar,但是两边的Jar的版本可能不同,也就是class中的内容可能不同。
目前打包工具对于应用APK中的dex加上SDK具体实现方法数超过65535的做法是,直接将第三方SDK的所有jar编译为classes2.dex,然后使用google的aapt工具添加到应用APK中去,应用的Applicaiton被修改或者继承自MultidexApplication,在应用启动时安装classes2.dex。这样APK中存在两个dex文件,而且两个dex文件很容易出现冲突的类。
这种情况在Android5.0以上的ART模式中不会有问题,原因是ART会在安装时将多个dex合并生成ODEX文件,当某个加载出来的类调用另外一个类时会在本dex中加载调用,如果本dex中没有再到另外一个dex种寻找该类,而且引用类和要装载的类即使不来自于同一个dex也不会报错。
但是这种情况在Android5.0以下的Dalvik虚拟机上就会出现了问题,原因是虚拟机加载类时有个
dvmResolveClass方法,这个方法对Class进行了校验。判断这个要Resolve的class是否和其引用来自一个dex。如果不是,返回false后Dalvik就会跑出异常并崩溃。
之前出现该问题时的临时解决方法是:将第三方SDK中和应用冲突的jar编译成smali文件,在反编译应用APK后,强制将第三方SDK的smali覆盖到应用的smali中,也就是第三方SDK的代码覆盖掉了应用的代码。当时出包后暂时没有测试出其它问题。但是该解决方案存在两个问题:
1. 当应用的Jar和第三方SDK的Jar版本差异较大时,可能会导致应用使用该功能时崩溃。
2. 如果应用主dex的方法数较多,当覆盖后会造成主dex的方法数依然超过65535,无法编译出APK。
比较完善的解决方案
目前对于运行时加载dex是有很多的成熟的解决方案的,比如阿里的百川HotFix,通过研究HotFix的原理(http://blog.csdn.net/xwl198937/article/details/49801975),提出如下解决方案:
1. 通过开源的Hook框架(目前暂定是Cydia Hook),应用启动时检测到系统是5.0以下时,引用Cydia Hook技术来hook Native dalvik中dvmResolveClass这个方法,直接返回true,也就是跳过的原始的检测。
2. 在SDK接口服务层封装第三方SDK时,将第三方SDK需要在Applicaiton中初始化并加载的jar通过Ant脚本放到资源包的mainsmali文件夹中,该文件夹会在打包工具中复制到反编译后的APK的smali中。
这样做的风险:
主要风险
1, 跳过了Dalvik的中dvmResolveClass的检查后,在Android 5.0以下系统的表现是对于冲突的类只加载第二个Dex中的类,这时如果两个Dex中的相同package中的类差异很大,比如方法数不同,方法实现不同等,应用可能会使用该类时直接Crash或者行为异常。
2. Cydia Hook的性能,稳定性问题,但是据我所知,目前有些公司已经用它来实现apk加壳和脱壳防止反编译技术方案。具体可以参考:
http://www.cydiasubstrate.com/
而且Cydia Hook的so只有134kb,多于APK大小不会造成太大影响,提供了源代码,可以进行更加深入的研究。
3. 需要通过工作经验的方式在封装第三方SDK时手动添加必须放在mainsmali文件夹的jar。但是脚本已经优化的比较简单,可以通过正则匹配的方式查找Jar,即第三方SDK在封装时只需要修改一次,之后升级时就不需要再次修改了,除非第三方SDK进行了非常大的改动。
这样做的优势:
这样只将必须在Application中加载的类放在应用的主dex中,减少了主dex方法数超出65535的可能,而且只在系统是5.0以下时启动Hook方案,对于5.0以上原生支持multidex的系统不造成任何影响。
具体实现
Cydia Hook技术来hook Native dalvik中dvmResolveClass这个方法,直接返回true,也就是跳过的原始的检测。然后使用Android提供的support-mulitdex安装Dex文件,具体实现在:
https://github.com/blueiceheaven/ndk-patch.git
在应用的Application
中的attachBaseContext
回调中调用Cydia Hook,:
public class MyApplication extends Application { public MyApplication() { } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); PatchDex.install(this); HookBridge.initJNIEnv(); PatchDex.addAllDexFile(base, HookManager.getInstance().getPatchDir(base).getAbsolutePath(), HookManager.getInstance().getPatchOptDir(base).getAbsolutePath(), false); }}
- Android5.0以下手机上关于类冲突的总结
- cocos2dx3.0以下版本打的apk包在android5.0机上运行崩溃
- java.lang.NoClassDefFoundError:Android5.0以下错误
- Android5.0以下报NoClassDefFoundError解决方法
- FlashLight在Android5.0上的使用方法
- 关于Android5.0+去掉标题栏的研究
- Android5.0以上获取topActivity的包名方法与以下的整合
- 关于Android5.0以上屏幕截图探索总结
- 关于android6.0权限适配6.0系统以下的问题总结(二维码扫描)
- Android Studio2.0在Android5.0以下机型无法调试
- Android5.0以下WebView实现访问Https双向认证网页
- Android5.0以下(Android4.x)出现NoClassDefFoundError
- Android5.0以下 native方法监听app卸载
- xUtils-2.6.14在android5.0以下系统https问题
- Android5.0上JNI的GC回收机制
- Android5.0上SD卡抽拔发送的Intent研究
- Android5.0上WebView的android.content.res.Resources$NotFoundException
- 解决HttpClient在Android5.0系统上无效的问题
- Stack Clash:Linux安全杀手
- dubbo命令行
- Ubuntu 16.04 adb devices : no permissions 解决方法
- LXC——LinuX Container 使用简介
- python3josn 数据解析
- Android5.0以下手机上关于类冲突的总结
- 关于迭代器的相关基础操作
- oracle中从4个表查询数据并插入到另一张表里 扩展到查询到的数据和其他数据共同插入
- OpenCV自学笔记8:读取视频文件
- 分位数概念
- ubuntu14.04安装cudnn
- 转发
- Lua笔记补充
- spark【例子】同类合并、计算(主要使用groupByKey)