25

R8 的编译和使用

 3 years ago
source link: http://www.phodal.com/blog/r8-compile-and-usage/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

项目上需要使用 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 来生成。只是呢,我们根本不需要这个过程。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK