爱奇艺App架构升级之路——64位适配探索与实践

背  景

随着手机硬件的不断发展,近两年的新式手机已经全部采用了64位CPU,64位真的比32位快吗?实际上32位和64位的差异主要体现在内存寻址上,32位最高只支撑4GB内存,而64位则能够最高支撑128GB内存。

目前三星、华为、VIVO、OPPO等手机厂商应用商店已经支持32位和64位,但尚未强制要求,然而从长远的App框架角度来看,64位爱奇艺App给予的其实是更加广阔的创新空间,经过兼容更多更新的硬件来提高软件的全体性能,随着64位CPU逐步成新式手机的主流,针对适配64位的应用软件将越来越遍及。

1

64位适配的原理

64位适配,就是令APP在支持64位系统的设备上启动64位进程来运行。下面详细讲解一下如何让APP运行在64位进程上。

  1. Android手机系统在启动的过程中,会根据设备的ro.zygote属性值决定启动哪类Zygote。如果是支持64位系统的设备,会有两个Zygote(一个32位,一个64位)进程同时运行。

  2. 在APP安装的过程中,PMS里面的scanPackageDirtyLI方法通过遍历APK文件夹里面的lib下面的so库根目录,再结合该手机硬件支持的abilists列表,来决定primaryCpuAbi的值,具体请参考下图2-1的时序图:

  3. 在APP启动的过程中,AMS根据前面得到的primaryCpuAbi的值作为参考,通过调用Process的start()方法来确定,该APP是从64位还是32位的Zygote进程fork出子进程。如果子进程来自64位Zygote,该APP就运行在64位进程。

举个例子:假如APK里面的so库有armeabi-v7a和arm64-v8a这两个目录,而某一个手机硬件支持的abilists是armeabi,armeabi-v7a,arm64-v8a;这时候,primaryCpuAbi的取值就是arm64-v8a;那么当APP启动后,就会运行在64位进程。

由此见得,64位适配的重点在应用端就是so库的适配。下面详细介绍一下爱奇艺APP在so库适配上的实践。

2

so库的适配

APP里面so库的存在方式有三种:一种是APK里面自带的静态so库;另一种是通过插件中心加载的插件内的so库;第三种是动态下发的so库。下面分别介绍一下这三种情况的适配。

2.1 APK里面so库的适配

APK里面的so库有两种适配方案:

方案一,这种是比较粗暴的,即构建一个支持所有的ABI类型的APK,优点是可以满足任意机型的安装,缺点是包体积可能变得非常大。

方案二,这种是比较轻量级的,即为每个ABI类型都单独构建一个APK,不同ABI类型的设备安装对应的APK,前提条件是应用市场需要根据用户的手机设备ABI,下发对应合适的APK包,目前Google Play和国内应用市场都是支持的。

2.2 插件内的so库的适配

不管是通过何种方式实现的插件化,插件APK里面都可能会包含so库。插件里面的so库加载有三种方案:

方案一,提供包含所有ABI类型so库的插件包,由插件中心根据APP运行的进程情况来加载对应的so库;

方案二,提供多个包含不同ABI架构的插件包;插件中心在下载的时候,再决定下载哪一种对应的插件APK;

方案三,提供一个基础的不包含so库的base插件包,再把每种CPU架构的so库生成一个APK,插件中心最后下载base插件包以及对应ABI类型的so库文件;

目前爱奇艺插件化适配采用的是第二种方案,这里的插件中心就类似应用市场的角色。

2.3 云端下发的so库适配

客户端需要根据当前应用运行的进程情况,去获取相应类型的so库进行加载。针对云端下发的so库,我们专门设计了一个so分发管理平台来实现64位架构的适配。

so分发管理平台分为两部分:前端SDK部分以及后端发布平台部分,架构设计图请参考下图:

2.3.1 前端SDK部分

前端在请求后端接口时,Request会携带APP平台标识、APP版本号以及运行时进程信息,获取接口配置数据,客户端根据应用运行状态下载包含对应so库的zip包,并解压到相应文件夹。流程图请参考下图:

*注意:客户端需要根据当前APK内置的libs目录和当前手机支持的SUPPORT ABIs共同决定运行时ABI

1) 客户端SDK实现

