本文共 1755 字,大约阅读时间需要 5 分钟。
在Android开发中,热补丁方案旨在在不重新启动应用程序的情况下修复已安装的应用程序。通常情况下,开发者希望通过热补丁实现多方面的修复,包括类修复、资源修复以及SO库的修复。以下将详细介绍Android下的SO库修复思路,分析其实现原理以及在不同虚拟机(如Dalvik和Art)下的兼容性问题。
在Android中,Java API提供了两种加载SO库的接口:
System.loadLibrary(String libName):传入的参数是SO库的名称,表示SO库文件位于APK压缩文件中的libs目录下。系统会将该SO库复制到APK安装目录下,最后通过nativeLoad方法加载该SO库。
System.load(String pathName):传入的参数是SO库在磁盘中的完整路径,用于加载一个自定义外部的SO库文件。
这两种方式最终都调用nativeLoad方法来加载SO库,参数为SO库的文件名。JNI编程中,动态注册的native方法必须实现JNI_OnLoad方法,而静态注册的native方法则必须以类名+方法名的格式进行注册。
为了实现SO库的实时修复,我们需要分析动态注册和静态注册native方法在不同虚拟机下的兼容性。
动态注册的native方法通过JNI_OnLoad方法重新映射,前提是SO库已经加载过一次。我们可以先加载原SO库,然后再加载补丁SO库,从而实现动态注册native方法的实时修复。
hashtable中查找到的句柄指向补丁SO库,从而实现动态注册方法的实时生效。System.load方法加载补丁SO库,调用补丁SO库的JNI_OnLoad方法,实现动态注册方法的实时生效。静态注册的native方法在第一次执行时完成映射,无法直接通过补丁SO库实现实时修复。我们需要解注册这些方法,然后重新加载补丁SO库。
尽管SO库的实时生效在动态注册方法中实现了兼容性,但静态注册方法的实时修复存在局限性:
因此,最终放弃了SO库的实时生效需求,转而采用冷部署重启生效的方案。
通过替换System.loadLibrary接口,优先加载补丁SO库:
优点:无需对不同SDK版本进行区分处理,实现简单。
缺点:需要强制侵入用户接口调用,无法修复三方库的SO库。
通过反射注入修改DexPathList.findLibrary方法,插入补丁SO库的路径:
findLibrary方法中,插入补丁SO库的路径到nativeLibraryDirectories数组的最前面。优点:可以修复三方库的SO库,不依赖于用户接口调用。
缺点:需对SDK进行适配,findLibrary实现在SDK23之前和之后有所不同。同时,需选择合适的CPU架构的SO库文件进行修复。
SO库修复方案目前主要采用接口调用替换和反射注入两种方式:
对于实时生效的需求,可以通过SO库的重命名确保动态注册方法的实时生效,但静态注册方法仍存在局限性。因此,推荐采用冷部署重启生效的方案,兼顾通用性和实现复杂度。
转载地址:http://vkrb.baihongyu.com/