在危险的边缘试探——方舟编译器环境支持的新浪新闻极速版APP分析

昨晚刷知乎突然知道,据说终于有了支持方舟编译器环境的APP了,也就是今天要分析的主角——新浪新闻极速版。

1. 使用感受

听说后第一时间去AppGallery下载体验了一番。因为本人以前并不使用这个APP,所以并没有什么可以参照的地方,所以我就先大概说一下总体的使用感受。测试的手机为荣耀 V20 (6+128),系统版本为Magic UI 3.10 (10.1.0.160)。

  • 不知道是不是极速版的原因,第一个很明显的感受就是:图片、视频加载的速度极快。这么形容吧,别的APP(就以新浪微博为例),快速下滑的过程中,图片没及时加载出来会呈现灰白色,新浪新闻极速版不会出现这个问题。
  • 冷启动速度很快。用绿色守护将进程完全关闭之后,再重新打开,重复几次,可以明显感受到,从点击到进入首页,几乎是毫秒级别的延迟。

当然,这些感受只是相对于其他的APP的对比,主观性较强,而且,可能还有其他因素:

  • 新浪新闻极速版本身有优化,速度就已经很快了;
  • 新浪新闻极速版体积很小,只有15M左右。

不过,毕竟咱是搞技术的,不管是装逼还是拆台,拿得出证据才是硬道理。

2. APK分析

接下来对于新浪新闻极速版的APK安装包进行分析。

2.1 直接解压

首先是最粗暴的方式,直接解压.apk文件,可以得到如下的结果:

还是一个喜闻乐见的Android APP的常见结构,就不赘述了。

唯一一个不同的地方是,有一个叫maple的目录,我们知道方舟编译器的IR叫maple IR,所以这个肯定是关键。不过我们先放下,后面再分析。

可以看到有一个androidsupportmultidexversion.txt,一开始我还奇怪哪里multidex了?后来在assets里发现了一个PlayerSDKCore-0.apk,所以播放器的功能是另一个dex实现的。不过这个播放器内部没有类似的maple目录了。

2.2 dex反编译

PlayerSDKCore-0.apk没有啥好看的,就是一个播放器的实现,这里先直接略过。

而对于APP本身的dex,反编译可以大致得到如下的结果:

从目录结果上看可以知道,这也是一个典型的Android应用,有android的support libraries,有crash analysis,有华为、小米等的推送SDK,当然,也少不了igexin等广告SDK。

从初步的分析结果来看,这个dex本身就已经实现了新浪新闻极速版的完整功能。所以,那个maple目录下的东西是干啥的呢?

2.3 maple目录分析

首先maple目录的结构是这样的:

首先这个apk.info的内容看起来让人摸不着头脑:

system_component_build_version:20200619_01
buildNumber:230843_520
registerId:NA
HW_Vendor_NDK_cap:28.0.0(00.25)
vndk_huawei_projects_hashValue:dce62c18f4c2153d35c6274a6cb8ef0409f42eb0561591dea1763f3e7893800f
version:System 10.0.0.43(Q.19082101.ALL.B100RLS.043.20061901.GENERIC_A15_CHINA_FULL_DPI480_EXT4)
short_version:System 10.0.0.43(0HBI)
build_user_id:
real_build_user_id:
build_start_time:2020-06-19 01:06:04
label:system_10.0.0.Q_system_EMUI10_Q_maple2.1_release_20200619_01
archive_server:rnd-shahelix102:1666
archive_path:system_component/10.0.0.Q/system_EMUI10_Q_maple2.1_release/component/generic_a15/china-full/dpi480
selinux_hashvalue:34611fb6bb1291908a36871cc9a9eafcbad7edb7a1c158af9966c886ba38a6cc
opensource_name:
build_jenkins_url:http://100.106.46.247:8080/jenkins/job/system_component_maple2.1_app/273/
system:slave:(local):10.29.137.47:emui11.0v100r001_ubuntuserver18.04.2_20200224220658
read_only_memory_size:3319560
data_memory_size:0
rom_size:3319560
Decouple: true
Lazy: 
DAI: true

看起来很多应该是CI/CD的构建信息,和应用无关。

arm64目录下只有一个mapleclasses.so,这个应该就是方舟编译器编译的结果了。

其大小为23M多,应该占了.apk中的大头了。

可以大致猜测,arm64是arch名称,和Android的native libraries放的目录的命名类似,也就是说,方舟目前应该只支持了arm64架构,但是日后这里可能会多个x64、mips甚至riscv64啥的也不一定呢。

用Android NDK的toolchain进行一定的分析,先直接readelf一把梭,结果太长,我把其中有趣的地方截取出来:

首先可以知道,这个mapleclasses.so就是一个典型的shared object文件,没啥特殊的。

在Section部分可以发现一些比较有趣的,例如红色部分的.java_text、.classmetadata、.maple_java_debug_xxx等,这是和java直接相关的。

除此之外,有一个比较有趣的是.maple.gcrootsmap,关键字gc roots,所以这个和传闻的方舟编译器的backup的GC应该有直接的关系。

然后就是动态库的依赖,可以看到,除了C/C++的依赖库之外,还依赖了两个比较特殊的库:libmaplecore-all.solibcommon_bridge.so。这大概就是方舟编译器的运行时了。

至此已经可以大致知道,方舟编译器和此前某贴吧大佬的分析差不多:

真实的方舟?www.douban.com图标【图片】瞎写点儿什么【高通吧】_百度贴吧tieba.baidu.com图标

然后,还剩一个问题,既然这个是一个动态库,同时又作为一个Android应用,应该会有一个导出函数用于启动吧?