So库管理实现逻辑:客户端先加载本地缓存数据,同时拉取线上接口数据,两者数据合并之后,正确处理so实例对象状态,对于未安装的实例进行下载操作,下载完成后,依据业务方需要进行加载。具体的实现流程参考下图:

2)客户端SDK问题处理

so库兼容性问题

定义了最低兼容版本号。当主APP发生升级时,线上的so库如果不兼容了,就必须强制卸载升级,防止加载了有问题的so库而导致崩溃;如果是向前兼容的,则可以继续使用上个版本的so库,而没有必要重新下载。

CPU架构变化的处理

由于国内市场大部分还是支持32位的,爱奇艺Android客户端会长期存在32位和64位并行的情况。所以应用升级的时候,根据APP运行的是32位进程还是64位进程,升级到对应的APK。升级之后,打开应用,加载云端so库的时候,就需要先判断CPU架构是否发生变化,如果发生变化,就直接抛弃所有的旧的so库,重新下载新的so库。

so库文件损坏问题

so库zip包的下载,跟其它网络文件下载一样,都有被劫持或者损坏的风险,所以需要提供校验,后端在zip包上传的时候生成对应的md5校验码,客户端下载后通过md5进行校验。而每次进入应用重新加载的时候,由于so库在可修改存储上面,也有可能被修改或者破坏掉,所以需要重新进行so库文件的校验,如果校验失败则重新解压或者重新下载。

2.3.2 后端发布平台部分

后端发布平台支持上传多种ABI类型的so库文件,后端接口根据前端上传的APP平台标识,APP版本号和APP运行时进程信息,下发正确的so版本和下载地址给前端。后端发布平台so库上传如下图所示:

3

64位适配优化

3.1. 升级及兼容性的优化

发现问题:爱奇艺从32位升级到64位后,第一次进入APP,加载了缓存中的32位的so库,导致APP崩溃。

优化策略:APP启动后,每次客户端加载动态so库之前,要先根据APP的运行进程判断,缓存中的so库是否符合要求,如果不符合要求,直接删除已有的,重新下载及加载。

3.2. Tinker热修复的优化

发现问题:针对一个渠道对应多个包(32位,64位,32+64位)的情况,如果一次打包只能产生一种类型的包,一个渠道多次打包肯定会导致同一渠道的多个包的dex是不一致的,这样热修的时候可能需要下发多个patch。

优化策略:为了简化流程,针对一个渠道key,下发一个patch。我们需要打包平台做一些处理,代码编译一次,先产生一个32+64位的包,然后对这个包进行处理,生成对应的32位包和64位包,之后重新签名APK文件即可。

3.3. 包体积在编译中的优化

如下是比较common的优化措施,可以优化编译效果,缩减编译后的so库大小:

1.  添加-fvisibility=hidden和-fvisibility-inlines-hidden编译选项,这两个设置所用符号库默认hidden,可减少大量func符号string,优化效果明显

2.  将编译选项-Os改成-Oz,它主要是在-Os的基础上禁用循环向量化优化,防止代码膨胀发生

3.  加-flto链接时优化编译选项,库大小优化效果比较明显

4.  以及动态链接c++_shared,也可以优化包体积

爱奇艺包体积编译参数经过优化,so库的整体大小缩减了10%左右。

 实践总结 

总体来说,爱奇艺App在64位全方位的适配之后,在性能方面有了一定的提升并且整体开播速度也有所提升。适配期间同时也遇到了一些问题,比如64位的适配过程中,插件适配方面原来采用的方案是插件APK里面支持多套so库,当时虽然能做到适配,但插件放APK较大,导致插件的分发成功率下降,以及爱奇艺的存储使用变大,然而通过后续的持续优化实现了支持多种ABI类型的插件APK的分发,并解决了上述的系列问题。

64位适配的动态加载so库的适配过程中,代码各个模块的动态so库,有不同的后台,对应不同的下载路径,就需要各个业务方针对自己的so库做适配,其工作量和沟通成本巨大,为了解决这一问题则设计了一个统一的so库管理平台,各个业务模块可以直接对接so库管理平台,后续新的so库的接入,由so库管理平台来支持适配工作就可以完美支持了。

也许你还想看

爱奇艺iOS移动端网络优化实践 | 请求成功率优化篇

爱奇艺知识的音视频通用播放架构实践

扫一扫下方二维码,更多精彩内容陪伴你!