Dynamic Feature技术 坑点分析

dynamic feature是Google利用了安卓系统自带的split apk机制提供的官方‘插件化‘方案,通过使用此技术,我们可以做到模块按需下发,减少应用包体积。但使用过程中有一些坑点需要注意。

在首次触发dynamic feature安装之后,若app未重启,此时可能会出现如下问题

  1. base apk中使用Class的getResourceAsStream方法会返回空
  2. 使用System.loadLibarary会失败

但base apk的class加载似乎不受影响。

猜测如下:

首次触发dynamic feature安装的时候,系统也会重新安装base apk? 导致base apk的目录发生改变。原base apk目录被删除,产生新的base apk目录,新的目录包含base apk以及dynamic feature apk

为什么getResourceAsStream方法会失败?

在安卓中最终负责此工作的ClassLoader是PathClassLoader,PathClassLoader会将此工作委托给DexPathList,然后经过如下步骤

  1. 检查DexPathList.Element中含有此资源
  2. 返回一个有效的uri,这个uri以jar协议开头,同时后面会携带apk的目录以及资源名拼接成的uri
  3. 打开此uri

问题可能出在第1/3步,当执行第1步时,如果dynamic feature已经安装,分情况讨论

如果对应的DexPathList.Element没有打开过,会进行初始化,由于对应的目录已经不存在了,所以初始化会失败,但第1步catch了IOException,不会发生崩溃,但会直接返回null,

如果对应的DexPathList.Element打开过了,内存中会保留对应文件的文件描述符,安卓系统底层的linux保证对应的文件描述符没关闭期间,文件被删除移动,该对应的文件描述符仍然可以访问文件。因此第二步能够检查成功,返回一个有效的uri,问题出在第三步,通过这个uri打开对应文件的时候,由于对应的文件apk已经被移动了,这里打开会失败。返回为空。

为什么使用System.loadLibarary会失败?

原因类似getResourceAsStream. System.loadLibrary也会把对应的工作最终交给DexPathList.Element. DexPathList会有两个路径,一个是从apk中解压出来对应架构的so目录,另外一个就是apk目录,通常来说,会直接从解压的目录加载对应的so,因此这一步在查找中会失败,因为文件夹已经不存在了。当尝试从apk文件中解析的时候则面临着和getResourceAsStream一样的处境。

有的人可能会有心理误区,会想只要我加载的so不在dynamic feature的模块中,那应该就不会出问题,因为安装split apk似乎不会对base apk做什么更改,但问题在于base apk的目录也被改变了!!因此直接就加载失败了。

为什么base apk的class加载似乎不受影响?

因为base apk的文件已经被打开并且没有被关闭,因此不会受到重新安装的影响。

SplitCompat.install做了什么

  1. 会通过反射调用AssetManager的addAssetPath把split apk的路径加进去。
  2. 似乎会通过在线程池发起一个任务,修改PathClassLoader的dexPathList
    注意,splitapk安装,部分设备存在兼容性问题,首次安装会导致进程被杀死。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容