nm查看导出符号可以发现,没有发现相关的东西,不过有一堆获取某些信息的函数(又是Hot又是Cold的,咋这么像热点分析呢?),可能和启动有关?objdump反汇编出来大概有500M,查找了一下,也没有发现start、main等可能和入口函数有关的关键字。对于这个启动的机制还不太清楚。

个人的一个瞎猜的可能是:通过上述的导出函数,获取编译为native代码的Activity类,然后启动。所以在方舟编译器的运行时中,可能会有一个大概叫MapleClassLoader的东西,用于从mapleclasses.so中加载类。

还有一个是,单纯用方舟编译器编译出来的.apk程序,结构上除了没有dex文件外,其他的布局资源、图片资源、manifest文件等,应该会直接重用,这样改动最小。一个可以佐证这个的就是下图,用maple关键字搜AndroidManifest.xml可以看到如下的两个meta-data(以及Ark开头的配置,方舟的版本都已经迭代到了第4版了吗?),虽然不知道干啥的,但是可以明确知道,方舟版的程序在被启动时,肯定会从这里加载配置数据。这也侧面说明,zygote确实是被魔改了。

在dex反编译的结果中,没有发现任何native方法调用了mapleclasses.so,所以可以明确地说,传统安卓程序和方舟编译器编译的程序是共存的。

可惜华为手机现在很难root,不然可以跟踪调试一下的。

3. 纯方舟运行

我尝试了一下二次打包,然后看能不能只用.so运行。

不过这个过程挺坑的,用apktool反编译之后,直接删除所有的smali文件,然后再build,会报很多错误:

No resource identifier found for attribute 'xxx' in package 'com.sina.news.lite'

应该是apktool本身的问题,解决方案就是将res资源目录下的所有res-auto替换为lib/com.sina.news.lite,然后再用apktool重新构建apk就可以成功。然后就是生成签名证书,重新签名,这个都是常规操作了,不会的同学出门右转问度姐,暂且不表。

当然这样也还不行,一般来说,安装时会强制进行安全检查,然后会发现签名不对劲,强制让你去AppGallery下载,如下图:

所以,需要在系统设置里关闭安装安全检查,具体操作就是设置里搜索“安装”,然后就能看到了(我的手机是英文界面,搜install就行了),就是关闭下图第二个选项(Check apps from external sources):

不过,即使如此,安装还是会失败,说是安装包不完整,具体的错误如下:

不知道算不算一个方舟的bug?目前还不支持纯方舟、没有任何dex的安装包的安装吗?不过也许是我错过了什么……

3.1 又是曲线救国的一天

不过,无论遇到什么困难我们都不要怕,微笑地面对它,这点小问题算什么?还是很容易解决的!

它不是说要有code吗?那我给一个没法运行的dex做个placeholder不就好了?

所以……

smali目录下的文件不是全部删了,而是只留下R文件,如下图:

众所周知(大雾),R文件只是一些资源的常量索引,是不能运行的。

然后又是一番操作,构建、签名,重新构建出来的apk大小大概是12M不到,然后,安装成功了!

也许是过程太过于曲折,结果已不重要,又或许我早就预料到了这个结果,这种方式构建的安卓程序(不含可运行的dex文件),在支持方舟编译器的华为手机上是可以运行的。也就是说,目前方舟确实是可用的!

有兴趣的同学可以自己尝试一下。

这个是纯方舟运行的演示视频:

知乎视频www.zhihu.com图标

3.2 另一个小实验(存疑)

上诉提到,除了主dex之外,还有一个播放器的dex:PlayerSDKCore-0.apk

尝试把这个文件删了之后,再二次打包,可以发现的是,对于视频,可以加载预览图,但是,也就仅限于此了。视频怎么点击也无法播放,也就是说,目前看到的mapleclasses.so中,只包含了主dex的功能,对于其他的dex,应该还是用DexClassLoader进行加载,并没有一起编译。

所以,对于multidex、或有热更新的应用,也还是有一个备选的dex加载的方式,不过会不会在后台把dex编译为.so,这个就不知道了,我觉得方舟团队应该也做了这个吧。

所以方舟可能是如下的执行流程(瞎猜):

  1. 对于不支持方舟运行时的系统,仍采用传统安卓程序的方式执行;
  2. 如果支持方舟,那么会有一个主dex编译成的mapleclasses.so,从这个.so中加载、执行应用;
  3. 对于multidex、或者有热更新的应用,可以先DexClassLoader加载dex运行,并且,后台可能会对这些dex进行编译,生成这些dex的编译缓存,下次运行可以加速;(所以手机中除了方舟的运行时外,可能还有编译器);
  4. 为了支持动态字节码生成的程序,可能还会有一个backup的带JIT的解释器;
  5. 而对于GC,也有一个backup的Tracing GC。

这么一想,确实可以兼容安卓的生态,同时,又构建出基于方舟编译器的新的生态。

4. 总结

写得比较乱,在这里总结一下吧:

  • 可喜可贺,终于有一个可以拿出来show的东西了,方舟编译器不再cheap;
  • 方舟编译器编译后的程序,其实是和原生的Android应用程序共存的,只是看APP运行的环境进行调整,如果支持方舟编译器,那就运行方舟编译器编译后的,否则就是原生的。这点我觉得挺好的,其实就相当于Android应用多渠道发布时多了一个渠道——支持方舟编译器的手机。缺点就是下载的安装包会比较大,占用也大一些,不过,都0202年了,还在乎这点流量和存储占用?谁的手机不是128G起步的?/狗头
  • 对于多dex应用、热更新机制等,是怎么处理的呢?目前还不知道。听说方舟在华为内部已经结项了,有机会举办个交流会啥的吧,交流一下思路。
  • 可以想象方舟编译器的难度,所以真的挺难得的。
编辑于 09-17