项目上需要使用 R8 来对 Java 代码进行压缩、混淆,并转换为多个 dex 的形式。由于种种原因,需要从头编译源码,并通过 jar 包引用。考虑到可重复过程的问题,我决定写一篇文章记录一下这个过程。
R8 的编译还是蛮简单的,主要步骤在源码上有: https://r8.googlesource.com/r8。
但是呢,上面少了一个步骤,即安装 depot_tools
。
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=$HOME/android/depot_tools:"$PATH"
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 的使用也相当的简单,只需要 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.
接着,我们可以写一个 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 的场景,同样的也只需要一个参数 --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.** {
*;
}
嗯,就是这么简单。
顺便一提,在旧版本的 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 Idea墙, 也许,你会遇到心仪的项目