Recently, Facebook has opened source a toolkit named Redex[1], which is specially used for optimizing Android bytecode. APK transformed by Redex becomes smaller and faster. Redex optimizes Android’s.dex file in a piped manner. A source.dex file is piped through a series of custom transformations to get an optimized.dex file. Next, we will give you a brief and quick introduction to what Redex is, as well as its basic principles and usage methods. For more details, please refer to Optimizing android-Bytecode-with-Redex [2]
Timing of conversion
We know that the compilation process of Android is to compile.java files into.class files through javac tools, then merge all.class files into the executable file of Dalvik VM. Dex, and finally compress them into APK files together with other resources files. The general process is as follows:
Redex chose to optimize based on bytecode files rather than Java source because bytecode allows for more global, class-to-class optimization than Java source, rather than local optimization of individual class files. Optimization based on DEX bytecode rather than Java bytecode was chosen because some optimizations can only be done in dex files.
The idea of pipes
As time goes by, developers may constantly get new optimization ideas. In order to conveniently add new optimization points into existing codes and facilitate parallel development of optimization points by different developers, Redex chooses the idea of pipeline to realize optimization of DEX. In this way, each optimized idea can be integrated into the pipeline in the form of plug-ins to achieve plug-and-play without affecting other optimized plug-ins. The overall optimization process is as follows:
Reduce the meaning of bytecode
Reducing the size of bytecode in an APP has a number of benefits, most notably reducing the time it takes to download and install an APP, as well as the amount of storage it takes up after the APP is installed. At the same time, theoretically less bytecode means fewer instructions to execute and fewer missing pages in code pages that need to be loaded into memory, which is obviously a good performance optimization for resource-intensive usage scenarios such as application cold starts. In Redex, pipe-based ideas implement a series of converters designed to reduce and optimize bytecode, three of which are described below.
Obfuscation and compression
Code obfuscating is widely used in Web development languages such as Javascript and HTML to reduce the total code size by replacing complete string information with meaningless and short characters without changing functionality. Proguard is used in Android to achieve a similar goal.
In the development phase, it is important to have readable string information in the code, such as the full path of the class, the path of the source file, the function name, and so on. But the complete string information takes up a lot of space in the compiled bytecode, and more importantly, the virtual machine doesn’t care if the string information is readable when it runs bytecode. ABC () and MyFooSuccessCallback() make no difference to the virtual machine. Thus, we can replace a readable but space-consuming string in bytecode with a meaningless but short string, as follows:
Similar to the mapping.txt file generated after Java layer code is confused with Proguard, the corresponding mapping file needs to be generated for bytecode confusion, so that when problems occur during APP running and need to be located, the confused log information can be restored into readable string information through the mapping file. The mapping file content looks something like this:
Use inline functions
Inline function is the function body directly embedded during compilation of the function call, which is at compile time does not have the function of nature, there is no executive function call overhead, so as to improve the purpose of the code running performance, at the same time, if the right application, it can also reduce the compiled the generated file size. Software engineering best practices encourage developers to think about encapsulation and define the responsibilities of a class, which often leads to the need to split a class into functions and responsibilities. This idea is important in practical development, but there is also room for further refinement in the resulting bytecode.
The simplest examples are adapter-type functions, which are usually used to encapsulate small functions to provide a cleaner, unified API, or multiple overloaded functions with different argument lists, or setter/getter functions, etc. Some of these functions may not be called at all in the resulting APK. Therefore, inlining these functions at the.dex file stage is a big optimization point.
Elimination of useless code
There will inevitably be a lot of useless code in the source code of large projects, and removing this useless code will reduce the size of the final APK without causing any side effects. In some ways, the removal of unwanted code is similar to a Mark-sweep garbage collection algorithm. We’ll call from a specific entry, for example, MainActivity began to traverse all conditional branching and function call, in the figure of generated markup to access code, branch and the condition of iterates through all of the function call, you can determine who don’t have marked function is useless code, can be safely deleted, as shown below:
Of course, it’s simple in theory, but in Android you have to deal with exceptions like reflection, or references to code in XML layout files.
Integration and use of Redex
Redex supports both Mac OSX and Linux operating systems. The following uses Mac OSX as an example.
Dependent installation
Open the Terminal window and execute the following HomeBrew command to install the Redex dependencies:
brew install autoconf automake libtool python3
brew install boost double-conversion gflags glog libevent
Download, build, and install
After Redex’s dependencies were successfully installed, Git was then used to checkout Redex’s source code to the computer. With Folly [3] introduced as a submodule, the following command was required to initialize the submodule:
git submodule update --init
Finally, compile Redex with autoreconf and make commands:
autoreconf -ivf && ./configure && make && make install
Once successful, you can begin to use Redex to convert.dex files from existing APK files.
use
Using Redex is as simple as specifying the path of the source APK and the generated APK, as shown below:
redex path/to/your.apk -o path/to/output.apk
More dry goods, welcome to pay attention to my wechat public number
[1] https://github.com/facebook/redex ↩
[2] https://code.facebook.com/posts/1480969635539475/optimizing-android-bytecode-with-redex ↩
[3] https://github.com/facebook/folly ↩
complaints