基于 Facebook Redex 实现 Android APK

作者: asce1885 | 来源:发表于2016-04-14 22:17 被阅读4605次

    @author ASCE1885的 Github 简书 微博 CSDN
    本文由于潜在的商业目的,不开放全文转载许可,谢谢!

    最近 Facebook 开源了一个名为 Redex[1] 的工具包,专门用于 Android 字节码的优化,经过 Redex 转换后的 APK,体积变得更小,运行速度变得更快。Redex 基于管道的方式来优化 Android 的 .dex 文件,一个源 .dex 文件通过管道进行一系列的自定义转换后,将得到一个优化的 .dex 文件。接下来将带大家简单快速的了解 Redex 是什么,以及它的基本原理和使用方法,更详细内容可以参见《optimizing-android-bytecode-with-redex》[2]

    转换的时机

    我们知道 Android 的编译过程首先是通过 javac 工具将 .java 文件编译成 .class 文件,接着将所有的 .class 文件合并成 Dalvik 虚拟机的可执行文件 .dex,最后再跟其他资源等文件一起压缩成 APK 文件,大致流程如下所示:

    Redex 选择基于字节码文件而不是 Java 源码进行优化,是因为字节码相比 Java 源码而言,可以进行更为全局的,类与类之间的优化,而不是单个类文件的局部优化;选择基于 dex 字节码而不是 Java 字节码进行优化,是因为某些优化只能在 dex 文件中进行。

    管道的思想

    鉴于随着时间的推移,开发人员可能会不断得到新的优化 idea,为了方便的将新的优化点加入既有的代码中,同时也方便不同开发人员并行开发优化点, 所以 Redex 选择基于管道的思想来实现 dex 的优化,这样每一个优化的 idea 可以通过插件的形式集成到管道中,实现即插即用,也不会影响其他的优化插件,整体优化流程如下所示:

    减少字节码的意义

    减少 APP 中字节码的大小有很多好处,最明显的是可以减少 APP 下载和安装的时间,也可以减少 APP 安装后占用的存储空间。同时,理论上更少的字节码也意味着需要执行的指令更少,需要加载进内存的代码页发生缺页的情况也更少,这些显然对于资源密集型使用场景例如应用冷启动起到很好的性能优化作用。在 Redex 中,基于管道的思想实现了一系列旨在减少和优化字节码的转换器,下面来了解其中的三种。

    混淆和压缩

    代码混淆广泛应用于 Web 开发语言例如 Javascript 和 HTML中,在不改变功能的前提下,通过使用无意义和简短的字符来替换完整的字符串信息,从而减少总的代码大小,Android 中使用 Proguard 也达到类似的目的。

    在开发阶段,代码中可读性强的字符串信息是非常重要的,例如类的完整路径,源文件的路径,函数名称等等。但对于编译后的字节码而言,这些完整的字符串信息占用了较大的空间,更重要的是,虚拟机运行字节码的时候并不关心这些字符串信息是否可读性强,abc()MyFooSuccessCallback() 这两者对虚拟机的处理来说并无区别。因此,我们就可以将字节码中可读性强但占用空间的字符串替换成无意义但简短的字符串,如下所示:

    跟 Java 层代码使用 Proguard 混淆后需要生成 mapping.txt 文件类似,字节码的混淆也需要生成对应的映射文件,以便当 APP 运行时出现问题需要定位的时候,可以将混淆后的日志信息通过映射文件还原成可读的字符串信息,映射文件内容类似这样:

    使用内联函数

    内联函数是在编译期间将函数体直接嵌入该函数的调用处,也就是在编译时不具备函数的性质,不存在执行函数调用产生的开销,从而得到提高代码运行性能的目的,同时,如果正确的应用它,还可以减小编译后生成的文件大小。软件工程的最佳实践鼓励开发者要具备封装的思想,要明确类的职责,这样往往会导致需要对一个类按功能和职责进行拆分等操作。在实际开发中,这种思想是很重要的,但同时在最终生成的字节码中也留有进一步优化的空间。

    最简单的一个例子是适配器类型的函数,这些函数通常是用来封装一些小函数,以提供更简洁统一的 API 接口,或者是由于参数列表不同而存在的多个重载函数,亦或者是setter/getter 函数等,这些函数在最终生成的 APK 中有的可能根本不会被调用到。因此在 .dex 文件阶段对这些函数进行内联操作,是很大的一个优化点。

    无用代码的消除

    在大型项目的源代码中,不可避免的会存在很多无用的代码,移除这些无用代码在减少最终 APK 的大小的同时也不会带来任何副作用。在某些方面,无用代码的移除类似于标记清除(mark-sweep)垃圾回收算法。我们从某个明确会调用的入口,例如 MainActivity 开始遍历各个条件分支和函数调用,在生成的图中标记访问到的代码,在遍历了所有的条件分支和函数调用之后,就可以判定那些没有被标记的函数是无用的代码,可以安全的删除,如下所示:

    当然,理论上很简单,但在 Android 中还需要处理类似反射,或者 XML 布局文件中对代码的引用等异常情况。

    Redex 的集成和使用

    在使用 Redex 之前,首先要配置好编译环境,Redex 目前支持 Mac OSX 和 Linux 系统,Windows 同学请掩泪飘过~,下面以 Mac OSX 为例说明。

    依赖的安装

    打开 Terminal 窗口,执行以下的 HomeBrew 命令安装 Redex 的依赖:

    brew install autoconf automake libtool python3
    brew install boost double-conversion gflags glog libevent
    

    下载,构建和安装

    Redex 的依赖成功安装后,接着使用 Git 将 Redex 的源码 checkout 到电脑上,由于 Redex 是以子模块的方式引入 folly[3] 的,因此需要执行如下命令初始化子模块:

    git submodule update --init
    

    最后,通过 autoreconfmake 命令来编译 Redex:

    autoreconf -ivf && ./configure && make && make install
    

    成功后,就可以开始使用 Redex 来转换现有的 APK 文件中的 .dex 文件了。

    使用

    Redex 的使用很简单,如下所示,只需指定源 APK 和 生成的 APK 的路径就可以:

    redex path/to/your.apk -o path/to/output.apk
    

    更多干货,欢迎关注我的微信公众号


    1. https://github.com/facebook/redex

    2. https://code.facebook.com/posts/1480969635539475/optimizing-android-bytecode-with-redex

    3. https://github.com/facebook/folly

    相关文章

      网友评论

      • cbe15694de32:make: *** No rule to make target `check'. Stop.

        make install也找不到,咋回事?
      • ray0802:就少了0.6M,而且没法安装
      • b537cbe46aee:没打开混淆,dex确实能减少很多,但是一般都会打开混淆,我这边几个apk只能减少100-250k,效果不明显
      • 不爱编码的蝎子:checking for a Python interpreter with version >= 3.0... none
        configure: error: Redex requires python3
        按照上面的做法一直这样,然后redex命令无效是什么原因。
        皮球二二:@会编码的蝎子 你好,请问你是如何解决的,我装了3但是环境变量那边还是2,也不知道如何修改
        不爱编码的蝎子:@asce1885 对的,我昨天发现了,mac自带2.7……
        asce1885:@会编码的蝎子 这个提示应该是你的电脑没有安装python3吧
      • w4lle:试了下,签名后可以安装,大小减少了大概1M左右,但是并没有混淆,原文的意思应该是dex字节码是可以被混淆的,例如使用DexGuard,但是facebook并没有采用DexGuard,而是使用了Proguard 混淆class 字节码,在此基础上使用redex优化提高了25%的冷启动时间和安装包大小
        Benhero:@流川枫与苍井空_ 似乎说来对于你们项目效果还是比较显著的~我这边才减了200K,不好。原本是多少M的?
      • 8a7d3b05c41c:试过了, 我这边可以压缩 1-2mb 但是,压后 apk 无法安装,安装时提示安装失败
      • YeMengSky:brew install double-conversion

        Error: No available formula for double-conversion
        Searching formulae...
        Searching taps...
        YeMengSky:@asce1885 网路环境没问题的,其他的都可以安装除了double-conversion
        asce1885:@YeMengSky 这个应该跟你的网络环境,电脑系统环境优关系,跟redex无关
        YeMengSky:@asce1885 请问怎么处理这个,安装的时候一直这个错误
      • AndroidZou:看到后面才知道Windows的同学需要掩泪飘过,我就撕心裂肺了 :joy:
        asce1885:@zhzhzh 据说在Window10 Preview上面使用bash可以安装
      • a8fe016ba8f4:亲测,11.4M的app优化后是11.1M,而且优化后不可安装,刚出来问题还是蛮多的,看了下git上它的项目,issue问题不少
        ed968b200d2d:@夏夜初雨 为毛 按照官网步骤集成后 安装apk时Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES] 一直是这个错 我已经重新打包了呀 你没有遇到?
        asce1885:@夏夜初雨 可以先观望,等稳定了再接入
        键盘男:@夏夜初雨 11.4M优化到11.1M,就是折腾……而且附带风险

      本文标题:基于 Facebook Redex 实现 Android APK

      本文链接:https://www.haomeiwen.com/subject/nxzglttx.html