Blog

Blog

PHODAL

R8 的编译和使用

项目上需要使用 R8 来对 Java 代码进行压缩、混淆,并转换为多个 dex 的形式。由于种种原因,需要从头编译源码,并通过 jar 包引用。考虑到可重复过程的问题,我决定写一篇文章记录一下这个过程。

编译 R8

R8 的编译还是蛮简单的,主要步骤在源码上有: https://r8.googlesource.com/r8

但是呢,上面少了一个步骤,即安装 depot_tools

  1. 安装 depot_tools
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
  1. 添加到环境变量
export PATH=$HOME/android/depot_tools:"$PATH"
  1. 进行编译
tools/gradle.py d8 r8

这样一来就能得到我们想要的包:

build/libs
├── d8.jar
├── d8.jar.zip
├── deps_all.jar
├── r8-src.jar
├── r8.jar
├── r8.jar.zip
├── r8_with_deps.jar
├── r8_with_relocated_deps.jar
└── sources_main.jar

有了 jar 包以后,我们要使用就比较简单。

R8 使用

R8 的使用也相当的简单,只需要 java -jar build/libs/d8.jar,从中我们就可以看到对应的参数:

Usage: d8 [options] [@<argfile>] <input-files>
 where <input-files> are any combination of dex, class, zip, jar, or apk files
 and each <argfile> is a file containing additional arguments (one per line)
 and options are:
  --debug                 # Compile with debugging information (default).
  --release               # Compile without debugging information.
  --output <file>         # Output result in <outfile>.
                          # <file> must be an existing directory or a zip file.
  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.
  --classpath <file>      # Add <file> as a classpath resource.
  --min-api <number>      # Minimum Android API level compatibility, default: 1.
  --pg-map <file>         # Use <file> as a mapping file for distribution.
  --intermediate          # Compile an intermediate result intended for later
                          # merging.
  --file-per-class        # Produce a separate dex file per class.
                          # Synthetic classes are in their own file.
  --file-per-class-file   # Produce a separate dex file per input .class file.
                          # Synthetic classes are with their originating class.
  --no-desugaring         # Force disable desugaring.
  --desugared-lib <file>  # Specify desugared library configuration.
                          # <file> is a desugared library configuration (json).
  --main-dex-list <file>  # List of classes to place in the primary dex file.
  --main-dex-list-output <file>
                          # Output resulting main dex list in <file>.
  --force-enable-assertions[:[<class name>|<package name>...]]
  --force-ea[:[<class name>|<package name>...]]
                          # Forcefully enable javac generated assertion code.
  --force-disable-assertions[:[<class name>|<package name>...]]
  --force-da[:[<class name>|<package name>...]]
                          # Forcefully disable javac generated assertion code. This
                          # is the default handling of javac assertion code when
                          # generating DEX file format.
  --force-passthrough-assertions[:[<class name>|<package name>...]]
  --force-pa[:[<class name>|<package name>...]]
                          # Don't change javac generated assertion code. This
                          # is the default handling of javac assertion code when
                          # generating class file format.
  --version               # Print the version of d8.
  --help                  # Print this message.

转换 dex

接着,我们可以写一个 hello, world 进行测试:

java -jar build/libs/r8_with_deps.jar --output out.jar hello.jar --classpath ~/android/sdk/platforms/android-30/android.jar

看它是否能将其转换为我们所需要的 dex 格式。

混淆

为了正确地混淆,我们需要添加 --pg-conf proguard-common.txt 参数,并添加一些 keep 的规则,如:

-keep class Main {
    static void main(java.lang.String[]);
}

以保留我们的类,生成可用的 dex 文件。

multiDex

除了这个场景,我们还需要考虑 multiDex 的场景,同样的也只需要一个参数 --main-dex-rules maindexclasses.txt

如下是 Android 的相关配置:

 -keep public class * extends android.app.Instrumentation {
  }
  -keep public class * extends android.app.Application {
  }
  -keep public class * extends android.app.Activity {
  }
  -keep public class * extends android.app.Service {
  }
  -keep public class * extends android.content.ContentProvider {
  }
  -keep public class * extends android.content.BroadcastReceiver {
  }
  -keep public class * extends android.app.backup.BackupAgent {
  }
  -keep class android.support.multidex.** {
    *;
  }

嗯,就是这么简单。

DX 历史

顺便一提,在旧版本的 Android 开发环境中,是使用 dx 工具来使用 maindexlist.txt 文件的。

其通过 SDK 工具中的 mainDexClasses 对源码进行扫描,生成 main-dex-list

会生成一个类似如下的文件,包含所有需要包含在主 dex 的文件:

rx/android/concurrency/AndroidSchedulers.class
rx/android/concurrency/HandlerThreadScheduler.class
android/support/v4/view/GravityCompat.class
android/support/v4/view/ViewCompat$AccessibilityLiveRegion.class
android/support/v4/view/MenuCompat.class
android/support/v4/view/ViewCompat$ImportantForAccessibility.class
android/support/v4/view/ViewCompat$OverScroll.class
android/support/v4/view/ViewCompat.class
android/support/v4/view/ViewCompat$LayoutDirectionMode.class
android/support/v4/view/ViewCompat$LayerType.class
android/support/v4/view/ViewCompat$ResolvedLayoutDirectionMode.class
android/support/v4/content/ContextCompat.class
android/support/v4/util/LogWriter.class
android/support/v4/util/SimpleArrayMap.class
android/support/v4/util/DebugUtils.class
android/support/v4/app/ActivityCompat.class
android/support/v4/app/FragmentManagerImpl$2.class
android/support/v4/app/FragmentContainer.class
android/support/v4/app/FragmentTransaction$Transit.class
android/support/v4/app/FragmentManagerImpl$5.class
android/support/v4/app/FragmentActivity$2.class
android/support/v4/app/BackStackRecord$TransitionState.class
...

再把这个文件添加到参数中即可:--main-dex-list

同样的,对于旧的格式,我们也可以通过 --main-dex-list-output 来生成。只是呢,我们根本不需要这个过程。

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

标签